How can I use `schema` in apollo client cache? - graphql

I use apollo-client in a react project to manage UI state. I define a schema type for apollo mutation but it doesn't seem to work.
Below is how I create apollo client instance.
const cache = new InMemoryCache();
export const createClient = () => {
return new ApolloClient({
cache,
resolvers: {
Mutation: {
...alertResolvers
},
},
typeDefs: [alertTypeDefs]
});
};
Below code is the type schema definition. As you can see I have created a showErrorAlert return type of Alert.
export const alertTypeDefs = gql`
type Alert {
id: ID!
message: String!
type: String!
duration: Int!
}
extend type Mutation {
showErrorAlert(message: String!): Alert
}
`;
I use below code to send mutation. As you can see that it doesn't return the duration in the return object. But the application works without any error. It seems that the type doesn't have impact on the application.
gql`
mutation showErrorAlert($message: String!) {
showErrorAlert(message: $message) #client {
id
message
type
}
}
`;

From the docs:
You can optionally set a client-side schema to be used with Apollo Client, through either the ApolloClient constructor typeDefs parameter, or the local state API setTypeDefs method... This schema is not used for validation like it is on the server because the graphql-js modules for schema validation would dramatically increase your bundle size. Instead, your client-side schema is used for introspection in Apollo Client Devtools, where you can explore your schema in GraphiQL.
In other words, the only point of providing typeDefs for local state is enable querying local state through GraphiQL in Apollo Client Devtools.
There is no type validation for local state, although the client will throw if there is a mismatch in the general shape of the object in the cache compared to what's being requested.

Related

Creating a MERN-G App, when querying my database I'm stuck in loading = true

I'm creating a MERN-G food ordering app. When I query my database in the Apollo sandbox, I get all the data I've seeded my database with as expected, but when I do it in my code, I can't get past loading. Here is my code
import { useQuery } from "#apollo/client";
import { QUERY_MENU } from "../utils/queries";
export default function Menu() {
const {data, loading} = useQuery(QUERY_MENU)
useEffect(() => {
if(loading){
console.log("Loading...");
} else {
console.log(data);
}
}, [data, loading])
It never seems to change from loading.
I tried logging the data, and expected to get back the same result I'm seeing in my apollo sandbox. I am using the exact query used in the sandbox in my code.
Here are my typedefs and resolvers from my server side.
typedefs
resolvers
And my utils/queries file for the client side
utils/queries

Is it possible to query my running apollo graphqlserver locally, without using http?

I'm running a Graphql server from Apollo, and the objective is fetch some data. However, I need this data locally - on the same server. Is that possible, or is the only way to query the Apollo server using http?
I know that I could possible accomplish this without using GraphQl, and just access the data layer, but the thing is that I would like to benefit from:
Authorization
Dataloaders
Already built-in optimization in our Graphql Api
I already have a working solution where I just use node-fetch to query localhost, but it seems like quite a bit of overhead.
Yes it is possible!
Apollo makes the schema building and execution for you, but you can also do it yourself.
Here is a mini example based on the apollo-server-express package. I create the schema and then give it to the apollo-server. Look below the server startup, I also create a query-string, then parse it and execute it without apollo and without an http request.
const express = require('express');
const { ApolloServer, gql, makeExecutableSchema } = require('apollo-server-express');
const { parse } = require('graphql/language')
const { execute } = require('graphql')
// Construct a schema, using GraphQL schema language
const typeDefs = gql`
type Query {
hello: String
}
`;
// Provide resolver functions for your schema fields
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
};
const schema = makeExecutableSchema({
typeDefs,
resolvers,
})
async function startApolloServer() {
const server = new ApolloServer({ schema });
await server.start();
const app = express();
server.applyMiddleware({ app });
await new Promise(resolve => app.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
return { server, app };
}
startApolloServer()
const query = `
query {
hello
}
`
const document = parse(query)
const res = execute({
schema,
document,
})
console.log('res no request:', res)
if you run it, install apollo-server-express and graphql with npm and you are good to go
To execute you can pass all your request logic as well:
execute({
schema,
document,
rootValue: {},
contextValue: {
userInfo,
dbClient,
},
variableValues: body.variables,
}),
It is highly useful also if you want to test you server. If you need to do subscriptions you can use the subscribe method imported from graphql as well.

Apollo graphql: makeExecutableSchema & playground

Im a beginner trying to set up a graphql API with apollo-express and prisma
All was going well, but than I decided to use this library to add input validation:
graphql-constraint-directive
It requires me to use makeExecutableSchema to build my schema so I can use the schemaDirectives param
My code to start the server was like this:
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({req}) => {
const userId = getUserID(req);
return {
userId,
prisma
}
}
});
And all was working perfectly
But to use that lib I had it refactored it like this:
const schema = makeExecutableSchema({
typeDefs,
resolvers,
schemaDirectives: {constraint: ConstraintDirective}
});
const server = new ApolloServer({
schema,
context: ({req}) => {
const userId = getUserID(req);
return {
userId,
prisma
}
}
});
And it works too, all my queries and mutations are working, and the validation too.
But it broke the graphql-playground: its no longer able to load my schema and docs, both tabs are empty. And it displays the following error:
Server cannot be reached
It still works: Im able to send my querys and mutations and all, but I no longer have code completion and the auto docs, since it doenst knows my schema, and so is no longer as useful
If I replace the executable schema for the pure typeDefs ans resolvers, than it works alright again, playground loads everything again
Am I supposed to do something different when using makeExecutableSchema, for playgroun to work?
Whether you use makeExecutableSchema or pass the typeDefs and resolvers to the ApolloServer constructor directly shouldn't matter -- Apollo Server uses makeExecutableSchema under the hood anyway. In fact, you can just pass the directive map to the constructor directly:
const server = new ApolloServer({
typeDefs,
resolvers,
schemaDirectives: {constraint: ConstraintDirective}
context: ({req}) => {
const userId = getUserID(req);
return {
userId,
prisma
}
}
});
This is a bug with the library you're using. The directive replaces the built-in scalars with custom ones, but doesn't actually add those custom scalars to the schema. When Playground tries to introspect the schema, it can't find the custom scalars in the introspection results and errors out. I would avoid that particular library -- it doesn't look like it's actively maintained.

