#client Apollo GQL tag breaks query - graphql

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.

Related

local state management for graphql vue apollo

I am trying to make local state management with vue apollo, but even after following the docs I am getting no result. There is no error in the console so I am not sure what is wrong.
Here is my setup:
// main.js file the initializing part
const client = new ApolloClient({
link: ApolloLink.from([
errorLink,
authMiddleware,
link,
]),
cache,
typeDefs,
resolvers,
connectToDevTools: true,
});
// resolvers file
import gql from 'graphql-tag';
import { todoItemsQuery } from './task.queries';
export const typeDefs = gql`
type Item {
id: ID!
text: String!
done: Boolean!
}
type Mutation {
changeItem(id: ID!): Boolean
deleteItem(id: ID!): Boolean
addItem(text: String!): Item
}
`;
export const resolvers = {
Mutation: {
checkItem: (_, { id }, { cache }) => {
const data = cache.readQuery({ query: todoItemsQuery });
console.log('data res', data);
const currentItem = data.todoItems.find(item => item.id === id);
currentItem.done = !currentItem.done;
cache.writeQuery({ query: todoItemsQuery, data });
return currentItem.done;
},
},
};
//queries file
import gql from 'graphql-tag';
export const todoItemsQuery = gql`
{
todoItems #client {
id
text
done
}
}
`;
export const checkItemMutation = gql`
mutation($id: ID!) {
checkItem(id: $id) #client
}
`;
// component where I call it
apollo: {
todoItems: {
query: todoItemsQuery
}
},
checkItem(id) {
this.$apollo
.mutate({
mutation: checkItemMutation,
variables: { id }
})
.then(({ data }) => {
console.log("CACHE", data);
});
},
I get empty todoItems, no errors.
Please let me know what am I missing, I am not grasping some concept I think, and if there is a way to use vuex with apollo then I can do that too.
Foreword: I'm not an Apollo specialist, just starting to use it.
Just in case, these thoughts may help you. If they don't, well, I'm sorry.
In main.js: Apollo documentation provides a slightly different set up when using Apollo Boost (https://www.apollographql.com/docs/link/links/state/#with-apollo-boost). Just in case, this is how I have set up my implementation so far.
import VueApollo from 'vue-apollo'
import ApolloClient from 'apollo-boost';
import { InMemoryCache } from 'apollo-cache-inmemory';
const cache = new InMemoryCache();
Vue.use(VueApollo)
const apolloClient = new ApolloClient({
//...whatever you may already have,
clientState: {
// "defaults" is your initial state - if empty, I think it might error out when your app launches but is not hydrated yet.
defaults: {
todoItems: [],
}
cache,
typeDefs: {...yourTypeDefs},
resolvers: {...yourResolvers},
},
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
});
In your typeDefs: I can not see a Query Type for your todoItems:
type Query {
todoItemsQuery: [Item]
}
In your component: my own implementation was not working until I added an update method to the apollo request:
apollo: {
todoItems: {
query: todoItemsQuery,
update: data => data.todoItems
}
},

Nested query and mutation in type-Graphql

I found a feature in graphql to write nested query and mutation, I tried it but got null. I found the best practices of building graphqL schema on Meetup HolyJs and the speaker told that one of the best ways is building "Namespaced" mutations/queries nested, in this way you can write some middlewares inside the "Namespaced" mutations/queries and for get the Child mutation you should return an empty array because if you return an empty array, Graphql understand it and go one level deep.
Please check the example code.
Example in graphql-tools
const typeDefs = gql`
type Query { ...}
type Post { ... }
type Mutation {
likePost(id: Int!): LikePostPayload
}
type LikePostPayload {
recordId: Int
record: Post
# ✨✨✨ magic – add 'query' field with 'Query' root-type
query: Query!
}
`;
const resolvers = {
Mutation: {
likePost: async (_, { id }, context) => {
const post = await context.DB.Post.find(id);
post.like();
return {
record: post,
recordId: post.id,
query: {}, // ✨✨✨ magic - just return empty Object
};
},
}
};
This is my Code
types
import { ObjectType, Field } from "type-graphql";
import { MeTypes } from "../User/Me/Me.types";
#ObjectType()
export class MeNameSpaceTypes {
#Field()
hello: string;
#Field({ nullable: true })
meCheck: MeTypes;
}
import { Resolver, Query } from "type-graphql";
import { MeNameSpaceTypes } from "./MeNamespace.types";
#Resolver()
export class MeResolver {
#Query(() => MeNameSpaceTypes)
async Me() {
const response = {
hello: "world",
meCheck:{}
};
return response;
}
}
Result of code
query {
Me{
hello
meCheck{
meHello
}
}
}
--RESULT--
{
"data": {
"Me": {
"hello": "world",
"meCheck": {
"meHello": null
}
}
}
}
I got a null instead a meHello resolver. Where am I wrong?
Namespaced mutations are against GraphQL spec as they are not guarranted to run sequentially - more info in this discussion in GitHub issue related to your problem:
https://github.com/MichalLytek/type-graphql/issues/64

Update local config based on query result in react-apollo

Using apollo and react, I want to update my local apollo state based on the result of a (remote) apollo query.
The query is of type [String!]!, my local state contains { name } of type String. I want to assign the local { name } to the first item returned by the query.
const App = compose(
graphql(gql`{ name #client }`, { name: 'localData' }),
graphql(gql`{ currencies { name } }`)
)(({ localData }) => <h1>{ localData.name }</h1>)
I don't want to use componentWillReceiveProps (mutating there the local state as soon as props come from the remote query), as it's bad practice.
I need to use local apollo state, not just the state of the component, as this value is going to be used by other components.
I tried using the props config of the graphql hoc, but it feels as bad as componentWillReceiveProps, and lead to an infinite loop.
Any idea?
Relevant versioning:
"apollo-boost": "^0.1.19",
"apollo-link-state": "^0.4.2",
"react-apollo": "^2.2.4",
Code
import React from 'react'
import ReactDOM from 'react-dom'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloProvider, graphql, compose } from 'react-apollo'
import { ApolloLink } from 'apollo-link'
import { withClientState } from 'apollo-link-state'
import { HttpLink } from 'apollo-link-http'
const resolvers = {
Mutation: {
updateName: (ops, { name }, { cache }) => cache.writeData({ data: { name } })
}
}
const typedefs = gql`
type Query {
name: String
}
type Mutation {
updateName(name: String): String
}
`
const defaults = { name: 'defaultName' }
const cache = new InMemoryCache()
const stateLink = withClientState({
cache,
defaults,
resolvers
});
const apolloClient = new ApolloClient({
uri: '/store/api/graphql',
link: ApolloLink.from([stateLink, new HttpLink()]),
cache,
clientState: {
defaults,
resolvers,
typedefs
}
})
const App = compose(
graphql(gql`{ name #client }`, { name: 'localData' }),
graphql(gql`{ currencies { name id } }`)
)(({ localData }) => <h1>{ localData.name }</h1>)
ReactDOM.render(
<ApolloProvider client={ apolloClient }>
<App />
</ApolloProvider>,
document.getElementById('app')
)
I found a solution using the Query component instead of the graphql hoc. The Query component has the onCompleted property, where I can pass a function that updates the local state:
const UPDATE_NAME = gql`mutation ($name: String) {
updateName (name: $name) #client { name }
}`
const App = compose(
withApollo,
graphql(gql`{ name #client }`, { name: 'localData' })
)(({ client, localData }) => <Query
query={ gql`{ currencies { name id } }` }
onCompleted={ data =>
client.mutate({
mutation: UPDATE_NAME,
variables: { name: data.currencies[0].name }
})
}>
{
() => <h1>Current: { localData.name }</h1>
}
</Query>)
I find the Query component less elegant than the graphql hoc, but at least is a solution.

