I'm sitting for 2 days trying to get my setup right. My target is Next.js with ApolloGraphQl for Database request as well as local state management handling.
My problem is the setup for the local state management. Don't know why i'm unable to correctly get it to work. I hope you can help me out.
I pushed my actual status of the project on github using the following repo:
https://github.com/Maetes/apollo-next-local
UPDATE:
it is fine to query and mutate the state when i just call the cache object itself so all is fine. But when i try to trigger a resolver, nothing happens. I'ts like the Apollo-client does not see the typedefs and resolvers.
Here is my useApollo Hook with Client init:
import { IncomingMessage, ServerResponse } from 'http';
import { useMemo } from 'react';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache, NormalizedCacheObject } from 'apollo-cache-inmemory';
import { resolvers } from './localResolvers';
import { typeDefs } from './localTypeDefs';
import {
CREATE_USER,
GET_CURRENT_USER,
SET_CURRENT_USER,
ALL_USERS,
} from './localDocuments';
let apolloClient: ApolloClient<NormalizedCacheObject> | undefined;
export type ResolverContext = {
req?: IncomingMessage;
res?: ServerResponse;
};
function createIsomorphLink(context: ResolverContext = {}) {
if (typeof window === 'undefined') {
const { SchemaLink } = require('apollo-link-schema');
const { schema } = require('../pages/api/index');
return new SchemaLink({ schema, context });
} else {
const { HttpLink } = require('apollo-link-http');
return new HttpLink({
uri: 'http://localhost:3000/api',
credentials: 'same-origin',
});
}
}
function createApolloClient(context?: ResolverContext) {
let cache = new InMemoryCache();
cache.writeData({
data: {
currentUser: {
__typename: 'CurrentUser',
email: '',
nachname: '',
title: '',
id: '',
},
},
});
let client = new ApolloClient({
ssrMode: typeof window === 'undefined',
link: createIsomorphLink(context),
cache: cache,
typeDefs,
resolvers,
});
// client.writeData({
// data: {
// currentUser: {
// __typename: 'CurrentUser',
// email: '',
// nachname: '',
// title: '',
// id: '',
// },
// },
// });
return client;
}
export function initializeApollo(
initialState: any = null,
// Pages with Next.js data fetching methods, like `getStaticProps`, can send
// a custom context which will be used by `SchemaLink` to server render pages
context?: ResolverContext
) {
const _apolloClient = apolloClient ?? createApolloClient(context);
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// get hydrated here
if (initialState) {
_apolloClient.cache.restore(initialState);
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === 'undefined') return _apolloClient;
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}
export function useApollo(initialState: any) {
const store = useMemo(() => initializeApollo(initialState), [initialState]);
return store;
}
This is my typedef file:
import gql from 'graphql-tag';
export const typeDefs = gql`
extend type Query {
getCurrentUser: CurrentUser!
}
type CurrentUser {
email: String!
nachname: String!
title: String!
id: ID!
__typename: String!
}
extend type Mutation {
setCurrentUser(
email: String!
nachname: String!
title: String!
id: ID!
): CurrentUser!
}
`;
this are my resolvers:
import { InMemoryCache } from 'apollo-cache-inmemory';
import { GET_CURRENT_USER } from './localDocuments';
export const resolvers = {
CurrentUser: {
currentUser: (_: {}, { cache }: { cache: InMemoryCache }) => {
console.log('Resolver Query triggered');
// const { user }: any = cache.readQuery({
// query: GET_CURRENT_USER,
// });
const data: any = {};
data.__typename = 'CurrentUser';
return data;
},
getCurrentUser: (_: {}, { cache }: { cache: InMemoryCache }) => {
console.log('Resolver Query triggered');
// const { user }: any = cache.readQuery({
// query: GET_CURRENT_USER,
// });
const data: any = {};
data.__typename = 'CurrentUser';
return data;
},
},
Query: {
getCurrentUser: (_: {}, { cache }: { cache: InMemoryCache }) => {
console.log('Resolver Query triggered');
// const { user }: any = cache.readQuery({
// query: GET_CURRENT_USER,
// });
const data: any = {};
data.__typename = 'CurrentUser';
return data;
},
currentUser: (_: {}, { cache }: { cache: InMemoryCache }) => {
console.log('Resolver Query triggered');
// const { user }: any = cache.readQuery({
// query: GET_CURRENT_USER,
// });
const data: any = {};
data.__typename = 'CurrentUser';
return data;
},
},
Mutation: {
setCurrentUser: (
_: {},
variables: any,
{ cache }: { cache: InMemoryCache }
) => {
const { currentUser }: any = cache.readQuery({ query: GET_CURRENT_USER });
console.log('mutationResolver Triggered');
console.log(
'olduser',
currentUser.email,
currentUser.nachname,
currentUser.title
);
const newUser = {
email: variables.email,
title: variables.title,
nachname: variables.nachname,
__typename: 'CurrentUser',
};
console.log('newUser', newUser);
const erg = cache.writeQuery({
query: GET_CURRENT_USER,
data: newUser,
});
return newUser;
},
},
};
And finaly my Document file for individual input. Please note in the GetCurrentUserQuery query when i change "getCurrentUser" to "currentUser" all is working fine cause apollo targets the raw object itself.
import gql from 'graphql-tag';
//Server
export const ALL_USERS = gql`
query allUsersQuery {
allUsers {
title
nachname
email
id
__typename
}
}
`;
//client
export const GET_CURRENT_USER = gql`
query GetCurrentUserQuery {
getCurrentUser #client(always: true) {
email
nachname
title
__typename
}
}
`;
//Client
export const SET_CURRENT_USER = gql`
mutation SetCurrentUserMutation(
$email: String!
$nachname: String!
$title: String!
$id: String!
) {
setCurrentUser(email: $email, nachname: $nachname, title: $title) #client {
email
title
nachname
id
__typename
}
}
`;
//Server
export const CREATE_USER = gql`
mutation CreateUserMutation(
$email: String!
$nachname: String!
$title: String!
$password: String!
) {
createUser(
email: $email
nachname: $nachname
title: $title
password: $password
) {
__typename
email
nachname
title
password
id
}
}
`;
Related
I am running into a POST http://localhost:4000/ 400 (Bad Request) Error.
I am trying to create a new user with the following frontend.
const REGISTER_USER = gql`
mutation Mutation(
$createUser: CreateUserInput!
) {
createUser(createUserInput: $createUserInput){
email
name
token
password
}
}
`
const Register = () => {
const context = useContext(AuthContext)
let navigate = useNavigate()
const [errors, setErrors] = useState([])
function registerUserCallback() {
console.log("Callback hit")
registerUser()
}
const {onChange, onSubmit, values} = useForm(registerUserCallback, {
name: '',
email: '',
password:'',
confirmPassword: '',
})
const [registerUser, {loading}] = useMutation(REGISTER_USER, {
update(proxy, {data: {registerUser: userData}}) {
context.login(userData)
navigate('/Dashboard')
},
onError({graphQLErrors}) {
setErrors(graphQLErrors)
console.log("Error: " + graphQLErrors)
console.log(graphQLErrors)
},
variables: {createUserInput: values}
})
However, the grapQLErrors is not even being console.logged for some reason. When I run the Mutation via Apollo Studio it works. Any information would be great!
Edit: Network Tab Screenshot:
Adding Code for my httpLink:
import { ApolloClient, InMemoryCache, createHttpLink } from "#apollo/client";
import { setContext } from "#apollo/client/link/context";
const httpLink = createHttpLink({
uri: 'http://localhost:4000'
})
const authLink = setContext((_, {headers}) => {
return {
headers: {
...headers,
authorization: localStorage.getItem('token') || ""
}
}
})
export const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
Edit: createUser Mutation seems to be the issue. This is the Network response error: ["GraphQLError: Unknown argument "createUserInput" on field "Mutation.createUser".","
#Mutation((returns) => User)
async createUser(#Arg('data') data:CreateUserInput, #Ctx() ctx: Context) {
const oldUser = await ctx.prisma.user.findFirst({ where: { email: data.email}})
if(oldUser) {
throw new ApolloError('A user is already registered with the email' + data.email, 'USER_ALREADY_EXISTS')
}
var encryptedPassword = await bcrypt.hash(data.password, 10)
const newUser = await ctx.prisma.user.create({
data: {
name: data.name,
email: data.email,
password: encryptedPassword
}
})
return {token: jwt.sign(newUser, 'supersecret')}
}
Here is a screen shot of my Preview in my Network...I really don't get it.
export class CreateUserInput {
#Field((type) => String)
name: string
#Field((type) => String)
email: string
#Field((type) => String)
password: string
I'm building a webpage that consumes the spaceX graphQL api, using apollo as a client. On the landing page I want to display a 'launches' card, that when clicked on, directs to a new page with details about that particular launch, as below:
index.js
import { ApolloClient, InMemoryCache, gql } from "#apollo/client"
import Link from 'next/link'
export const getStaticProps = async () => {
const client = new ApolloClient({
uri: 'https://api.spacex.land/graphql/',
cache: new InMemoryCache()
})
const { data } = await client.query({
query: gql`
query GetLaunches {
launchesPast(limit: 10) {
id
mission_name
launch_date_local
launch_site {
site_name_long
}
links {
article_link
video_link
mission_patch
}
rocket {
rocket_name
}
}
}
`
});
return {
props: {
launches: data.launchesPast
}
}
}
export default function Home({ launches }) {
return (
<div>
{launches.map(launch => {
return(
<Link href = {`/items/${launch.id}`} key = {launch.id}>
<a>
<p>{launch.mission_name}</p>
</a>
</Link>
)
})}
</div>
)
}
I've set up a new page items/[id].js to display information about individual launches, but this is where the confusion is. Using a standard REST api I'd simply use fetch, then append the id to the end of the url to retrieve the desired data. However I'm not sure how to do the equivalent in graphQL, using the getStaticPaths function. Any suggestions?
Here's items/[id]/js, where I'm trying to render the individual launch data:
import { ApolloClient, InMemoryCache, gql } from "#apollo/client"
export const getStaticPaths = async () => {
const client = new ApolloClient({
uri: "https://api.spacex.land/graphql/",
cache: new InMemoryCache(),
});
const { data } = await client.query({
query: gql`
query GetLaunches {
launchesPast(limit: 10) {
id
}
}
`,
});
const paths = data.map((launch) => {
return {
params: { id: launch.id.toString() },
};
});
return {
paths,
fallback:false
}
};
export const getStaticProps = async (context) => {
const id = context.params.id
// not sure what to do here
}
const Items = () => {
return (
<div>
this is items
</div>
);
}
export default Items;
for getStaticPaths
export const getStaticPaths = async () => {
const { data } = await client.query({
query: launchesPastQuery, // this will query the id only
});
return {
paths: data.CHANGE_THIS.map((param) => ({
params: { id: param.id },
})),
fallback: false,
};
};
CHANGE_THIS is the Query Type that follows data in the JSON response.
for getStaticProps
export const getStaticProps = async ({
params,
}) => {
const { data } = await client.query({
query: GetLaunchPastByID ,
variables: { LaunchID: params.id, idType: "UUID" }, // the idType is optional, and the LaunchID is what you'll use for querying by it*
});
return {
props: {
launchesPast: data.CHANGE_THIS,
},
};
The launchPastQueryByID is like:
const GetLaunchPastByID = gql`
query LaunchPastByID($LaunchID: UUID!) { // UUID is id type
CHANGE_THIS(id: $LaunchID) {
id
//...
}
}
`;
sorry for not giving you the correct queries, spacex.land is currently down.
I wanna integrate multiple clients in my react-hooks application. I'm using graphql-hooks via Apollo client we have a module to integrate multiple clients
Following is the link Apollo graphql multiple client
`https://www.npmjs.com/package/#titelmedia/react-apollo-multiple-clients`
Following I'm using for graphql hooks
https://www.npmjs.com/package/graphql-hooks
How do we achieve the same for graphql-hooks?
My requirement is depending on the selection of the radio button I need to switch between these multiple clients all that in one component using more than one client.
In my app I'm using graphql-hook we wrap the component to the client here the same component has functionality wherein depending on the select one of the radio buttons the client must be switch.I'm having one client but need to integrate multiple clients I googled but i've not found so have questioned here.
Is this possible?
Can anyone please help out there
We can use new GraphQLClient() multiple times to get multiple graphql clients
Here is a good approach and you can follow it: https://www.loudnoises.us/next-js-two-apollo-clients-two-graphql-data-sources-the-easy-way/
I have two middleware graphql servers running. One for the app one for analytics. I do it in two different ways according to my need as below...
CubeClient.jsx
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { SchemaLink } from "apollo-link-schema";
import { makeExecutableSchema } from "graphql-tools";
const cache = new InMemoryCache();
const defaultDashboardItems = [{"vizState":"{\"query\":{\"measures\":[\"Product.count\"],\"timeDimensions\":[{\"dimension\":\"Product.createdAt\"}],\"dimensions\":[\"Producttype.name\"],\"filters\":[],\"order\":{}},\"chartType\":\"bar\",\"orderMembers\":[{\"id\":\"Product.count\",\"title\":\"Product Count\",\"order\":\"none\"},{\"id\":\"Producttype.name\",\"title\":\"Producttype Name\",\"order\":\"none\"},{\"id\":\"Product.createdAt\",\"title\":\"Product Created at\",\"order\":\"none\"}],\"pivotConfig\":{\"x\":[\"Producttype.name\"],\"y\":[\"measures\"],\"fillMissingDates\":true,\"joinDateRange\":false},\"shouldApplyHeuristicOrder\":true,\"sessionGranularity\":null}","name":"Product Types","id":"2","layout":"{\"x\":0,\"y\":0,\"w\":24,\"h\":8}"},{"vizState":"{\"query\":{\"measures\":[\"Sale.total\"],\"timeDimensions\":[{\"dimension\":\"Sale.createdAt\",\"granularity\":\"day\"}],\"dimensions\":[\"Customer.firstname\"],\"order\":{\"Sale.total\":\"desc\"}},\"chartType\":\"area\",\"orderMembers\":[{\"id\":\"Sale.total\",\"title\":\"Sale Total\",\"order\":\"desc\"},{\"id\":\"Customer.firstname\",\"title\":\"Customer Firstname\",\"order\":\"none\"},{\"id\":\"Sale.createdAt\",\"title\":\"Sale Created at\",\"order\":\"none\"}],\"pivotConfig\":{\"x\":[\"Sale.createdAt.day\"],\"y\":[\"Customer.firstname\",\"measures\"],\"fillMissingDates\":true,\"joinDateRange\":false},\"shouldApplyHeuristicOrder\":true}","name":"Sale Total","id":"3","layout":"{\"x\":0,\"y\":8,\"w\":24,\"h\":8}"}]
export function getCubeClient(location){
const getDashboardItems = () => {
// return JSON.parse(window.localStorage.getItem("dashboardItems")) || defaultDashboardItems;
return defaultDashboardItems
}
const setDashboardItems = items => {
return window.localStorage.setItem("dashboardItems", JSON.stringify(items));
}
const nextId = () => {
const currentId = parseInt(window.localStorage.getItem("dashboardIdCounter"), 10) || 1;
window.localStorage.setItem("dashboardIdCounter", currentId + 1);
return currentId.toString();
};
const toApolloItem = i => ({ ...i, __typename: "DashboardItem" });
const typeDefs = `
type DashboardItem {
id: String!
layout: String
vizState: String
name: String
}
input DashboardItemInput {
layout: String
vizState: String
name: String
}
type Query {
dashboardItems: [DashboardItem]
dashboardItem(id: String!): DashboardItem
}
type Mutation {
createDashboardItem(input: DashboardItemInput): DashboardItem
updateDashboardItem(id: String!, input: DashboardItemInput): DashboardItem
deleteDashboardItem(id: String!): DashboardItem
}
`;
const schema = makeExecutableSchema({
typeDefs,
resolvers: {
Query: {
dashboardItems() {
const dashboardItems = getDashboardItems();
return dashboardItems.map(toApolloItem);
},
dashboardItem(_, { id }) {
const dashboardItems = getDashboardItems();
return toApolloItem(dashboardItems.find(i => i.id.toString() === id));
}
},
Mutation: {
createDashboardItem: (_, { input: { ...item } }) => {
const dashboardItems = getDashboardItems();
item = { ...item, id: nextId(), layout: JSON.stringify({}) };
dashboardItems.push(item);
setDashboardItems(dashboardItems);
return toApolloItem(item);
},
updateDashboardItem: (_, { id, input: { ...item } }) => {
const dashboardItems = getDashboardItems();
item = Object.keys(item)
.filter(k => !!item[k])
.map(k => ({
[k]: item[k]
}))
.reduce((a, b) => ({ ...a, ...b }), {});
const index = dashboardItems.findIndex(i => i.id.toString() === id);
dashboardItems[index] = { ...dashboardItems[index], ...item };
setDashboardItems(dashboardItems);
return toApolloItem(dashboardItems[index]);
},
deleteDashboardItem: (_, { id }) => {
const dashboardItems = getDashboardItems();
const index = dashboardItems.findIndex(i => i.id.toString() === id);
const [removedItem] = dashboardItems.splice(index, 1);
setDashboardItems(dashboardItems);
return toApolloItem(removedItem);
}
}
}
});
return new ApolloClient({
cache,
uri: "http://localhost:4000",
link: new SchemaLink({
schema
})
});
}
Dashboard.jsx
import { getCubeClient } from './CubeClient';
import { Query } from "react-apollo";
let cubeClient = getCubeClient()
const Dashboard = () => {
const dashboardItem = item => (
<div key={item.id} data-grid={defaultLayout(item)}>
<DashboardItem key={item.id} itemId={item.id} title={item.name}>
<ChartRenderer vizState={item.vizState} />
</DashboardItem>
</div>
);
return(
<Query query={GET_DASHBOARD_ITEMS} client={cubeClient}>
{({ error, loading, data }) => {
if (error) return <div>"Error!"</div>;
if (loading) return (
<div className="alignCenter">
<Spinner color="#be97e8" size={48} sizeUnit="px" />
</div>
);
if (data) {
return (<DashboardView t={translate} dashboardItems={data && data.dashboardItems}>
{data && data.dashboardItems.map(deserializeItem).map(dashboardItem)}
</DashboardView>)
} else {
return <div></div>
}
}}
</Query>
)
};
Explore.jsx
import { useQuery } from "#apollo/react-hooks";
import { withRouter } from "react-router-dom";
import ExploreQueryBuilder from "../components/QueryBuilder/ExploreQueryBuilder";
import { GET_DASHBOARD_ITEM } from "../graphql/queries";
import TitleModal from "../components/TitleModal.js";
import { isQueryPresent } from "#cubejs-client/react";
import PageHeader from "../components/PageHeader.js";
import ExploreTitle from "../components/ExploreTitle.js";
import { PageLayout } from '#gqlapp/look-client-react';
import Helmet from 'react-helmet';
import { translate } from '#gqlapp/i18n-client-react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faAngleRight, faTrashAlt, faPlusCircle } from '#fortawesome/free-solid-svg-icons';
import settings from '#gqlapp/config';
import PropTypes from 'prop-types';
import { getCubeClient } from './CubeClient';
let cubeClient = getCubeClient()
const Explore = withRouter(({ history, location,t }) => {
const [addingToDashboard, setAddingToDashboard] = useState(false);
const params = new URLSearchParams(location.search);
const itemId = params.get("itemId");
const { loading, error, data } = useQuery(GET_DASHBOARD_ITEM, {
client: cubeClient,
variables: {
id: itemId
},
skip: !itemId
});
Titlemodal.jsx
import React from "react";
import { Modal, Input } from "antd";
import { useMutation } from "#apollo/react-hooks";
import { GET_DASHBOARD_ITEMS } from "../graphql/queries";
import {
CREATE_DASHBOARD_ITEM,
UPDATE_DASHBOARD_ITEM
} from "../graphql/mutations";
import { getCubeClient } from '../containers/CubeClient';
let cubeClient = getCubeClient()
const TitleModal = ({
history,
itemId,
titleModalVisible,
setTitleModalVisible,
setAddingToDashboard,
finalVizState,
setTitle,
finalTitle
}) => {
const [addDashboardItem] = useMutation(CREATE_DASHBOARD_ITEM, {
client: cubeClient,
refetchQueries: [
{
query: GET_DASHBOARD_ITEMS
}
]
});
const [updateDashboardItem] = useMutation(UPDATE_DASHBOARD_ITEM, {
client: cubeClient,
refetchQueries: [
{
query: GET_DASHBOARD_ITEMS
}
]
});
queries and mutations.js
import gql from "graphql-tag";
export const GET_DASHBOARD_ITEMS = gql`
query GetDashboardItems {
dashboardItems {
id
layout
vizState
name
}
}
`;
export const GET_DASHBOARD_ITEM = gql`
query GetDashboardItem($id: String!) {
dashboardItem(id: $id) {
id
layout
vizState
name
}
}
`;
export const CREATE_DASHBOARD_ITEM = gql`
mutation CreateDashboardItem($input: DashboardItemInput) {
createDashboardItem(input: $input) {
id
layout
vizState
name
}
}
`;
export const UPDATE_DASHBOARD_ITEM = gql`
mutation UpdateDashboardItem($id: String!, $input: DashboardItemInput) {
updateDashboardItem(id: $id, input: $input) {
id
layout
vizState
name
}
}
`;
export const DELETE_DASHBOARD_ITEM = gql`
mutation DeleteDashboardItem($id: String!) {
deleteDashboardItem(id: $id) {
id
layout
vizState
name
}
}
`;
I am trying to use REST endpoints to post data and GraphQL for query and fetch along with apollo-link-state. My rest endpoint is getting hit and application is getting created. But when I try to run the query to write to cache it's not hitting the graphql endpoint. and I keep getting the following error:
Unhandled Rejection (Error): Can't find field findApplicationByUuid({"uuid":"912dc46d-2ef8-4a77-91bc-fec421f5e4bc"}) on object (ROOT_QUERY) {
"application": {
"type": "id",
"id": "$ROOT_QUERY.application",
"generated": true
}
}.
Here are my GQL query
import gql from 'graphql-tag';
const START_APP = gql`
mutation startApp($type: String!) {
application: startApp( input: { applicationType: $type})
#rest(type: "Application", path: "v1/member/application/create", method: "POST") {
uuid: applicationUuid
}
}
`;
const GET_APP = gql`
query getAppByUuid($uuid: String!) {
application: findApplicationByUuid(uuid: $uuid) {
uuid,
type,
version,
}
}
`;
export {
START_APP,
GET_APP,
};
Here is my resolver:
import { START_APP, GET_APP } from './application'
import client from '../apolloClient';
const startApp = async (_, { type }, { cache }) => {
client.mutate({
variables: { type },
mutation: START_APP,
}).then(({ data: { application } }) => {
const { uuid } = application;
const { data } = cache.readQuery({
query: GET_APP,
variables: { uuid },
});
cache.writeQuery({
query: GET_APP,
data,
});
});
};
const resolvers = {
Mutation: {
startApp,
},
};
Here are my links:
import { resolvers, defaults } from './resolvers';
const cache = new InMemoryCache();
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message, locations, path }) =>
console.log(`[GQL Error]: Msg: ${message}, Loc: ${locations}, Path: ${path}`));
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const stateLink = withClientState({
cache,
defaults,
resolvers,
});
const restLink = new RestLink({
uri: 'http://localhost:7010/api/',
credentials: 'include',
});
const batchHttpLink = new BatchHttpLink({
uri: 'http://localhost:7010/api/graphql',
credentials: 'include',
});
const httpLink = new HttpLink({
uri: 'http://loaclhost:7010/api/graphql',
credentials: 'include',
});
const link = ApolloLink.from([
errorLink,
stateLink,
restLink,
httpLink,
]);
my client
const client = new ApolloClient({
link,
cache,
});
My react component:
// Remote Mutation
const START_APP = gql`
mutation startApp($type: String!) {
startApp(type: $type) #client {
uuid
}
}
`;
const StartApp = ({ match }) => {
const { type } = match.params;
return (
<Mutation mutation={START_APP} variables={{ type }}>
{startApp => (<button onClick={startApp}>click me</button>)}
</Mutation>
)
};
When I hit the button it calls create endpoint and creates the app and returns the uuid. But the following I want to happen is hit the graphql endpoint and query for the application using the uuid returned from the rest request, and write that data to the cache/state.
Im using Apollo Client, Graphcool and React. I have a working login form but I need the UI to update when the user is logged in, and I need this to happen in different components.
It seems apollo-link-state is the solution for this. My code below seems to work but Im getting this error:
Missing field CurrentUserIsLoggedIn in {} in writeToStore.js
My Apollo Client setup:
import React from 'react';
import ReactDOM from 'react-dom';
// Apollo
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloLink, split } from 'apollo-link';
import { withClientState } from 'apollo-link-state';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
// Components
import LoginTest from './components/App/LoginTest';
const wsLink = new WebSocketLink({
uri: `wss://subscriptions.graph.cool/v1/XXX`,
options: {
reconnect: true,
},
});
// __SIMPLE_API_ENDPOINT__ looks like: 'https://api.graph.cool/simple/v1/__SERVICE_ID__'
const httpLink = new HttpLink({
uri: 'https://api.graph.cool/simple/v1/XXX',
});
// auth
const middlewareAuthLink = new ApolloLink((operation, forward) => {
const token = localStorage.getItem('auth-token');
const authorizationHeader = token ? `Bearer ${token}` : null;
operation.setContext({
headers: {
authorization: authorizationHeader,
},
});
return forward(operation);
});
const cache = new InMemoryCache();
const defaultState = {
CurrentUserIsLoggedIn: {
__typename: 'CurrentUserIsLoggedIn',
value: false,
},
};
const stateLink = withClientState({
cache,
defaults: defaultState,
resolvers: {
Mutation: {
CurrentUserIsLoggedIn: (_, args) => {
const data = {
CurrentUserIsLoggedIn: {
__typename: 'CurrentUserIsLoggedIn',
value: args.value,
},
};
cache.writeData({ data });
},
},
},
});
const client = new ApolloClient({
cache,
link: ApolloLink.from([
stateLink,
middlewareAuthLink,
split(
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httpLink,
),
]),
});
ReactDOM.render(
<ApolloProvider client={client}>
<LoginTest />
</ApolloProvider>,
document.getElementById('root'),
);
LoginTest.js:
import React from 'react';
import { graphql, compose } from 'react-apollo';
import gql from 'graphql-tag';
import App from './App';
const LoginTest = props => {
if (props.LoginServerQuery.loading) return <p>Loading...</p>;
// If the server tells us the user is logged in
if (props.LoginServerQuery.loggedInUser) {
// Then set the local logged in state to true
props.CurrentUserIsLoggedInMutation({
variables: {
value: true,
},
});
}
return <App />;
};
const CurrentUserIsLoggedInMutation = gql`
mutation CurrentUserIsLoggedInMutation($value: Boolean) {
CurrentUserIsLoggedIn(value: $value) #client {
value
}
}
`;
const LoginServerQuery = gql`
query LoginServerQuery {
loggedInUser {
id
}
}
`;
const LoginTestQuery = compose(
graphql(LoginServerQuery, { name: 'LoginServerQuery' }),
graphql(CurrentUserIsLoggedInMutation, {
name: 'CurrentUserIsLoggedInMutation',
}),
)(LoginTest);
export default LoginTestQuery;
At the moment, apollo-link-state requires you to return any result in your resolver function. It can be null too. This might be changed in the future.
const stateLink = withClientState({
cache,
defaults: defaultState,
resolvers: {
Mutation: {
CurrentUserIsLoggedIn: (_, args) => {
const data = {
CurrentUserIsLoggedIn: {
__typename: 'CurrentUserIsLoggedIn',
value: args.value,
},
};
cache.writeData({ data });
return data;
},
},
},
try adding a return statement in your mutation. Similar problem occured here with different function: apollo-link-state cache.writedata results in Missing field warning