Apollo client: Can #defer be used with client side resolvers?

For some reason, I had to build a client-side only GraphQL server, my schema is built as follow:
private buildSchema(): GraphQLSchema {
const allTypes: string = ...// my types
const allResolvers: IResolvers[] = ...// my resolvers
return makeExecutableSchema({
typeDefs: allTypes,
resolvers: allResolvers
});
}
The client is as follow:
this.client = new ApolloClient({
link: new SchemaLink({schema: this.buildSchema()}),
cache: new InMemoryCache({
addTypename: false
})
});
And everything works fine except that my queries are not defered. For instance if I run:
const gqlQuery: string = `
{
user {
name
slowResolver #defer {
text
}
}
}
`
const $result = this.apollo.getClient().watchQuery({
query: gql(gqlQuery)
});
The $result will be emited only when the whole query will be resolved (instead of user and then slowResolver as expected).
Any idea of what I missed in the workflow?
The #defer directive was actually removed from Apollo, although there's been some work done to reimplement it. Even if it's implemented, though, deferred queries would have to be handled outside of the execution context. In other words, executing the schema can return a deferred execution result, but something else (like Apollo server itself) has to handle how that response (both the initial payload, and the subsequent patches) are actually sent to the server over whatever transport.
If you're defining a schema client-side, unfortunately, it's not going to be possible to use the #defer directive.

How to call an apollo client query from a redux action

If I'm using redux and the apollo client in my app, what's the best way to trigger a query from an action outside of a component.
For example, if I have a standard app, with redux and apollo client configured, how should I trigger a "refresh" list. I can trigger a function on the component itself which has the gql, but how would I do it from an action which would be more in line with flux.
import React, { Component, PropTypes } from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import { connect } from 'react-redux';
import { refreshProfile } from './actions';
class Profile extends Component { ... }
Profile.propTypes = {
data: PropTypes.shape({
loading: PropTypes.bool.isRequired,
user: PropTypes.object,
}).isRequired,
};
const UserQuery = gql`
query getUser {
user {
id
name
}
}
`;
const ProfileWithData = graphql(UserQuery)(Profile);
const ProfileWithDataAndState = connect(
(state) => ({ user: state.user })),
)(ProfileWithData);
And, say I want to trigger an action to refresh that user data? Since the logic is in the component itself, I'm not sure how I would trigger that gql query from the action itself.
I would need to use the ApolloClient in my actions.js. e.g.
import ApolloClient, { createNetworkInterface } from 'apollo-client';
const networkInterface = createNetworkInterface({
uri: config.graphCoolUri,
});
const client = new ApolloClient({
networkInterface,
dataIdFromObject: r => r.id,
});
const { data } = await client.query({
query: UserQuery
});
I see your needs, as I was just in your place couple of days ago.
The sad news is: if you want to use actions with graphQL, then you shouldn't be using apollo, just use graphQL directly. This is a very good article to walk you through - getting started with Redux and GraphQL. Why? Because Apollo uses a function called qraphql(query) which calls its own action.
How both Redux and Apollo work in a very simplistic way.
Redux: (User dispatches an action) ActionCreator --> Action --> Middleware --> reducer --> store --> bind data to user props. And we control each state manually.
Apollo: (User passes the query/mutation to graphql(query)) all hidden (action --> store) then binds data to user props.
You can say that Apollo replaces Redux if you are using graphql, because it has a better integration with react and graphQL.
In the meantime, as Apollo is still developing, you might need redux for redux-form and so on. If you are used to some redux libraries, which you might consider to continue using redux besides Apollo, you can still bind their stores and add costumed middleware that probably apply to both, but you probably won't be fetching data using Redux actions through Apollo.
I know it feels like you are loosing redux, but you are getting all advantages with more async requests and caching taking care of with Apollo.
and if you need a place to start react-redux-apollo.

Resources