I have a Graphql schema that looks like this:
type Mutation {
createUploadUrl(
input: CreateUploadUrl!
): MyResponse!
}
input CreateUploadUrl {
deploymentId: ID!
}
I'm new to Graphql and am getting lost when trying to work out what my query should look like when I submit it via fetch:
fetch('https://example.com', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `
mutation($input: ) {
createUploadUrl($input) {
__typename,
// my values I want to retrieve
// ...
}
}`,
variables: {
input: {deploymentId: "XXX"}
}
}),
})
.then((res) => res.json())
.then((result) => console.log(result));
The above gives me an error: `message: "Syntax Error: Expected Name, found "$"."
How am I supposed to specify a query or mutation that requires an object as a variable?
`
You just need to define your parametrized function and wrap the effective mutation.
In the example below the mutation MyMutation takes as param a $arg wich is a CreateUploadUrl!.
Then pass the $arg to the input of the mutation, the invocation becomes:
mutation MyMutation($arg: CreateUploadUrl!) {
createUploadUrl(input: $arg) {
__typename,
...
}
Related
I created a NestJS sample, I add a Mutation to sync user info to my backend.
And added the following codes, the #GqlUser is to extract user info from my jwt token.
#Mutation((returns) => UpdateUserResult)
#UseGuards(JwtAuthGuard)
updateUser(#GqlUser() user: UserPrincipal): Observable<UpdateUserResult> {
console.log('gql user:', user);
const { userId, email, name } = user;
return this.usersService.update({ id: userId, email, name }).pipe(
map((b) => ({
success: b,
})),
);
}
The related GraphQL definition is generated by NestJS as the following:
type Mutation {
//... other defs.
updateUser: UpdateUserResult!
}
type UpdateUserResult {
message: String!
success: Boolean!
}
// other defs.
But when executing this mutation by the following forms.
// failed due to *the expectedName is not a ")"*, it is a syntax error
mutation SyncUser{
updateUser(){//error
success
}
}
// or
mutation SyncUser(){ // error
updateUser(){
success
}
}
or
// failed due to *this.subQuery is not a function*
mutation {
updateUser {
success
}
}
In GraphQLspec, the input arguments should be optional in a mutation definition.
Your mutation name is updateUser, so you should use something similar this:
mutation {
updateUser ()
{
success
}
}
Consider that inside the parentheses, you need to pass data needed for the operation.
Anyway, I suggest using GraphQL PlayGround to find the correct query format.
Suppose I have the following GraphQL types:
type User {
id: String!
posts: [Post!]!
}
type Post {
id: String!
text: String,
}
And here is a mutation that returns the updated post:
mutation addNewPost(
$userId: String!
$text: String!
) {
addNewPost(userId: $userId, text: $text) {
id
text
}
}
After running this mutation my cache contains a new entry of a post. How do I add it to the user's posts array? I have tried cache.writeQuery and cache.modify but I cannot figure it out.
We do push the item into array inside the update function, which is one of the options of useMutation.
I'm writing the whole mutation so that you can get the idea 💡, let have a look at example:
By Update function:
const [createPost, { data, loading, error }] = useMutation(CREATE_POST, {
update(cache, response) {
// Here we write the update
// Step 1: Read/Fetch the data 👈
const data = client.readQuery({
query: FETCH_POSTS_QUERY,
});
// Step 2: Update the cache by immutable way 👈
client.writeQuery({
query: FETCH_POSTS_QUERY,
data: {
getPosts: [response.data.createPost, ...data.getPosts],
},
});
},
variables: formValues,
});
By refetchQueries:
That's really shortcut 🔥 way to update the cache, by using DocumentNode object parsed with the gql function
const [createPost, { data, loading, error }] = useMutation(CREATE_POST, {
refetchQueries: [ 👈
FETCH_POSTS_QUERY
'fetchPosts`' // OR Query name
],
variables: formValues,
});
You're going to want to directly write to the Apollo cache in order to update the other entities that your mutation has modified.
Have a look at the docs https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates here for specifics (you're going to want to use cache.modify and cache.writeFragment)
I'm using the request-promise library to make http request to a graphql server. To achieve a query, I'm doing this:
const query = `
{
user(id:"123173361311") {
_id
name
email
}
}
`
const options = {
uri: "http://localhost:5000/graphql",
qs: { query },
json: true
}
return await request(options)
The above code is working fine. However I'm confused about how to go about a mutation since I need to specify both the actual mutation and the inputData like this:
// Input
{
name: "lomse"
email: "lomse#lomse.com"
}
const mutation = `
mutation addUser($input: AddUserInput!){
addUser(input: $input) {
_id
name
email
}
}
`
const option = {
uri: "http://localhost:5000/graphql",
formData: {mutation},
json: true,
// how to pass the actual data input
}
request.post(option)
Or is it that the request-promise library isn't designed for this use case?
Use body, not formData. Your body should consist of three properties:
query: The GraphQL document you're sending. Even if the operation is a mutation, the property is still named query.
variables: A map of your variable values serialized as a JSON object. Only required if your operation utilized variables.
operationName: Specifies which operation to execute. Only required if your document included multiple operations.
request.post({
uri : '...',
json: true,
body: {
query: 'mutation { ... }',
variables: {
input: {
name: '...',
email: '...',
},
},
},
})
The graphql-request library seems to do what I needed the request-promise library to do.
import { request } from 'graphql-request'
const variables = {
name: "lomse",
email: "lomse#lomse.com"
}
const mutation = `
mutation addUser($input: AddUserInput!){
addUser(input: $input) {
_id
name
email
}
}
`
response = await request(uri, mutation, {input: variables})
I am making the following query in GraphQL:
{
metal(silver_bid_usd_toz: 1) {
silver_bid_usd_toz
}
}
which returns
{
"data": {
"metal": {
"silver_bid_usd_toz": 16.45
}
}
}
The JSON object returned by the API is flat:
{
silver_bid_usd_toz: 123,
gold_bid_usd_toz: 123,
copper_bid_usd_toz: 123
}
I don't understand what the int 1 in my graphql query means metal(silver_bid_usd_toz: 1)
It doesn't matter what I change it to, it could be 1 or 355, but it is required for the query to work. Why cant I just do
{
metal(silver_bid_usd_toz) {
silver_bid_usd_toz
}
}
My schema looks like this:
module.exports = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
description: '...',
fields: () => ({
metal: {
type: MetalType,
args: {
gold_bid_usd_toz: { type: GraphQLFloat },
silver_bid_usd_toz: { type: GraphQLFloat }
},
resolve: (root, args) => fetch(
`api_url`
)
.then(response => response.json())
}
})
})
});
You are passing silver_bid_usd_toz as an argument for the field, but apparently you are not using it in the resolve function, so it's being ignored.
It seems to be the reason why the result is always the same when you change the argument value.
But it is weird when you say that it is required for the query to work, since it is not defined as a GraphQLNonNull type.
It should be possible to query this field without passing any argument, according to the Schema you passed us.
I have a relationship between User and Post. This is how I query the User Posts.
const UserType = new GraphQLObjectType({
name: 'User'
fields: () => ({
name: {
type: GraphQLString
},
posts: {
type: new GraphQLList(PostType),
resolve(parent, args , { db }) {
// I want to get here the args.someBooleanArg
return someLogicToGetUserPosts();
}
}
})
});
The main query is:
const queryType = new GraphQLObjectType({
name: 'RootQuery',
fields: {
users: {
type: new GraphQLList(UserType),
args: {
id: {
type: GraphQLInt
},
someBooleanArg: {
type: GraphQLInt
}
},
resolve: (root, { id, someBooleanArg }, { db }) => {
return someLogicToGetUsers();
}
}
}
});
The problem is the args in the resolve function of the UserType posts is empty object, how do i pass the args from the main query to sub resolves functions?
When resolving the root query you can use object assign to attach the argument to the user object returned.
Then, on the user type, resolve the argument from the root value (first argument of resolve function).
Example:
const queryType = new GraphQLObjectType({
name: 'RootQuery',
fields: {
users: {
type: new GraphQLList(UserType),
args: {
id: {
type: GraphQLInt
},
someBooleanArg: {
type: GraphQLInt
}
},
resolve: (root, { id, someBooleanArg }, { db }) => {
return Promise.resolve(someLogicToGetUsers()).then(v => {
return Object.assign({}, v, {
someBooleanArg
});
});
}
}
}
});
const UserType = new GraphQLObjectType({
name: 'User'
fields: () => ({
name: {
type: GraphQLString
},
posts: {
type: new GraphQLList(PostType),
resolve(parent, args , { db }) {
console.log(parent.someBooleanArg);
return someLogicToGetUserPosts();
}
}
})
});
You can use the resolver fouth argument, info, to receive the desired variable - from Apollo docs:
Every resolver in a GraphQL.js schema accepts four positional arguments:
fieldName(obj, args, context, info)
{ result }
These arguments have
the following meanings and conventional names:
obj: The object that contains the result returned from the resolver on
the parent field, or, in the case of a top-level Query field, the
rootValue passed from the server configuration. This argument enables
the nested nature of GraphQL queries.
args: An object with the
arguments passed into the field in the query. For example, if the
field was called with author(name: "Ada"), the args object would be: {
"name": "Ada" }.
context: This is an object shared by all resolvers in
a particular query, and is used to contain per-request state,
including authentication information, dataloader instances, and
anything else that should be taken into account when resolving the
query. If you're using Apollo Server, read about how to set the
context in the setup documentation.
info: This argument should only be
used in advanced cases, but it contains information about the
execution state of the query, including the field name, path to the
field from the root, and more. It's only documented in the GraphQL.js
source code.
The info seems to be a very undocumented feature, but I'm using it now with no problems (at least until somebody decide to change it).
Here is the trick:
const UserType = new GraphQLObjectType({
name: 'User'
fields: () => ({
name: {
type: GraphQLString
},
posts: {
type: new GraphQLList(PostType),
resolve(parent, args , { db }, info) {
// I want to get here the args.someBooleanArg
console.log("BINGO!");
console.log(info.variableValues.someBooleanArg);
return someLogicToGetUserPosts();
}
}
})
});