How to write resolvers for GraphQL Mutation fields - graphql

My GraphQL schema looks like this:
type Outer {
id: ID
name: String,
inner: [Inner]
}
type Inner {
id: ID
name: String
}
input OuterInput {
name: String,
inner: InnerInput
}
Input InnerInput {
name: String
}
type Query {
getOuter(id: ID): Outer
}
type Mutation {
createOuter(outer: OuterInput): Outer
}
In database, Outer object is store this way:
{
id: 1,
name: ‘test’,
inner: [5, 6] //IDs of inner objects. Not the entire inner object
}
Outer and Inner are stored in separate database collections/tables (I’m using DynamoDB)
My resolvers look like this:
Query: {
getOuter: (id) => {
//return ‘Outer’ object with the ‘inner’ IDs from the database since its stored that way
}
}
Outer: {
inner: (outer) => {
//return ‘Inner’ objects with ids from ‘outer.inner’ is returned
}
}
Mutation: {
createOuter: (outer) => {
//extract out the IDs out of the ‘inner’ field in ‘outer’ and store in DB.
}
}
OuterInput: {
inner: (outer) => {
//Take the full ‘Inner’ objects from ‘outer’ and store in DB.
// This is the cause of the error
}
}
But I’m not able to write a resolver for the ‘inner’ field in ‘OuterInput’ similar to what I did for ‘Outer’. GraphQL throws an error saying
Error: OuterInput was defined in resolvers, but it's not an object
How can I handle such a scenario if writing resolvers for Input types are not allowed?

Resolvers are not necessary for input types. Input types are only passed in as values to field arguments, which means their value is already know -- it does not need to be resolved by the server, unlike values for fields being returned in the query.
The signature for every resolver is the same and includes 4 parameters: 1) the parent object, 2) the arguments for the field being resolved, 3) the context and 4) an object containing more detailed information about the request as a whole.
So to access the outer argument for your createOuter mutation:
Mutation: {
createOuter: (root, args, ctx, info) => {
console.log('Outer: ', args.outer)
console.log('Outer name: ', args.outer.name)
console.log('Inner: ', args.outer.inner)
console.log('Inner name: ', args.outer.inner.name)
// resolver logic here
}
}

Related

Field variableValues not working on graphql library execution on node

I'm playing with the graphql library (https://github.com/graphql/graphql-js) on node but I'm having some hard time on passing variable attributes...
const variableValues = {
routing, // String
statuses, // Array
date // Input type described in the query
}
return graphql({
schema: schema,
source: query,
rootValue: resolvers,
variableValues: variableValues
})
Unfortunately the variableValues are not passed to the resolver (if I log the context from the resolver, it show me that the variableValues is an empty object).
Any suggestions?
Variable values are not passed to your context. Variables are used to substitute values inside an operation. So instead of using literal values like this:
query GetUser {
getUser(id: 42) {
name
}
}
we can write
query GetUser($userId: ID!) {
getUser(id: $userId) {
name
}
}
In this particular example, userId would be exposed to the resolver for getUser as the id argument. The arguments for a field are provided as a the second parameter to the resolver function, separate from the context (which is the third parameter passed to the resolver).
const resolvers = {
Query: {
getUser: (root, args, ctx) => {
console.log(args.id) // prints the value of $userId
...
},
},
}
Note that variables may be used as arguments to directives as well, in which case they will not be passed to the resolver as part of the argument map at all.

Prisma Not Returning Created Related Records

