How to define client side schema in `react-apollo` application? - graphql

I am using react-apollo in my react applicant and I can't figure out how to implement a client side schema.
I have below type definition:
export const alertTypeDefs = gql`
type Query {
alert: Alert
}
type Alert {
message: String!
type: String!
duration: Int!
}
`;
It defines a Query which returns an alert object.
Below is the code I want to use this query.
const cache = new InMemoryCache();
export const createClient = () => {
return new ApolloClient({
cache,
typeDefs: [alertTypeDefs]
});
};
First I initialised a ApolloClient instance with memory cache and alertTypeDefs defined above. Then below is the code to run the query:
const client = createClient();
const data = client.readQuery({query: gql`
{
alert #client
}
`});
console.log('data:', data);
But I got this error Missing selection set for object of type Alert returned for query field alert when run readQuery on the client instance. It seems that the Alert is not defined. But I already defined the Alert query in the typeDefs. It works fine if I change the query code to below which I have to specify what to be returned inside { message }. But it doesn't seem to use the schema. What I expect is that I don't need to specify the return fields if it returns all fields in the schema object. Do I mis-understand the schema?
const data = client.readQuery({query: gql`
{
alert #client {
message
}
}
`});
console.log('data:', data);
If I have to specify the return fields one by one, what the point to define the schema?

This is expected behavior with GraphQL. You always need to specify inside the query which fields you're expecting. So in order to receive all the data you add the fields to the query:
const data = client.readQuery({query: gql`
{
alert #client {
message
type
duration
}
}
`});
console.log('data:', data);
There is an open issue inside the GraphQL specs.

You can define a fragment with all the fields of the entity and then reuse it.
Like this
fragment AllAlertFields on Alert {
message
type
duration
}
And then in a query
query {
allAlerts {
...AllAlertFields
}
}
More details: https://www.apollographql.com/docs/react/data/fragments/

Related

Apollo GraphQL query refetch

I'm trying to do a query refetch after a form submission using Apollo. I am trying to use this example: https://www.apollographql.com/docs/react/data/queries/#refetching
My query is:
const { data: accountData, loading: accountLoading, refetch: accountDataRefetch } = useGetUserSocialLoginQuery({ variables: { accountId: nrId } })
After the form submission I'm calling the refetch function:
const formSubmissionFunction = async () => {
// update an UserSocialLogin entity
await accountDataRefetch()
}
I've also tried to update the query result within the update mutation, but also without success. The UserSocialLogin entity is updated but the data object remains the same. The UI should display the new data.
Do you have any ideas?

Graphql apollo-client useMutation variable with gql , GraphQLInputObjectType does not check for input object fields