Data not returned from REST datasource in Apollo server for graphQL

I am trying out the basic implementation for Apollo server for GraphQL with my REST API calls as Data Sources. I do not see any data returned from the same even though there is data returned when I call the API separately. Can anyone help figure out what could be going wrong?
PS: I have CORS enabled on my API so not sure if I am passing that too correctly. I do not have any idea how to figure out what URL this is calling.
My sample code below:
const { ApolloServer, gql } = require('apollo-server');
const { RESTDataSource } = require('apollo-datasource-rest');
class Contact extends RESTDataSource {
constructor() {
super();
this.baseURL = 'http://localhost:8080/objects/';
}
async getContactById(id) {
return this.get(`contact/${id}`);
}
async getAllContacts() {
const data = await this.get(`contact`);
return data.results;
}
// an example making an HTTP PUT request
async newContact(contact) {
return this.put(
'contact', // path
contact, // request body
);
}
};
// Type definitions define the "shape" of your data and specify
// which ways the data can be fetched from the GraphQL server.
const typeDefs = gql`
# Comments in GraphQL are defined with the hash (#) symbol.
type Query {
allContacts: [Contact]
contactById(id: ID): Contact
}
type Contact {
id: ID
contact_name: String
}
`;
// Resolvers define the technique for fetching the types in the
// schema.
const resolvers = {
Query: {
contactById: async (_source, { id }, { dataSources }) => {
return dataSources.contact.getContactById(id);
},
allContacts: async (_source, _args, { dataSources }) => {
return dataSources.contact.getAllContacts();
},
},
};
// In the most basic sense, the ApolloServer can be started
// by passing type definitions (typeDefs) and the resolvers
// responsible for fetching the data for those types.
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
contact : new Contact(),
};
},
cors : true,
});
// This `listen` method launches a web-server. Existing apps
// can utilize middleware options, which we'll discuss later.
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Below is the request and response from the GraphQL playground:
query {
contactById (id : 5) {
id
contact_name
}
}
Response:
{
"data": {
"contactById": {
"id": null,
"contact_name": null
}
}
}

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