i want to create a new graphql api and i have an issue that i am struggling to fix.
the code is open source and can be found at: https://github.com/glitr-io/glitr-api
i want to create a mutation to create a record with relations... it seems the record is created correctly with all the expected relations, (when checking directly into the database), but the value returned by the create<YourTableName> method, is missing all the relations.
... so so i get an error on the api because "Cannot return null for non-nullable field Meme.author.". i am unable to figure out what could be wrong in my code.
the resolver looks like the following:
...
const newMeme = await ctx.prisma.createMeme({
author: {
connect: { id: userId },
},
memeItems: {
create: memeItems.map(({
type,
meta,
value,
style,
tags = []
}) => ({
type,
meta,
value,
style,
tags: {
create: tags.map(({ name = '' }) => (
{
name
}
))
}
}))
},
tags: {
create: tags.map(({ name = '' }) => (
{
name
}
))
}
});
console.log('newMeme', newMeme);
...
that value of newMeme in the console.log here (which what is returned in this resolver) is:
newMeme {
id: 'ck351j0f9pqa90919f52fx67w',
createdAt: '2019-11-18T23:08:46.437Z',
updatedAt: '2019-11-18T23:08:46.437Z',
}
where those fields returned are the auto-generated fields. so i get an error for a following mutation because i tried to get the author:
mutation{
meme(
memeItems: [{
type: TEXT
meta: "test1-meta"
value: "test1-value"
style: "test1-style"
}, {
type: TEXT
meta: "test2-meta"
value: "test2-value"
style: "test2-style"
}]
) {
id,
author {
displayName
}
}
}
can anyone see what issue could be causing this?
(as previously mentioned... the record is created successfully with all relationships as expected when checking directly into the database).
As described in the prisma docs the promise of the Prisma client functions to write data, e.g for the createMeme function, only returns the scalar fields of the object:
When creating new records in the database, the create-method takes one input object which wraps all the scalar fields of the record to be
created. It also provides a way to create relational data for the
model, this can be supplied using nested object writes.
Each method call returns a Promise for an object that contains all the
scalar fields of the model that was just created.
See: https://www.prisma.io/docs/prisma-client/basic-data-access/writing-data-JAVASCRIPT-rsc6/#creating-records
To also return the relations of the object you need to read the object again using an info fragment or the fluent api, see: https://www.prisma.io/docs/prisma-client/basic-data-access/reading-data-JAVASCRIPT-rsc2/#relations

Skipping over a resolver for a query [duplicate]

I think I'm missing something obvious in the way GraphQL resolvers work. This is a simplified example of my schema (a Place that can have AdditionalInformation):
import { ApolloServer, gql } from 'apollo-server';
const typeDefs = gql`
type Place {
name: String!
additionalInformation: AdditionalInformation
}
type AdditionalInformation {
foo: String
}
type Query {
places: [Place]
}
`;
And the associated resolvers:
const resolvers = {
Query: {
places: () => {
return [{name: 'Barcelona'}];
}
},
AdditionalInformation: {
foo: () => 'bar'
}
};
const server = new ApolloServer({typeDefs, resolvers});
server.listen().then(({ url }) => {
console.log(`API server ready at ${url}`);
});
When I execute a basic query:
{
places {
name,
additionalInformation {
foo
}
}
}
I always get null as the additionalInformation:
{
"data": {
"places": [
{
"name": "Barcelona",
"additionalInformation": null
}
]
}
}
It's my first GraphQL app, and I still don't get why the AdditionalInformation resolver is not automatically executed. Is there some way to let GraphQL know it has to fire it?
I've found this workaround but I find it a bit tricky:
Place: {
additionalInformation: () => { return {}; }
}}
Let's assume for a moment that additionalInformation was a Scalar, and not an Object type:
type Place {
name: String!
additionalInformation: String
}
The value returned by the places resolver is:
[{name: 'Barcelona'}]
If you were to make a similar query...
query {
places {
name
additionalInformation
}
}
What would you expect additionalInformation to be? It's value will be null because there is no additionalInformation property on the Place object returned by the places resolver.
Even if we make additionalInformation an Object type (like AdditionalInformation), the result is the same -- the additionalInformation field will resolve to null. That's because the default resolver (the one used when you don't specify a resolver function for a field) simply looks for a property with the same name as the field on the parent object. If it fails to find that property, it returns null.
You may have specified a resolver for a field on AdditionalInformation (foo), but this resolver is never fired because there's no need -- the whole additionalInformation field is null so all of the resolvers for any fields of the associated type are skipped.
To understand why this is a desirable behavior, imagine a different schema:
type Article {
title: String!
content: String!
image: Image
}
type Image {
url: String!
copyright: String!
}
type Query {
articles: [Article!]!
}
We have a database with an articles table and an images table as our data layer. An article may or may not have an image associated with it. My resolvers might look like this:
const resolvers = {
Query: {
articles: () => db.getArticlesWithImages()
}
Image: {
copyright: (image) => `©${image.year} ${image.author}`
}
}
Let's say our call getArticlesWithImages resolves to a single article with no image:
[{ title: 'Foo', content: 'All about foos' }]
As a consumer of the API, I request:
query {
articles {
title
content
image
}
}
The image field is optional. If I get back an article object with a null image field, I understand there was no associated image in the db. As a front end client, I know not to render any image.
What would happen if GraphQL returned a value for the image regardless? Obviously, our resolver would break, since it would not be passed any kind of parent value. Moreover, however, as a consumer of the API, I would have to now parse the contents of image and somehow determine whether an image was in fact associated with the article and I should do something with it.
TLDR;
As you already suggested, the solution here is to specify a resolver for additionalInfo. You can also simply return that value in your places resolver, i.e.:
return [{name: 'Barcelona', additionalInfo: {}}]
In reality, if the shape of your schema aligns with the shape of your underlying data layer, it's unlikely you'll encounter this sort of issue when working with real data.

