I am working on a nextJS with react-apollo and apollo-client. I am using apollo-cache-persist to persist my data. But when I refresh the page, data are lost.
I tried the solution provided here but sadly didn't work.
import React from "react";
import Head from "next/head";
import App, { Container } from "next/app";
import { ApolloClient } from "apollo-client";
import {ApolloProvider} from "react-apollo";
import { InMemoryCache } from "apollo-cache-inmemory";
import { createHttpLink } from "apollo-link-http";
import { withClientState } from "apollo-link-state";
import fetch from "node-fetch";
import { resolvers, defaults } from "../Container/resolvers";
import { ApolloLink } from "apollo-link";
const cache = new InMemoryCache();
const stateLink = withClientState({
cache,
defaults,
resolvers
});
const httpLink = new createHttpLink({
fetch,
uri: "http://localhost:4444/",
headers: {
"Content-Type": "application/json"
}
});
export const client = new ApolloClient({
link: ApolloLink.from([stateLink, httpLink]),
cache
});
export default class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
return {
pageProps: {
// Call page-level getInitialProps
...(Component.getInitialProps
? await Component.getInitialProps(ctx)
: {})
}
};
}
async ComponentWillMount() {
await persistCache({
cache
});
}
render() {
const { Component, pageProps, store } = this.props;
return (
<ApolloProvider client={client}>
<Container>
<Head>
<title>HELLO WORLD</title>
</Head>
<Component {...pageProps} />
</Container>
</ApolloProvider>
);
}
}
I expect the cache to persist but the cache returns nothing on reload.
Related
I was trying to make a little demo with GraphQL subscriptions and GraphQL Apollo client.
I already have my GraphQL API, but when I try to use Apollo client, it looks like it doesn't complete the websocket subscribe step:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery } from '#apollo/client';
import { split, HttpLink } from '#apollo/client';
import { getMainDefinition } from '#apollo/client/utilities';
import { GraphQLWsLink } from '#apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { useSubscription } from '#apollo/react-hooks'
import reportWebVitals from './reportWebVitals';
const httpLink = new HttpLink({
uri: 'https://mygraphql.api'
});
const wsLink = new GraphQLWsLink(createClient({
url: 'wss://mygraphql.api',
options: {
reconnect: true
}
}));
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
fetchOptions: {
mode: 'no-cors',
}
});
const FAMILIES_SUBSCRIPTION = gql`
subscription{
onFamilyCreated {
id
name
}
}
`;
function LastFamily() {
const { loading, error, data } = useSubscription(FAMILIES_SUBSCRIPTION, {
variables: { },
onData: data => console.log('new data', data)
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error!</div>;
console.log(data);
const family = data.onFamilyCreated[0];
return (
<div>
<h1>{family.name}</h1>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
(<ApolloProvider client={client}>
<div>
<LastFamily />
</div>
</ApolloProvider>));
reportWebVitals();
According to graphql-transport-ws, to accomplish a success call, it should call connection_init and subscribe message. But when I open Dev Tools, it only sends "connection_init"
I'm expecting this output:
What step should I add to accomplish a successful call using graphql-transport-ws?
P.s. I'm not a React Developer, just be kind.
The solutions I'm putting up are based on #apollo/server v.4, with expressMiddleware and mongodb/mongoose on the backend and subscribeToMore with updateQuery on the client-side instead of useSubscription hook. In light of my observations, I believe there may be some issues with your backend code that require refactoring. The transport library graphql-transport-ws has been deprecated and advise to use graphql-ws. The following setup also applies as of 12.2022.
Subscription on the backend
Install the following dependencies.
$ npm i #apollo/server #graphql-tools/schema graphql-subscriptions graphql-ws ws cors body-parser mongoose graphql express
Set up the db models, I will refer to mongodb using mongoose and it might look like this one e.g.
import mongoose from 'mongoose'
const Schema = mongoose.Schema
const model = mongoose.model
const FamilySchema = new Schema({
name: {
type: String,
unique: true, //optional
trim: true,
}
})
FamilySchema.virtual('id').get(function () {
return this._id.toHexString()
})
FamilySchema.set('toJSON', {
virtuals: true,
transform: (document, retObj) => {
delete retObj.__v
},
})
const FamilyModel = model('FamilyModel', FamilySchema)
export default FamilyModel
Setup schema types & resolvers; it might look like this one e.g.
// typeDefs.js
const typeDefs = `#graphql
type Family {
id: ID!
name: String!
}
type Query {
families: [Family]!
family(familyId: ID!): Family!
}
type Mutation {
createFamily(name: String): Family
}
type Subscription {
familyCreated: Family
}
`
// resolvers.js
import { PubSub } from 'graphql-subscriptions'
import mongoose from 'mongoose'
import { GraphQLError } from 'graphql'
import FamilyModel from '../models/Family.js'
const pubsub = new PubSub()
const Family = FamilyModel
const resolvers = {
Query: {
families: async () => {
try {
const families = await Family.find({})
return families
} catch (error) {
console.error(error.message)
}
},
family: async (parent, args) => {
const family = await Family.findById(args.familyId)
return family
},
Mutation: {
createFamily: async (_, args) => {
const family = new Family({ ...args })
try {
const savedFamily = await family.save()
const createdFamily = {
id: savedFamily.id,
name: savedFamily.name
}
// resolvers for backend family subscription with object iterator FAMILY_ADDED
pubsub.publish('FAMILY_CREATED', { familyCreated: createdFamily })
return family
} catch (error) {
console.error(error.message)
}
}
},
Subscription: {
familyCreated: {
subscribe: () => pubsub.asyncIterator('FAMILY_CREATED'),
}
},
Family: {
id: async (parent, args, contextValue, info) => {
return parent.id
},
name: async (parent) => {
return parent.name
}
}
}
export default resolvers
At the main entry server file (e.g. index.js) the code might look like this one e.g.
import dotenv from 'dotenv'
import { ApolloServer } from '#apollo/server'
import { expressMiddleware } from '#apollo/server/express4'
import { ApolloServerPluginDrainHttpServer } from '#apollo/server/plugin/drainHttpServer'
import { makeExecutableSchema } from '#graphql-tools/schema'
import { WebSocketServer } from 'ws'
import { useServer } from 'graphql-ws/lib/use/ws'
import express from 'express'
import http from 'http'
import cors from 'cors'
import bodyParser from 'body-parser'
import typeDefs from './schema/tpeDefs.js'
import resolvers from './schema/resolvers.js'
import mongoose from 'mongoose'
dotenv.config()
...
mongoose.set('strictQuery', false)
let db_uri
if (process.env.NODE_ENV === 'development') {
db_uri = process.env.MONGO_DEV
}
mongoose.connect(db_uri).then(
() => {
console.log('Database connected')
},
(err) => {
console.log(err)
}
)
const startGraphQLServer = async () => {
const app = express()
const httpServer = http.createServer(app)
const schema = makeExecutableSchema({ typeDefs, resolvers })
const wsServer = new WebSocketServer({
server: httpServer,
path: '/',
})
const serverCleanup = useServer({ schema }, wsServer)
const server = new ApolloServer({
schema,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose()
},
}
},
},
],
})
await server.start()
app.use(
'/',
cors(),
bodyParser.json(),
expressMiddleware(server)
)
const PORT = 4000
httpServer.listen(PORT, () =>
console.log(`Server is now running on http://localhost:${PORT}`)
)
}
startGraphQLServer()
Subscription on the CRA frontend
Install the following dependencies.
$ npm i #apollo/client graphql graphql-ws
General connection setup e.g.
// src/client.js
import { ApolloClient, HttpLink, InMemoryCache, split } from '#apollo/client'
import { getMainDefinition } from '#apollo/client/utilities'
import { defaultOptions } from './graphql/defaultOptions'
import { GraphQLWsLink } from '#apollo/client/link/subscriptions'
import { createClient } from 'graphql-ws'
...
const baseUri = process.env.REACT_APP_BASE_URI // for the client
const wsBaseUri = process.env.REACT_APP_WS_BASE_URI // for the backend as websocket
const httpLink = new HttpLink({
uri: baseUri,
})
const wsLink = new GraphQLWsLink(
createClient({
url: wsBaseUri
})
)
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
const client = new ApolloClient({
cache: new InMemoryCache(),
link: splitLink,
})
export default client
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import client from './client'
import { ApolloProvider } from '#apollo/client'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>
)
Define the operations types for the client: queries, mutation & subscription e.g.
// src/graphql.js
import { gql } from '#apollo/client'
// Queries
export const FAMILIES = gql`
query Families {
families {
id
name
}
}
`
export const FAMILY = gql`
query Family($familyId: ID) {
family {
id
name
}
}
`
// Mutation
export const CREATE_FAMILY = gql`
mutation createFamily($name: String!) {
createFamily(name: $name) {
id
name
}
}
`
// Subscription
export const FAMILY_SUBSCRIPTION = gql`
subscription {
familyCreated {
id
name
}
}
Components, it might look like this one e.g.
Apollo's useQuery hook provides us with access to a function called subscribeToMore. This function can be destructured and used to act on new data that comes in via subscription. This has the result of rendering our app real-time.
The subscribeToMore function utilizes a single object as an argument. This object requires configuration to listen for and respond to subscriptions.
At the very least, we must pass a subscription document to the document key in this object. This is a GraphQL document in which we define our subscription.
We can a updateQuery field that can be used to update the cache, similar to how we would do in a mutation.
// src/components/CreateFamilyForm.js
import { useMutation } from '#apollo/client'
import { CREATE_FAMILY, FAMILIES } from '../graphql'
...
const [createFamily, { error, loading, data }] = useMutation(CREATE_FAMILY, {
refetchQueries: [{ query: FAMILIES }], // be sure to refetchQueries after mutation
})
...
// src/components/FamilyList.js
import React, { useEffect, useState } from 'react'
import { useQuery } from '#apollo/client'
import { Families, FAMILY_SUBSCRIPTION } from '../graphql'
const { cloneDeep, orderBy } = pkg
...
export const FamilyList = () => {
const [families, setFamilies] = useState([])
const { loading, error, data, subscribeToMore } = useQuery(Families)
...
useEffect(() => {
if (data?.families) {
setFamilies(cloneDeep(data?.families)) // if you're using lodash but it can be also setFamilies(data?.families)
}
}, [data?.families])
useEffect(() => {
subscribeToMore({
document: FAMILY_SUBSCRIPTION,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev
const newFamily = subscriptionData.data.familyCreated
if (!prev.families.find((family) => family.id === newFamily.id)) {
return Object.assign({}, prev.families, {
families: [...prev.families, newFamily],
})
} else {
return prev
}
},
})
}, [subscribeToMore])
const sorted = orderBy(families, ['names'], ['desc']) // optional; order/sort the list
...
console.log(sorted)
// map the sorted on the return statement
return(...)
END. Hard-coding some of the default resolvers are useful for ensuring that the value that you expect will returned while avoiding the return of null values. Perhaps not in every case, but for fields that refer to other models or schema.
Happy coding!
I need to fetch some data in real-time. So I decided to use the WebSocket connection
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { ApolloLink, split } from "apollo-link";
import { WebSocketLink } from "apollo-link-ws";
import { HttpLink } from "apollo-link-http";
import { setContext } from "apollo-link-context";
import { firebaseAppAuth } from "../../App";
import { onError } from "apollo-link-error";
import { getMainDefinition } from "apollo-utilities";
import config from "../../config";
const authLink = setContext((_, { headers }) => {
//it will always get unexpired version of the token
if (firebaseAppAuth && firebaseAppAuth.currentUser) {
return firebaseAppAuth.currentUser.getIdToken().then((token) => {
return {
headers: {
...headers,
"content-type": "application/json",
authorization: token ? `Bearer ${token}` : "",
},
};
});
} else {
return {
headers: {
...headers,
},
};
}
});
const errLink = onError(({ graphQLErrors, networkError }) => {
console.log(graphQLErrors);
console.log(networkError);
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) => {
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
);
});
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
});
const httpLink = new HttpLink({
uri: config.adminAPI,
});
const wsLink = new WebSocketLink({
uri: config.apiSocket, // use wss for a secure endpoint
options: {
reconnect: true,
},
});
const splittedLink = split(
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === "OperationDefinition" && operation === "subscription";
},
wsLink,
httpLink
);
const link = ApolloLink.from([errLink, authLink, splittedLink]);
const client = new ApolloClient({
cache: new InMemoryCache(),
link,
});
export default client;
I created authLink, errLink, httpLink and wsLink like the above code. and used the split function to combine httpLink and wsLink, it can run queries without any issues, but when I try to run a subscription hook, it throws following error message.
Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance in via options.
I am also passing the client as a prop
ReactDOM.render(
<BrowserRouter>
<ApolloProvider client={client}>
<SnackbarProvider maxSnack={5}>
<SnackbarUtilsConfigurator />
<Route path="/" component={App} />
</SnackbarProvider>
</ApolloProvider>
</BrowserRouter>,
document.getElementById("root")
);
How to resolve this issue?
Could you please share how are you using ApolloProvider? It looks like you are missing this:
import React from 'react';
import { render } from 'react-dom';
import { ApolloProvider } from '#apollo/client/react';
const client = new ApolloClient({ uri, cache });
function App() {
return (
<div>
<h2>My first Apollo app 🚀</h2>
</div>
);
}
render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root'),
);
Make sure that you're passing client as a prop to ApolloProvider
I've been having issues getting started with React, Apollo, and AWS-AppSync. I can't resolve this error message:
TypeError: this.currentObservable.query.getCurrentResult is not a function
I'm using the updated packages of #apollo/react-hooks and aws-appsync.
My current setup looks like this.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import config from './aws-exports';
import AWSAppSyncClient from 'aws-appsync';
import { ApolloProvider } from '#apollo/react-hooks';
const client = new AWSAppSyncClient({
url: config.aws_appsync_graphqlEndpoint,
region: config.aws_appsync_region,
auth: {
type: config.aws_appsync_authenticationType,
apiKey: config.aws_appsync_apiKey
}
});
ReactDOM.render(
<ApolloProvider client={client}>
<React.StrictMode>
<App />
</React.StrictMode>
</ApolloProvider>,
document.getElementById('root')
);
And I have a function that makes a query that looks like this:
import React from 'react';
import { useQuery } from '#apollo/react-hooks';
import gql from 'graphql-tag';
const Flavors = () => {
const GET_FLAVORS = gql`
query listAll {
items {
name,
image
}
}
`;
const { loading, error, data } = useQuery(GET_FLAVORS);
if(loading) return <p>loading...</p>
if(error) return <p>Something went wrong...</p>
return (
<div>
{
data.listIceCreamTypes.items.map(type => {
return <div key={type.name}>
<img src={type.image} alt={type.name} />
<h1>{type.name}</h1>
</div>
})
}
</div>
)
}
export default Flavors;
I've gone through various solutions described in https://github.com/apollographql/react-apollo/issues/3148 such as adding:
"resolutions": {
"apollo-client": "2.6.3"
}
to package.json. Then re-running npm install and restarting the server.
Nothing seems to solve my issues.
Edit** Here's a repo to reproduce the problem: https://github.com/Rynebenson/IceCreamQL
I already answered this on other related question here on stackoverflow..
As mentioned in other answer the problem is because aws-appsync is relying in an previous version apollo-client. Using resolutions is not the "cleaner" way to solve this problem as you're fixing a dependency version which is not fully compatible with this library.
I strongly recommend you to create a custom apollo client for AWS AppSync this way:
import { ApolloProvider } from '#apollo/react-hooks';
import { ApolloLink } from 'apollo-link';
import { createAuthLink } from 'aws-appsync-auth-link';
import { createHttpLink } from 'apollo-link-http';
import { AppSyncConfig } from './aws-exports';
import ApolloClient from 'apollo-client';
const url = AppSyncConfig.graphqlEndpoint;
const region = AppSyncConfig.region;
const auth = {
type: AppSyncConfig.authenticationType,
apiKey: AppSyncConfig.apiKey
};
const link = ApolloLink.from([
createAuthLink({ url, region, auth }),
createHttpLink({ uri: url })
]);
const client = new ApolloClient({
link,
cache: new InMemoryCache()
});
const WithProvider = () => (
<ApolloProvider client={client}>
<App />
</ApolloProvider>
)
export default WithProvider
I also created a more detailed post on medium
Wanted to post my experience that is a more recent. The accepted answer is still correct. aws-appsync uses outdated packages which conflict with the newest versions of the apollo client.
import React from 'react';
import PropTypes from 'prop-types';
// Apollo Settings
import { ApolloClient, ApolloProvider, InMemoryCache } from '#apollo/client';
import { ApolloLink } from 'apollo-link';
// AppSync
import { Auth } from 'aws-amplify';
import { createAuthLink } from 'aws-appsync-auth-link';
import { createHttpLink } from 'apollo-link-http';
import awsconfig from 'assets/lib/aws-exports';
// jwtToken is used for AWS Cognito.
const client = new ApolloClient({
link: ApolloLink.from([
createAuthLink({
url: awsconfig.aws_appsync_graphqlEndpoint,
region: awsconfig.aws_appsync_region,
auth: {
type: awsconfig.aws_appsync_authenticationType,
apiKey: awsconfig.aws_appsync_apiKey,
jwtToken: async () => (await Auth.currentSession()).getAccessToken().getJwtToken(),
},
}),
createHttpLink({ uri: awsconfig.aws_appsync_graphqlEndpoint }),
]),
cache: new InMemoryCache(),
});
const withApollo = ({ children}) => {
return <ApolloProvider client={client}><App /></ApolloProvider>;
};
withApollo.propTypes = {
children: PropTypes.object,
authData: PropTypes.object,
};
export default withApollo;
Package.json
"#apollo/client": "^3.3.15",
"#apollo/react-hooks": "^4.0.0",
"apollo-link": "^1.2.14",
"apollo-link-http": "^1.5.17",
"aws-amplify": "^3.3.27",
"aws-amplify-react-native": "^4.3.2",
"aws-appsync-auth-link": "^3.0.4",
You must initialize appsync like the following method.
const graphqlClient = new appsync.AWSAppSyncClient({
url: graphqlEndpoint,
region: process.env.AWS_REGION,
auth: {
type: 'AWS_IAM',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
sessionToken: process.env.AWS_SESSION_TOKEN
}
},
disableOffline: true
});
below is my setup of Gatsby SSR with Apollo for those looking for a solution on how to set them up. Hope it helps those looking online for a solution on how to set up Gatsby with Apollo
Credits: [https://github.com/apollographql/subscriptions-transport-ws/issues/333]
I am not sure at the moment if my onError variable is placed correctly. I will look it up
Nonetheless, subscription with gatsby client-side routes is working fine with this setup
client.js // to be imported into ApolloProvider
import ApolloClient from 'apollo-client'
// import * as ws from 'ws'
// import { createHttpLink } from 'apollo-link-http'
import { split } from 'apollo-link'
// import { setContext } from 'apollo-link-context'
import { onError } from 'apollo-link-error'
import { HttpLink } from 'apollo-link-http'
import { WebSocketLink } from 'apollo-link-ws'
// import fetch from 'isomorphic-fetch' // Comment out this line results in an error ...
import 'isomorphic-fetch'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { getMainDefinition } from 'apollo-utilities'
const HTTP_URI = `http://localhost:4000/graphql`
const WS_URI = `ws://localhost:4000/graphql`
const httplink = new HttpLink({
uri: HTTP_URI,
// credentials: 'same-origin',
// fetch,
})
const wsLink = process.browser
? new WebSocketLink({
// if you instantiate in the server, the error will be thrown
uri: WS_URI,
options: {
reconnect: true,
},
})
: null
const errorLink = onError(({ networkError, graphQLErrors }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
)
}
if (networkError) console.log(`[Network error]: ${networkError}`)
})
const link = process.browser
? split(
//only create the split in the browser
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
httplink,
errorLink
)
: httplink
export const client = new ApolloClient({
link,
cache: new InMemoryCache(),
})
graphql/subscriptions.js
import gql from 'graphql-tag'
const BOOK_ADDED_SUBSCRIPTION = gql`
subscription {
bookAdded {
_id
title
author
}
}
`
export { BOOK_ADDED_SUBSCRIPTION }
I'd like to be able to mock out some queries on the client-side so I don't have to provide a GraphQL endpoint in order to work on my React app.
According to the Apollo docs, it looks like I should be using apollo-link-schema. I've tried to follow the example, but in my case I end up with an error: Network error: Expected undefined to be a GraphQL schema. when trying to access the query result inside my wrapped component.
Can someone help me understand what I'm doing wrong here?
Here's a fully contained index.js example to illustrate my problem:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { SchemaLink } from "apollo-link-schema";
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloCache } from 'apollo-cache';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import { makeExecutableSchema, addMockFunctionsToSchema, MockList } from 'graphql-tools';
const typeDefs = `
type Query {
me: Person!
}
type Person {
name: String!
}
`;
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({ schema });
const schemaLink = new SchemaLink(schema);
const client = new ApolloClient({
link: schemaLink,
cache: new InMemoryCache()
});
class App extends Component {
render() {
if (this.props.query && this.props.query.loading) {
return <div>Loading...</div>
}
if (this.props.query && this.props.query.error) {
return <div>Error: {this.props.query.error.message}</div>
}
const person = this.props.query.me;
return <div> {person.name} </div>
}
}
const personQuery = gql`
query PersonQuery {
me {
name
}
}
`;
App = graphql(personQuery, { name: 'query' })(App);
ReactDOM.render(
<ApolloProvider client={client}>
< App />
</ApolloProvider>
, document.getElementById('root')
);
Update: I can get things working using MockedProvider from react-apollo/test-utils but I'm wondering if this is the best way forward. For example, as far as I can tell using MockedProvider requires that you set up exact matches for each query your component might perform, whereas addMockFunctionsToSchema from graphql-tools will let you customize your mocks more flexibly, as indicated in the docs -- I just wish I could make that work.
Here's the updated index.js file example working with MockedProvider:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { SchemaLink } from "apollo-link-schema";
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloCache } from 'apollo-cache';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import { MockedProvider } from 'react-apollo/test-utils';
import { makeExecutableSchema, addMockFunctionsToSchema, MockList } from 'graphql-tools';
const typeDefs = `
type Query {
me: Person!
}
type Person {
name: String!
}
`;
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({ schema });
const schemaLink = new SchemaLink(schema);
const client = new ApolloClient({
link: schemaLink,
cache: new InMemoryCache()
});
class App extends Component {
render() {
if (this.props.query && this.props.query.loading) {
return <div>Loading...</div>
}
if (this.props.query && this.props.query.error) {
return <div>Error: {this.props.query.error.message}</div>
}
const person = this.props.query.me;
return <div> {person.name} </div>
}
}
const personQuery = gql`
query PersonQuery {
me {
__typename
name
}
}
`;
const personQueryMockedData = {
me: {
__typename: 'String',
name: 'Gabe'
}
}
App = graphql(personQuery, { name: 'query' })(App);
ReactDOM.render(
<MockedProvider mocks={
[
{
request: { query: personQuery, variables: { cache: false } },
result: { data: personQueryMockedData }
}
]
}>
< App />
</MockedProvider>
, document.getElementById('root')
);
I think the link needs to be something else
Try this
import { ApolloLink, Observable } from 'apollo-link';
// ...
const link = new ApolloLink(operation => {
const { query, operationName, variables } = operation;
return new Observable(observer =>
graphql(schema, print(query), null, null, variables, operationName)
.then(result => {
observer.next(result);
observer.complete();
})
.catch(observer.error.bind(observer))
);
});
In context of code
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { ApolloLink, Observable } from 'apollo-link';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { SchemaLink } from "apollo-link-schema";
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloCache } from 'apollo-cache';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import { makeExecutableSchema, addMockFunctionsToSchema, MockList } from 'graphql-tools';
const typeDefs = `
type Query {
me: Person!
}
type Person {
name: String!
}
`;
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({ schema });
const link = new ApolloLink(operation => {
const { query, operationName, variables } = operation;
return new Observable(observer =>
graphql(schema, print(query), null, null, variables, operationName)
.then(result => {
observer.next(result);
observer.complete();
})
.catch(observer.error.bind(observer))
);
});
const client = new ApolloClient({
link,
cache: new InMemoryCache()
});
class App extends Component {
render() {
if (this.props.query && this.props.query.loading) {
return <div>Loading...</div>
}
if (this.props.query && this.props.query.error) {
return <div>Error: {this.props.query.error.message}</div>
}
const person = this.props.query.me;
return <div> {person.name} </div>
}
}
const personQuery = gql`
query PersonQuery {
me {
name
}
}
`;
console.log(personQuery)
App = graphql(personQuery, { name: 'query' })(App);
ReactDOM.render(
<ApolloProvider client={client}>
< App />
</ApolloProvider>
, document.getElementById('root')
);