GraphQL how to mutate data - graphql

I have a basic schema for mutating some data which looks like
const schema = new graphql.GraphQLSchema({
mutation: new graphql.GraphQLObjectType({
name: 'Remove',
fields: {
removeUser: {
type: userType,
args: {
id: { type: graphql.GraphQLString }
},
resolve(_, args) {
const removedData = data[args.id];
delete data[args.id];
return removedData;
},
},
},
})
});
Looking around google I cant find a clear example of the example query which needs to be sent to mutate.
I have tried
POST -
localhost:3000/graphql?query={removeUser(id:"1"){id, name}}
This fails with error:
{
"errors": [
{
"message": "Cannot query field \"removeUser\" on type \"Query\".",
"locations": [
{
"line": 1,
"column": 2
}
]
}
]
}

In order to post requests from the front-end application it is recommended to use apollo-client package. Say i wanted to validate a user login information:
import gql from 'graphql-tag';
import ApolloClient, {createNetworkInterface} from 'apollo-client';
client = new ApolloClient({
networkInterface: createNetworkInterface('http://localhost:3000/graphql')
});
remove(){
client.mutate({
mutation: gql`
mutation remove(
$id: String!
) {
removeUser(
id: $id
){
id,
name
}
}
`,
variables: {
id: "1"
}
}).then((graphQLResult)=> {
const { errors, data } = graphQLResult;
if(!errors && data){
console.log('removed successfully ' + data.id + ' ' + data.name);
}else{
console.log('failed to remove');
}
})
}
More information about apollo-client can be found here
Have you tried using graphiql to query and mutate your schema?
If you'd like to create a POST request manually you might wanna try to struct it in the right form:
?query=mutation{removeUser(id:"1"){id, name}}
(Haven't tried POSTing myself, let me know if you succeeded, i structured this out of the url when using graphiql)

You have to explicitly label your mutation as such, i.e.
mutation {
removeUser(id: "1"){
id,
name
}
}
In GraphQL, if you leave out the mutation keyword, it's just a shorthand for sending a query, i.e. the execution engine will interpret it as
query {
removeUser(id: "1"){
id,
name
}
}
cf. Section 2.3 of the GraphQL Specification

const client = require("../common/gqlClient")();
const {
createContestParticipants,
} = require("../common/queriesAndMutations");
const gql = require("graphql-tag");
const createPartpantGql = async (predictObj) => {
try {
let resp = await client.mutate({
mutation: gql(createContestParticipants),
variables: {
input: {
...predictObj,
},
},
});
let contestParticipantResp = resp.data.createContestParticipants;
return {
success: true,
data: contestParticipantResp,
};
} catch (err) {
console.log(err.message)
console.error(`Error creating the contest`);
return {
success: false,
message: JSON.stringify(err.message),
};
}
};

Related

GraphQL Stitching - Why would fields from subschemas return null?

I'm attempting to stitch together two GraphQL schemas, one from contentful and one from neo4j.
Each subschema appears to be interrogated during queries across the combined schema but "foreign" fields always come back as null.
I just can't figure this one out.
Sample Query:
query {
//Request data exclusively from the neo4j schema
Product(id:"475e006f-b9cf-4f40-8712-271ceb46d14b"){
id,
name,
weight
},
//This is a contentful schema query which should return weight from neo4j
product(id:"[contentful-native-id]"){
id,
weight,
}
}
Result:
"data": {
"Product": [
{
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b",
"name": "Test product name",
"weight": 14.9
}
],
"product": {
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b",
"weight": null //This shouldn't be null! :(
}
}
Logging:
//First query being executed against neo4j database
neo4j-graphql-js MATCH (`product`:`Product` {id:$id}) RETURN `product` { .id , .name , .weight } AS `product`
neo4j-graphql-js {
"offset": 0,
"first": -1,
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b"
}
//Triggered by the second query correctly trying to resolve weight from neo4j
neo4j-graphql-js MATCH (`product`:`Product` {id:$id}) RETURN `product` { .weight , .id } AS `product`
neo4j-graphql-js {
"offset": 0,
"first": -1,
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b"
}
This seems to suggest something is working, but the result of weight never makes it to the final output.
ApolloServer doesn't report any errors via didEncounterErrors()
Stitching:
const gatewaySchema = stitchSchemas({
subschemas: [{
schema: neoSchema,
merge: {
Product: {
selectionSet: '{id}',
fieldName: 'Product',
args: ({
id
}) => ({
id
}),
}
}
},
{
schema: contentfulSchema,
merge: {
}
}
],
})
Schemas:
const executor = async ({
document,
variables,
context
}) => {
const query = print(document);
//console.log(query);
const fetchResult = await fetch('https://graphql.contentful.com/content/v1/spaces/[SPACE]', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer [AUTHTOKEN]`,
},
body: JSON.stringify({
query,
variables
})
});
return fetchResult.json();
};
const contentfulSchema = wrapSchema({
schema: await introspectSchema(executor),
executor: executor
});
const driver = neo4j.driver(
process.env.NEO4J_URI || 'bolt://localhost:7687',
neo4j.auth.basic(
process.env.NEO4J_USER,
process.env.NEO4J_PASS
), {
encrypted: process.env.NEO4J_ENCRYPTED ? 'ENCRYPTION_ON' : 'ENCRYPTION_OFF',
}
)
const neoSchema = makeAugmentedSchema({
typeDefs: typeDefs,
});
Server:
const server = new ApolloServer({
schema: gatewaySchema,
context: ({ req }) => {
return {
driver,
req
};
},
plugins:[
myPlugin
]
});
Any insight or ideas much appreciated!
This appears to be down to the fact that stitchSchemas is NOT supported in ApolloServer...
Does Apollo Server work with GraphQL Tools stitchSchemas?

Apollo nodejs server; How to get mutation/query schema path in the request context when writing a plugin?

I'm writing an Apollo server plugin for node.js, and my goal is to improve my teams debugging experience. My plugin currently looks something like this:
export function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
// Set requestId on the header
const requestId = (requestContext?.context as EddyContext)?.requestId;
if (requestId) {
requestContext.response?.http?.headers.set('requestId', requestId);
}
return {
willSendResponse(context) { // <== Where do I find the "path" in the schema here?
// Inspired by this: https://blog.sentry.io/2020/07/22/handling-graphql-errors-using-sentry
// and the official documentation here: https://docs.sentry.io/platforms/node/
// handle all errors
for (const error of requestContext?.errors || []) {
handleError(error, context);
}
},
};
},
};
}
I would like to know if I can access the path in the schema here? It's pretty easy to find the name of mutaiton/query with operation.operationName, but where can I get the name of the query/mutation as defined in the schema?
Solution
export function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
// Set requestId on the header
const requestId = (requestContext?.context as EddyContext)?.requestId;
if (requestId) {
requestContext.response?.http?.headers.set('requestId', requestId);
}
return {
didResolveOperation(context) {
const operationDefinition = context.document
.definitions[0] as OperationDefinitionNode;
const fieldNode = operationDefinition?.selectionSet
.selections[0] as FieldNode;
const queryName = fieldNode?.name?.value;
// queryName is what I was looking for!
},
};
},
};
}
Your requirement is not very clear. If you want to get the name of the query/mutation to distinguish which query or mutation the client sends.
You could get the name from context.response.data in willSendResponse event handler.
E.g.
server.ts:
import { ApolloServer, gql } from 'apollo-server';
import { ApolloServerPlugin } from 'apollo-server-plugin-base';
import { parse, OperationDefinitionNode, FieldNode } from 'graphql';
function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
return {
didResolveOperation(context) {
console.log('didResolveOperation');
const obj = parse(context.request.query!);
const operationDefinition = obj.definitions[0] as OperationDefinitionNode;
const selection = operationDefinition.selectionSet.selections[0] as FieldNode;
console.log('operationName: ', context.request.operationName);
console.log(`${context.operation!.operation} name:`, selection.name.value);
},
willSendResponse(context) {
console.log('willSendResponse');
console.log('operationName: ', context.request.operationName);
console.log(`${context.operation!.operation} name:`, Object.keys(context.response.data!)[0]);
},
};
},
};
}
const typeDefs = gql`
type Query {
hello: String
}
type Mutation {
update: String
}
`;
const resolvers = {
Query: {
hello() {
return 'Hello, World!';
},
},
Mutation: {
update() {
return 'success';
},
},
};
const server = new ApolloServer({ typeDefs, resolvers, plugins: [eddyApolloPlugin()] });
const port = 3000;
server.listen(port).then(({ url }) => console.log(`Server is ready at ${url}`));
GraphQL Query:
query test {
hello
}
the logs of the server:
didResolveOperation
operationName: test
query name: hello
willSendResponse
operationName: test
query name: hello
GraphQL Mutation:
mutation test {
update
}
the logs of the server:
didResolveOperation
operationName: test
mutation name: update
willSendResponse
operationName: test
mutation name: update

GraphQL client not reading certain query variables [duplicate]

I have a component that passes a string (userToFetch) it as a variable parameter in a parameterized query. The component looks like this:
// pages/index.jsx
import React from 'react';
import { useQuery } from '#apollo/react-hooks';
import gql from 'graphql-tag';
const GET_USERS = gql`
query users ($limit: Int!, $username: String!) {
users (limit: $limit, where: { username: $username }) {
username
firstName
}
}
`;
const Home = () => {
const userToFetch = 'jonsnow';
const {
loading,
error,
data,
} = useQuery(
GET_USERS,
{
variables: { limit: 2, username: userToFetch },
notifyOnNetworkStatusChange: true,
},
);
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Error: {JSON.stringify(error)}</p>;
}
return (
<div>
<ul>
{data.users.map(user => {
return <li>{user.username} {user.firstName}</li>;
})}
</ul>
</div>
);
};
export default Home;
And this is how I have configured my Apollo client:
// /apollo-client.js
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import withApollo from 'next-with-apollo';
import { createHttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';
const GRAPHQL_URL = 'https://dev.schandillia.com/graphql';
const link = createHttpLink({
fetch, // Switches between unfetch & node-fetch for client & server.
uri: GRAPHQL_URL
});
// Export a HOC from next-with-apollo
// Docs: https://www.npmjs.com/package/next-with-apollo
export default withApollo(
// You can get headers and ctx (context) from the callback params
// e.g. ({ headers, ctx, initialState })
({ initialState, ctx }) => {
console.log('initialState', initialState);
console.log('ctx', ctx);
return new ApolloClient({
link: link,
cache: new InMemoryCache()
// rehydrate the cache using the initial data passed from the server:
.restore(initialState || {})
})
}
);
The database is a collection of following users:
"users": [
{
"username": "negger",
"firstName": "Arnold",
"lastName": "Schwarzenegger"
},
{
"username": "jonsnow",
"firstName": "Jon",
"lastName": "Snow"
},
{
"username": "tonystark",
"firstName": "Tony",
"lastName": "Stark"
}
]
}
Now, although this should work (it does when I run the query in my graphql playground at https://dev.schandillia.com/graphql), the code runs as if the where clause didn't exist! It just returns all results as if the query being run were:
users {
_id
username
firstName
}
In order to reproduce the issue, visit https://www.schandillia.com. The page ought to display a list with only one element consisting of a matching username-firstName value: jonsnow Jon but it returns two entries, negger Arnold and jonsnow Jon (respecing limit but completely ignoring where). Now, run the same query with jonsnow as a where parameter in https://dev.schandillia.com/graphql:
{
users(where: { username: "jonsnow" }) {
_id
username
firstName
}
}
And the results would be exactly as expected:
{
"data": {
"users": [
{
"_id": "5d9f261678a32159e61018fc",
"username": "jonsnow",
"firstName": "Jon",
}
]
}
}
What am I overlooking?
P.S.: The repo is up for reference at https://github.com/amitschandillia/proost/tree/master/apollo-nextjs.
UPDATE: In order to track down the root cause, I tried logging some values in apollo-client.js:
console.log('initialState', initialState);
Strangely, the output shows the right query, along with the variables being passed, but wrong results:
...
ROOT_QUERY.users({"limit":2,"where":{"username":"jonsnow"}}).0:
firstName: "Arnold"
username: "negger"
__typename: "UsersPermissionsUser"
...
UPDATE: Here's a screenshot of results in my Apollo Client Developer Tools:
The schema generated by Strapi gives the where attribute a Type JSON and hence you have to pass the entire where part in the query variable as JSON since the variables are not getting injected.
# Write your query or mutation here
query users($where: JSON) {
users(where: $where) {
username
firstName
}
}
And the variables would look like:
{"where": {"username": "jonsnow"}}

graphql-subscriptions withFilter returns undefined; subscriptions without variables work ok

I'm trying to get my head around graphql-subscriptions and withFilter. Subscriptions without variables work as intended, but if I try to use withFilter, I only get 'Subscription field must return Async Iterable. Received: undefined' error when I try to run the subscription.
Am I doing something wrong with setting up withFilter, are the some incompatibilities with packages I'm using or am I completely missing something obvious here? All queries and mutations work properly, so the basic set up should be fine.
My set up is similar to this (all code snippets are in https://gist.github.com/aqmattil/41e10e7c9f30b8ea964cecdc61c58f20
Package.json
// package.json
"dependencies": {
"apollo-server-express": "^2.0.0-beta.2",
"body-parser": "^1.18.3",
"express": "^4.16.3",
"graphql": "^0.13.2",
"graphql-subscriptions": "^0.5.8",
"subscriptions-transport-ws": "^0.9.11"
}
Mutations
// mutations.js
const mutation = new GraphQLObjectType({
name: 'mutation',
fields: {
addSite: {
type: SiteType,
description: "Create a new Site",
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
location: { type: GraphQLString },
company: { type: GraphQLString }
},
async resolve(parentValue, { name, location, company }) {
const site = await new Site({ name, location, company }).save()
const siteid = site._id;
console.log("addSite resolve", siteid, name, location, company );
pubsub.publish('siteAdded', { 'siteAdded': site } );
return site;
}
}
}
});
module.exports = mutation;
Subscriptions
// subscriptions.js
const graphql = require('graphql');
const {
GraphQLObjectType,
GraphQLString
} = graphql;
const { withFilter } = require('graphql-subscriptions');
const SiteType = require('./site_type');
const pubsub = require('./pubsub_helper');
const Subscriptions = new GraphQLObjectType({
name: 'subscription',
fields: () => ({
/*
// this code works, commented out to test withfilter
siteAdded: {
type: SiteType,
resolve(payload) {
return payload.siteAdded;
},
subscribe() {
return pubsub.asyncIterator('siteAdded');
}
},
*/
// test withFilter
siteAdded: {
type: SiteType,
args: {
name: { type: GraphQLString }
},
resolve(payload) {
return payload.siteAdded;
},
subscribe() {
// this returns undefined
withFilter(
() => {
console.log("in subscribe withfilter");
return pubsub.asyncIterator('siteAdded');
}
),
(payload, variables) => {
console.log("payload, variables", payload, variables);
return true;
}
}
}
})
});
module.exports = Subscriptions;
I'm using graphiql to run the queries,
// this is used to add a site
mutation {
addSite(name:"test name", location: "somewhere") {
id
}
}
// simple subscription - this works as inteded, and new sites are shown
subscription {
siteAdded {
name
location
company {
id
}
}
}
// using query variables --> returns "Subscription
// field must return Async Iterable. Received: undefined"
subscription {
siteAdded(name: "test name") {
name
location
company {
id
}
}
}

Hello world example for Apollo Client 2 + React?

Im trying to return a string with React and GraphQL but I'm getting stuck at the first stage. Here is my attempt:
import { makeExecutableSchema } from 'graphql-tools';
const typeDefs = `
type Query {
author: Person
}
type Person {
name: String
}
`;
const resolvers = {
Query: {
author: { name: 'billy' },
},
};
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
createApolloServer({ schema });
And this is my understanding of that code:
In my schema I've defined a Query called author which should return a Person.
A Person has a name field which is a string.
My resolver has a Query called author which should return an object with a name field of value 'billy'
However in my Graphicool browser tools this query:
query {
author{
name
}
}
Returns this:
{
"data": {
"author": null
}
}
Resolvers are functions which GraphQL will call when resolving that particular field. That means your resolvers object should look more like this:
const resolvers = {
Query: {
author: () => ({ name: 'billy' }),
},
}
Or, alternatively,
const resolvers = {
Query: {
author() {
return { name: 'billy' }
},
},
}
You can check out the docs for more information.
import { createApolloServer } from 'meteor/apollo';
import { makeExecutableSchema } from 'graphql-tools';
import merge from 'lodash/merge'; // will be useful later when their are more schemas
import GroupsSchema from './Groups.graphql';
import GroupsResolvers from './resolvers';
const typeDefs = [GroupsSchema];
const resolvers = merge(GroupsResolvers);
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
createApolloServer({ schema });
In ./Groups.graphql:
type Query {
hi: String
groups: [Group]
group: Group
}
type Group {
name: String
}
In './resolvers':
export default {
Query: {
hi() {
return 'howdy';
},
groups() {
return [{ name: 'one', _id: '123' }, { name: 'two', _id: '456' }];
// return Groups.find().fetch();
},
group() {
return { name: 'found me' };
},
},
};
In a React component:
const mainQuery = gql`
{
groups {
name
}
}
`;
export default graphql(mainQuery)(ComponentName);

Resources