Enumerating all fields from a GraphQL query

Given a GraphQL schema and resolvers for Apollo Server, and a GraphQL query, is there a way to create a collection of all requested fields (in an Object or a Map) in the resolver function?
For a simple query, it's easy to recreate this collection from the info argument of the resolver.
Given a schema:
type User {
id: Int!
username: String!
roles: [Role!]!
}
type Role {
id: Int!
name: String!
description: String
}
schema {
query: Query
}
type Query {
getUser(id: Int!): User!
}
and a resolver:
Query: {
getUser: (root, args, context, info) => {
console.log(infoParser(info))
return db.Users.findOne({ id: args.id })
}
}
with a simple recursive infoParser function like this:
function infoParser (info) {
const fields = {}
info.fieldNodes.forEach(node => {
parseSelectionSet(node.selectionSet.selections, fields)
})
return fields
}
function parseSelectionSet (selections, fields) {
selections.forEach(selection => {
const name = selection.name.value
fields[name] = selection.selectionSet
? parseSelectionSet(selection.selectionSet.selections, {})
: true
})
return fields
}
The following query results in this log:
{
getUser(id: 1) {
id
username
roles {
name
}
}
}
=> { id: true, username: true, roles: { name: true } }
Things get pretty ugly pretty soon, for example when you use fragments in the query:
fragment UserInfo on User {
id
username
roles {
name
}
}
{
getUser(id: 1) {
...UserInfo
username
roles {
description
}
}
}
GraphQL engine correctly ignores duplicates, (deeply) merges etc. queried fields on execution, but it is not reflected in the info argument. When you add unions and inline fragments it just gets hairier.
Is there a way to construct a collection of all fields requested in a query, taking in account advanced querying capabilities of GraphQL?
Info about the info argument can be found on the Apollo docs site and in the graphql-js Github repo.
I know it has been a while but in case anyone ends up here, there is an npm package called graphql-list-fields by Jake Pusareti that does this. It handles fragments and skip and include directives.
you can also check the code here.

How to update apollo cache after mutation (query with filter)

I am pretty new to GraphQL. I am using graph.cool in a Vue.js project with apollo.
I am using right now the in-memory cache.
I had previously a simple 'allPosts' query.
And after creating a new one, I used the update() hook and readQuery() + writeQuery()
However I want that logged in users can only see their posts. So I modified the query with a filter.
query userStreams ($ownerId: ID!) {
allStreams(filter: {
owner: {
id: $ownerId
}
}) {
id
name
url
progress
duration
watched
owner {
id
}
}
}
My thought was, that I only need to pass in the userid variable. However this is not working. I am always getting
Error: Can't find field allStreams({"filter":{"owner":{}}}) on object (ROOT_QUERY) undefined.
this.$apollo.mutate({
mutation: CREATE_STREAM,
variables: {
name,
url,
ownerId
},
update: (store, { data: { createStream } }) => {
const data = store.readQuery({
query: USERSTREAMS,
variables: {
id: ownerId
}
})
data.allStreams.push(createStream)
store.writeQuery({
query: USER_STREAMS,
variables: {
id: ownerId
},
data
})
}
})
When you use readQuery or writeQuery, you should use the same variable name. So replace
variables: { id: ownerId }
With
variables: { ownerId }
Also, the reason you are getting an exception is that readQuery throws an exception if the data is not in the store. That happens before the first time you use writeQuery (or get the data with some other query).
You could write some default values to the store before calling this mutation.
You could also use readFragment that returns null instead of throwing an exception. But that would require more changes to your code.

Resources