GraphQL mutation, parameters / variables objects (with dot notation) - graphql

I have a Graphql Mutation like this:
gql query:
mutation UpdateMother($mother_id: ID!, $child_name: String!) {
updateMother(
mother: {
id: $id
child: {
name: $child_name
}
}
)
}
It works good, but I need to use $child.name instead of $child_name. Is this possible?

You should be able to call this mutation like
mutate({
variables: {
mother_id: mother.id,
child_name: child.name
}
})
.then(() => {})
.catch(() => {})
Also note that you're defining $mother_id as an input, and using $id in the 4th line. You should be using something like:
mutation UpdateMother($mother_id: ID!, $child_name: String!) {
updateMother(
mother: {
id: $mother_id
child: {
name: $child_name
}
}
)
}

Related

GraphQL Codegen duplicates RegisterDocument with typescript-urql

The codegen.ts config below results in duplicating the RegisterDocument entries.
codegen.ts:
const config: CodegenConfig = {
overwrite: true,
schema: "http://localhost:4000/graphql",
documents: "src/graphql/**/*.graphql",
generates: {
"src/generated/graphql": {
preset: "client",
plugins: [
"typescript-urql"
],
config: {
documentVariableSuffix: 'test2'
}
}
}
};
the output:
export const RegisterDocument = {"kind":"Document", ...}
export const RegisterDocument = gql`
mutation Register($username: String!, $password: String!) {
register(options: {username: $username, password: $password}) {
errors {
field
message
}
user {
id
username
createdAt
}
}
}
`;
export function useRegisterMutation() {
return Urql.useMutation<RegisterMutation, RegisterMutationVariables>(RegisterDocument);
};
Seemingly either the documentVariableSuffix param didn't affect the output const naming or it was a wrong param. The use of the typescript-operations or/and typescript packages only led to more duplicates.
What is the way to have typescript-urql register the mutation differently?
UP. The register mutation I need typings for:
const registerMutationDocument = graphql(`
mutation Register($username: String!, $password: String!) {
register(options: { username: $username, password: $password }) {
errors {
field
message
}
user {
id
username
createdAt
}
}
}
`)
I'm Charly, from The Guild, working on GraphQL Code Generator.
The preset: "client" is not meant to be used in combination with other plugins.
You must use either the client-preset or typescript-urql-plugin which provides 2 different ways to get typed GraphQL Operations.
The typescript-urql-plugin generates React hooks while, the client-preset generated typed GraphQL documents that can be used with URQL's native useQuery() and useMutation().
We now recommend using the client-preset that provides a better developer experience and smaller generated code for the same result.
You will find a guide to setup the client-preset with URQL here: https://the-guild.dev/graphql/codegen/docs/guides/react-vue
After a few attempts with various code pieces it seems I've got it to work. Thank you mr. Poly for the hints.
Here's the take.
First, the present iteration of graphql-codegen watches for .ts/.tsx documents not *.graphql ones. Second the only plugins needed are the ones listed in the docs.
const config: CodegenConfig = {
overwrite: true,
schema: "http://localhost:4000/graphql",
documents: "src/graphql/mutations/*.ts",
generates: {
"src/generated/graphql/": {
preset: "client",
plugins: []
}
}
};
Third the way to put the mutations etc. to a dedicated folder that I used was to create one at src/graphql/mutations. The register.ts holding the mutation had the following code:
import { graphql } from '../../generated/graphql';
export const registerMutationDocument = graphql(`
mutation Register($username: String!, $password: String!) {
register(options: { username: $username, password: $password }) {
errors {
field
message
}
user {
id
username
createdAt
}
}
}
`);
The only problem for me was if I tried to add a field to it that didn't exist on the model the editor froze.
The usage within the component:
import { registerMutationDocument } from '../graphql/mutations/register';
...
const [, register] = useMutation(registerMutationDocument);

How to handle Union or Interface in graphQL mutations?

