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"}}
Related
I am at the very first point of making an app with gatsby-strapi toolchain and as usual, I want to log the query data on the console. But for some reason, only a number is logged. The query works properly in Graphql dashboard (http://localhost:8000/___graphql) and also it can be printed in the DOM. What is that number? What is going on?
Here is the code:
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
const IndexPage = ({data}) => (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
<div>{JSON.stringify(data)}</div>
</Layout>
)
export default IndexPage
export const pageQuery = graphql`
query MyQuery {
allStrapiCategory {
edges {
node {
createdAt
name
}
}
totalCount
}
}
`
console.log("Returned data: "+ pageQuery)
Graphql dashboard returns this (and though the query has something to return):
{
"data": {
"allStrapiCategory": {
"edges": [
{
"node": {
"createdAt": "2020-07-17T14:22:19.752Z",
"name": "music-history",
"id": "Category_5f11b41b2a43a66a84b0ac5c"
}
},
{
"node": {
"createdAt": "2020-07-17T14:48:40.595Z",
"name": "education",
"id": "Category_5f11ba48725ad26f9aed7aee"
}
}
],
"totalCount": 2
}
}
}
This result would also be printed out in the DOM (inside that div).
And finally, this is what the console logs:
Returned data: 1435490915
You're GraphQL data will be injected into you page through props. Try logging this out instead and you should see the result of the GraphQL query.
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
const IndexPage = ({data}) => {
console.log("Returned data: "+ data)
return (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
<div>{JSON.stringify(data)}</div>
</Layout>
)
}
export default IndexPage
export const pageQuery = graphql`
query MyQuery {
allStrapiCategory {
edges {
node {
createdAt
name
}
}
totalCount
}
}
`
The reason why you are seeing a number logged when executing console.log("Returned data: "+ pageQuery) is probably because the Gatsby internals converts the graphql tagged template literal into a number for internal usage.
I have a vue-apollo (using nuxt) query that is supposed to have a local client field show. However, when I have the show #client line included in the query the component does not render. For some reason it also seems to fail silently.
query myAccounts {
accounts: myAccounts {
email
calendars {
id
name
hex_color
is_enabled
show #client
}
}
}
I am extending the Calendar type in an extensions.js file (pasted below) with two mutations.
import gql from 'graphql-tag'
export const typeDefs = gql`
extend type Calendar {
show: Boolean
}
type Mutation {
showCalendar(id: ID!): Boolean
hideCalendar(id: ID!): Boolean
}
`
Here is the resolver that sets the value, along with the Apollo config:
import { InMemoryCache } from 'apollo-cache-inmemory'
import { typeDefs } from './extensions'
import MY_ACCOUNTS_QUERY from '~/apollo/queries/MyAccounts'
const cache = new InMemoryCache()
const resolvers = {
Mutation: {
showCalendar: (_, { id }, { cache }) => {
const data = cache.readQuery({ query: MY_ACCOUNTS_QUERY })
const found = data.accounts
.flatMap(({ calendars }) => calendars)
.find(({ id }) => id === '1842')
if (found) {
found.show = true
}
cache.writeQuery({ query: todoItemsQuery, data })
return true
}
}
}
export default context => {
return {
cache,
typeDefs,
resolvers,
httpLinkOptions: {
credentials: 'same-origin'
},
}
}
along with the nuxt config:
apollo: {
defaultOptions: {
$query: {
loadingKey: 'loading',
fetchPolicy: 'cache-and-network',
},
},
errorHandler: '~/plugins/apollo-error-handler.js',
clientConfigs: {
default: '~/apollo/apollo-config.js'
}
}
Querying local state requires the state to exist (i.e. it should be initialized) or for a local resolver to be defined for the field. Apollo will run the resolver first, or check the cache directly for the value if a resolver is not defined. There's not really a good way to initialize that value since it's nested inside a remote query, so you can add a resolver:
const resolvers = {
Calendar: {
show: (parent) => !!parent.show,
},
// the rest of your resolvers
}
See the docs for additional examples and more details.
I'm studying GraphQL Mutation components. I'm doing a mutation that adds a resolution, i.e. a New Year's resolution. Here's the schema:
type Resolution {
_id: String!
name: String!
goals: [Goal]
completed: Boolean
}
type Query {
resolutions: [Resolution]
}
type Mutation {
createResolution(name: String!): {
Resolution
user: String
}
}
Here are the resolution resolvers:
import Resolutions from "./resolutions";
import Goals from "../goals/goals";
import { PubSub } from 'graphql-subscriptions';
export const pubsub = new PubSub();
export default {
Query: {
resolutions(obj, args, { userId }) {
return Resolutions.find({
userId
}).fetch();
}
},
Resolution: {
goals: resolution =>
Goals.find({
resolutionId: resolution._id
}).fetch(),
completed: resolution => {
const goals = Goals.find({
resolutionId: resolution._id
}).fetch();
if (goals.length === 0) return false;
const completedGoals = goals.filter(goal => goal.completed);
return goals.length === completedGoals.length;
}
},
Mutation: {
createResolution(obj, { name }, { userId }) {
if (userId) {
const resolutionId = Resolutions.insert({
name,
userId
});
return Resolutions.findOne(resolutionId);
}
throw new Error("Unauthortized");
}
},
};
Here's the user resolver:
export default {
Query: {
user(obj, args, { user }) {
return user || {};
}
},
User: {
email: user => user.emails[0].address
}
};
Here's the mutation component:
const ResolutionForm = () => {
let input;
let state = {
error: null
};
return (
<Mutation
mutation={CREATE_RESOLUTION}
update={(cache, {data: {createResolution}}) => {
const {resolutions} = cache.readQuery({query: GET_RESOLUTIONS});
cache.writeQuery({
query: GET_RESOLUTIONS,
data: {resolutions: resolutions.concat([createResolution])}
});
}}
>
{(createResolution, {data}) => (
<div>
<form
onSubmit={e => {
e.preventDefault();
createResolution({
variables: {
name: input.value
},
});
input.value = "";
}}
>
<input
ref={node => {
input = node;
}}
/>
<button type="submit">Submit</button>
</form>
</div>
)}
</Mutation>
);
};
Here's the query that loads all the resolutions when the app launches:
const GET_RESOLUTIONS = gql`
query Resolutions {
resolutions {
_id
name
completed
goals {
_id
name
completed
}
}
user {
_id
}
}
`;
That works fine, but when I run the mutation:
const CREATE_RESOLUTION = gql`
mutation createResolution($name: String!) {
createResolution(name: $name) {
__typename
_id
name
goals {
_id
name
completed
}
completed
}
}
`;
...I get a console log error saying:
Missing field user in {
"resolutions": [
{
"_id": "GKTNgbuiDgiZ4wAFZ",
"name": "testing 123",
.....
How do I get the field user into my mutation response?
The GET_RESOLUTIONS query used is originally from a parent component, App.js. It really contains two separate queries-- one for the resolution and one for the user. The CREATE_RESOLUTION Mutation query and resolver, don't return user data, and I don't yet know how to get them to do that.
But, the Mutation component doesn't need the user data. It only gets upset during the call to cache.writeQuery because GET_RESOLUTIONS is asking for user, and the Mutation resolver isn't returning user.
So the fix seems to be to have a special GET_RESOLUTIONS_FOR_MUTATION_COMPONENT query that doesn't ask for user in the first place:
const GET_RESOLUTIONS_FOR_MUTATION_COMPONENT = gql`
query Resolutions {
resolutions {
_id
name
completed
goals {
_id
name
completed
}
}
}
`;
[.....]
const {resolutions} = cache.readQuery({query: GET_RESOLUTIONS_FOR_MUTATION_COMPONENT});
[.....]
Using that there is no error message asking for user.
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);
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),
};
}
};