could anyone please help me understand on how to validate the input argument object type on apollo client , i am trying to make sure that passing additional fields on the input object fails the query . But it works even if i pass additional fields (Note : I want the validation on front end and not on the server)
const mutationInput = new GraphQLObjectType({
name: 'mutationInput',
fields: {
Id: String,
statusCode: String,
}
});
const GET_DATA = gql`
mutation($in: ${mutationInput}!){
getData(in: $in) {
status
}
}
}`;
//usage
const [getData, { error,loading }] = useMutation(
GET_DATA,
{
...
}
)
getData({
variables: {
in: {
statusCode:"OK",
ID:"azz12",
extraField:"some value" //Validation needs to fail as this field is not defined on input object, but never fails on front end
}
}

Apollo server 2.0. Type "Upload" not found in document

How to replicate:
server.js
const { ApolloServer, makeExecutableSchema, gql } = require('apollo-server');
const typeDefs = gql`
type Mutation {
uploadAvatar(upload: Upload!): String!
}
`;
const resolvers = {
Mutation: {
uploadAvatar(root, args, context, info) {
return 'test';
}
}
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const server = new ApolloServer({
schema,
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
package.json
"dependencies": {
"apollo-server": "^2.0.0-rc.6",
"graphql": "^0.13.2"
}
On node server.js we get the following error:
Type "Upload" not found in document.
Given the latest version of apollo server, am I supposed to add anything else to the query? According to this tutorial and few other sources that I currently cannot recall, one does not need to do anything more than just write Upload and it should work fine. Am I missing anything?
There are a couple of ways I've fixed this, in the example on the apollo docs:
https://www.apollographql.com/docs/guides/file-uploads.html
you can see he doesn't use makeExecutableSchema but passed the resolvers and schema to the apollo server this stopped the error:
Type "Upload" not found in document.
If you want to use makeExecutableSchema then import the scalar
const typeDefs = gql`
scalar Upload
type Mutation {
uploadAvatar(upload: Upload!): String!
}
type Query {
ping: String
}
`;
https://github.com/jaydenseric/apollo-upload-examples/blob/master/api/schema.mjs
if you look at some of the example source code for the at blog post you can see he uses a scalar
The reason it wasn't being added automatically is
Scalar Upload
The Upload type automatically added to the schema by Apollo Server resolves an object containing the following:
stream
filename
mimetype
encoding
UPDATE: Apollo have made it clearer that when you use makeExecutableSchema you need to define the scalar for it to work
In a situation where a schema is set manually using makeExecutableSchema and passed to the ApolloServer constructor using the schema params, add the Upload scalar to the type definitions and Upload to the resolver
https://www.apollographql.com/docs/guides/file-uploads.html#File-upload-with-schema-param

Pass ID from React to Apollo to find correct result?

Im using React with Apollo (Apollo Client v2). I have group query which needs to return a single group.
This code is working but I've hard coded HARD-CODED-ID. How can I instead pass the ID as a string from the React component?
In my React component:
const groupQuery = gql`
query Group {
group {
_id
name
}
}
`;
export default graphql(groupQuery, {
props: ({ data }) => ({ ...data }),
})(GroupPage);
My resolver:
Query: {
groups() {
return Groups.find().fetch();
},
group() {
return Groups.findOne('HARD-CODED-ID');
},
}
There's three things that you'll need to do:
1.) If you haven't already, modify the schema on your server so that your query accepts the id as an input, for example:
type Query {
#other queries
group(id: ID!): Group
}
2.) Modify your resolver so that it handles the passed-in id. Assuming you're using graphql-tools:
group(root, { id }) {
return Groups.findOne(id); // did you mean something like findOne({id}) ?
},
3.) Modify your client-side code. Typically, you'll make the id a prop you pass in to your component, and then use that as a variable in your request.
const groupQuery = gql`
query Group($id: ID!) {
group(id: $id) {
_id
name
}
}
`;
// assuming that the component prop is called groupId
export default graphql(groupQuery, {
options: ({ groupId }) => ({
variables: { id: groupId },
}),
})(GroupPage);
Instead of an object, options can be a function, in which case it's passed the component's props as its first parameter. You can then use those props to define the variables your query will use. You can read more about using variables with Apollo client here and here.

GraphQL subscriptions: Error on calling apolloClient.subscribe

I think I have the backend subscription setup correctly. I am using angular on the client side, when I try to call subscribe I got an error
passwordUpdatedSubscription = gql`
subscription passwordUpdated{passwordUpdated{name password}}
`;
// Apollo Subscription
var subscription = this.apollo.subscribe({
query: this.passwordUpdatedSubscription
});
subscription.subscribe(
{
next(data) {
console.log(data);
},
error(err) { console.error('err', err); },
}
);
And then this is the error appears in the console
{"type":"subscription_fail","id":0,"payload":{"errors":[{"message":"Cannot read property 'subscribe' of undefined"}]}}
Maybe I am missing something on the backend? Do I need to define the setupFunctions in the SubscriptionManager?
This is my SubscriptionManager
const sub = require('graphql-subscriptions');
const pubSub = new sub.PubSub();
const manager = new sub.SubscriptionManager({
schema,
pubSub
});
This is my schema in graphQL
const graphql = require('graphql');
var schema = graphql.buildSchema(`
type Subscription {
passwordUpdated: User
}
type Mutation {
setMessage(message: String): String,
updateUserPassword(userName: String, password: String): User!
}
type Query {
getMessage: String,
getUsers: [User],
findUsers(userName: String): [User]
}
type User {
name: String,
password: String
}
`);
Yes you are missing the setup function. You could take a look at this links graphql subscription docu or example.
Your subscription manager could look like this:
const manager = new sub.SubscriptionManager({
schema,
pubSub,
setupFunctions: {
passwordUpdated: (options, args) => ({ // name of your graphQL subscription
passwordUpdatedChannel: { // name of your pubsub publish-tag
filter: () => {
return true
},
},
}),
},
});
When you call the pubsub publish function you have to write it like this pubsub.publish("passwordUpdatedChannel").
Sidenode: You might want to add the id of the user that has the password changed to the subscription. If you do that you can add it to the filter option, could look like this filter: (user) => {return user.id === args.userId}

Resources