I got the following schema :
type Vehicle {
id: ID!
name: String!
color: String!
}
input AddVehicle {
name: String!
color: String!
}
input UpdateVehicle {
id: ID!
name: String!
}
Now I would like to add some properties to my vehicles, depending of the Vehicle model, like
type CarProperties {
wheelSize: Int!
doors: Int!
}
type BoatProperties {
length: Int!
}
union VehicleProperties = CarProperties | BoatProperties
type Vehicle {
[ ... ]
properties: vehicleProperties!
}
So it's quite straightforward to write the queries, but I'm struggling when it comes to make mutations...
AFAIK graphQL inputs does not implement unions or interface (There is a related thread here https://github.com/graphql/graphql-spec/issues/488)
So the workaround I see here is to duplicate my inputs, like :
input addBoatVehicle {
name: String!
color: String!
properties: BoatProperties!
}
and so on with updateBoatVehicle, addCarVehicle, updateCarVehicle.
But if I get a lot of vehicle models, or maybe a third or a fourth mutation, I'm afraid it becomes cumbersome quickly.
Is there any recommended way to manage this case ?
Creating separate mutations is the proper solution. You can alleviate some of the pain by making your mutations incredibly lightweight and refactor out processing of those items to a separate function.
function addVehicle(input) {
// disambiguate the input type
}
function updateVehicle(input) {
// dismabiguate the input type, preferably in its own refactor function so
// it can be used above too!
}
const resolvers = {
Mutation: {
addBoat: (parent, boatInput) => { return addVehicle(boatInput) },
addCar: (parent, carInput) => { return addVehicle(carInput) },
updateBoat: (parent, boatInput) => { return updateVehicle(boatInput) },
updateCar: (parent, carInput) => { return updateVehicle(carInput) },
}
}

Nested GraphQL query with array argument

I am using apollo-datasource-rest with apollo-server-lambda and trying to figure out how to map a query to a specific resolver. I have the following schema, in which the plan query is supposed to return a list of users (that should be driven by the users query rather than the user query).
type Query {
user(user_id: String, username: String): User
users(user_ids: [String!]): [User!]
plan(plan_id: String): Plan
}
type User {
id: String
username: String
first: String
last: String
image: String
}
type Plan {
title: String
image: String
attending: [User]
}
The plan query resolver datasource is as follows:
planReducer(data) {
return {
image: data.public.info.image,
title: data.public.info.title,
attending: Object.keys(data.public.attending)
}
}
data.public.attending in the planReducer returns an array of user_ids that I would like to then be able to feed into my users query as opposed to my user query.
These are my current resolvers:
user: (_, { username }, { dataSources }) =>
dataSources.userData.getUserByUsername({ username: username }),
users: async (_, { user_ids }, { dataSources }) => {
const usersArray = await dataSources.userData.getUsersByIds({ userIds: user_ids })
return usersArray
},
plan: async (_, { plan_id }, { dataSources }) => {
return dataSources.planData.getPlanById({ planId: plan_id })
}
Your resolver map should look like below:
const resolvers = {
Query: {
plan: async (_parent, { plan_id: planId }, { dataSources }) => (
dataSources.planData.getPlanById({ planId })
)
},
Plan: {
users: async ({ user_ids: userIds }, _variables, { dataSources }) => (
dataSources.userData.getUsersByIds({ userIds })
)
}
}
Every key within Query should be a resolver that corresponds to a query defined within the root Query of your schema. Keys that are direct children of the root, in this case Plan, will be used to resolve their corresponding types when returned from the plan resolver within Query.
If resolvers are not defined, GraphQL will fall back to a default resolver which in this case looks like:
const resolvers = {
Plan: {
title: (parent) => parent.title,
image: (parent) => parent.image,
}
}
By specifying a custom resolver, you are able to compute fields to return to your clients based on the return value of parent resolvers.

How to add multiple resolvers in a type (Apollo-server)

I have used express-graphql and there i used to do something like this.
const SubCategoryType = new ObjectType({
name: 'SubCategory',
fields: () => ({
id: { type: IDType },
name: { type: StringType },
category: {
type: CategoryType,
resolve: parentValue => getCategoryBySubCategory(parentValue.id)
},
products: {
type: List(ProductType),
resolve: parentValue => getProductsBySubCategory(parentValue.id)
}
})
});
Here I have multiple resolvers, id and name are fetched directly from the result. and the category and products have there own database operation. and so on.
Now I am working on apollo-server and I can't find a way to replicate this.
for example I have a type
type Test {
something: String
yo: String
comment: Comment
}
type Comment {
text: String
createdAt: String
author: User
}
and in my resolver I want to split it up, for example something like this
text: {
something: 'value',
yo: 'value',
comment: getComments();
}
NOTE: this is just a representation of what I need.
You can add type-specific resolvers to handle specific fields. Let's say you have the following schema (based on your example):
type Query {
getTest: Test
}
type Test {
id: Int!
something: String
yo: String
comment: Comment
}
type Comment {
id: Int!
text: String
createdAt: String
author: User
}
type User {
id: Int!
name: String
email: String
}
Let's also assume you have the following DB methods:
getTest() returns an object with fields something, yo and
commentId
getComment(id) returns an object with fields id, text, createdAt and userId
getUser(id) returns an object with fields id, name and email
Your resolver will be something like the following:
const resolver = {
// root Query resolver
Query: {
getTest: (root, args, ctx, info) => getTest()
},
// Test resolver
Test: {
// resolves field 'comment' on Test
// the 'parent' arg contains the result from the parent resolver (here, getTest on root)
comment: (parent, args, ctx, info) => getComment(parent.commentId)
},
// Comment resolver
Comment: {
// resolves field 'author' on Comment
// the 'parent' arg contains the result from the parent resolver (here, comment on Test)
author: (parent, args, ctx, info) => getUser(parent.userId)
},
}
Hope this helps.

apollo (graphQL) - how to send array of object in queries

I would like to send an array of object in a graphQL queries. But I don't have any idea how to type the pointer in the query $gallery: where Type will be a simple datastructure like a class or dictionnary.
apollo_client.mutate({mutation: gql`
mutation m(
$title: String!, $gallery:<Type?>){
mutatePmaGallery(pmaData:
{title: $title, gallery: $gallery}) {
pma{
id
}
}
}`,
variables: {
title: _this.state.title,
gallery: {<Type?>}
})
You first need to define an input type according to your gallery structure :
input GalleryType {
id:ID!
name: String!
}
Then you can simply do this:
apollo_client.mutate({mutation: gql`
mutation m(
$title: String!, $gallery:[GalleryType!]!){ //changed part
mutatePmaGallery(pmaData:
{title: $title, gallery: $gallery}) {
pma{
id
}
}
}`,
variables: {
title: _this.state.title,
gallery: {<Type?>}
})

Resources