How to return nested objects in GraphQL schema language - graphql

I was going through the documentation for GraphQl and realized that the new Schema Langugage supports only default resolvers. Is there a way I can add custom resolvers while using the new Schema Language?
let userObj = {
id: 1,
name: "A",
homeAddress: {
line1: "Line1",
line2: "Line2",
city: "City"
}
};
let schema = buildSchema(`
type Query {
user(id: ID): User
}
type User {
id: ID
name: String
address: String
}
`);
//I would like User.address to be resolved from the fields in the json response eg. address = Line1, Line2, City
This is the schema that I have defined. I would like to add some behavior here that would allow me to parse the address object and return a concatenated string value.

As mentioned by HagaiCo and in this github issue, the right way would be to go with graphql-tools.
It has a function called makeExecutableSchema, which takes a schema and resolve functions, and then returns an executable schema

It seems like you have a confusion in here, since you defined that address is String but you send a dictionary to resolve it.
what you can do, is to define a scalar address type:
scalar AddressType if you use buildSchema and then attach parse functions to it. (or use graphql-tools to do it easily)
or build the type from scratch like shown in the official documentations:
var OddType = new GraphQLScalarType({
name: 'Odd',
serialize: oddValue,
parseValue: oddValue,
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return oddValue(parseInt(ast.value, 10));
}
return null;
}
});
function oddValue(value) {
return value % 2 === 1 ? value : null;
}
and then you can parse the dictionary into a string (parseValue) and otherwise

Related

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.

How to create generics with the schema language?

Using facebook's reference library, I found a way to hack generic types like this:
type PagedResource<Query, Item> = (pagedQuery: PagedQuery<Query>) => PagedResponse<Item>
​
interface PagedQuery<Query> {
query: Query;
take: number;
skip: number;
}
​
interface PagedResponse<Item> {
items: Array<Item>;
total: number;
}
function pagedResource({type, resolve, args}) {
return {
type: pagedType(type),
args: Object.assign(args, {
page: { type: new GraphQLNonNull(pageQueryType()) }
}),
resolve
};
function pageQueryType() {
return new GraphQLInputObjectType({
name: 'PageQuery',
fields: {
skip: { type: new GraphQLNonNull(GraphQLInt) },
take: { type: new GraphQLNonNull(GraphQLInt) }
}
});
}
function pagedType(type) {
return new GraphQLObjectType({
name: 'Paged' + type.toString(),
fields: {
items: { type: new GraphQLNonNull(new GraphQLList(type)) },
total: { type: new GraphQLNonNull(GraphQLInt) }
}
});
}
}
But I like how with Apollo Server I can declaratively create the schema. So question is, how do you guys go about creating generic-like types with the schema language?
You can create an interface or union to achieve a similar result. I think this article does a good job explaining how to implement interfaces and unions correctly. Your schema would look something like this:
type Query {
pagedQuery(page: PageInput!): PagedResult
}
input PageInput {
skip: Int!
take: Int!
}
type PagedResult {
items: [Pageable!]!
total: Int
}
# Regular type definitions for Bar, Foo, Baz types...
union Pageable = Bar | Foo | Baz
You also need to define a resolveType method for the union. With graphql-tools, this is done through the resolvers:
const resolvers = {
Query: { ... },
Pageable {
__resolveType: (obj) => {
// resolve logic here, needs to return a string specifying type
// i.e. if (obj.__typename == 'Foo') return 'Foo'
}
}
}
__resolveType takes the business object being resolved as its first argument (typically your raw DB result that you give GraphQL to resolve). You need to apply some logic here to figure out of all the different Pageable types, which one we're handling. With most ORMs, you can just add some kind of typename field to the model instance you're working with and just have resolveType return that.
Edit: As you pointed out, the downside to this approach is that the returned type in items is no longer transparent to the client -- the client would have to know what type is being returned and specify the fields for items within an inline fragment like ... on Foo. Of course, your clients will still have to have some idea about what type is being returned, otherwise they won't know what fields to request.
I imagine creating generics the way you want is impossible when generating a schema declaratively. To get your schema to work the same way it currently does, you would have to bite the bullet and define PagedFoo when you define Foo, define PagedBar when you define Bar and so on.
The only other alternative I can think of is to combine the two approaches. Create your "base" schema programatically. You would only need to define the paginated queries under the Root Query using your pagedResource function. You can then use printSchema from graphql/utilities to convert it to a String that can be concatenated with the rest of your type definitions. Within your type definitions, you can use the extend keyword to build on any of the types already declared in the base schema, like this:
extend Query {
nonPaginatedQuery: Result
}
If you go this route, you can skip passing a resolve function to pagedResource, or defining any resolvers on your programatically-defined types, and just utilize the resolvers object you normally pass to buildExecutableSchema.

In GraphQL schema, how can a field be GraphQLString as well as GraphQLInt?

Here's my code from the express application for GraphQL schema:-
let data = new GraphQLObjectType({
name:"Data",
fields: {
id: {type: GraphQLID},
value: {type: GraphQLString} // TODO: allow for string as well as int
}
});
How can I make 'value' field accept a string value as well as int value so that it can be stored using the correct type?
According to your comment saying that you use NoSQL database which can place both strings and ints in the same field, you should go with GraphQLString. When creating new instance of above object, you can create a resolve method for value field, which would check if passed value is string or int (of course it would always be string because of GraphQLString type, however it can be a string like "123" which can be parsed to int) - according to this you can perform some parsing before saving in the database.
On the other hand, when you will retrieve the data from database, it will always occur as a string in the graphql representation - if this is not a case I think that this could be a simple solution.
However, if you are not satisfied with this proposition, I am afraid that you can't trick GraphQL as you want to. Every field can obtain only single type definition.
EDIT:
This solution is not valid for the question. It works only for object types and not scalars
You should look at GraphQLUnionType: http://graphql.org/graphql-js/type/#graphqluniontype
I'm not used to define types like this, but I expect it to be something like the below:
var ValueType = new GraphQLUnionType({
name: 'Value',
types: [ GraphQLString, GraphQLInt ],
resolveType(value) {
if (value instanceof string) {
return GraphQLString;
}
if (value instanceof number) {
return GraphQLInt;
}
}
});
let data = new GraphQLObjectType({
name:"Data",
fields: {
id: {type: GraphQLID},
value: {type: ValueType}
}
});

Object mutations in graphQL

Here is my EventAttendee Object.
const EventAttendee = new GraphQLInputObjectType({
name: 'EventAttendee',
fields: () => ({
attendeeName: {type: GraphQLString},
personalizedDateSelection: {type: new GraphQLInputObjectType()}
})
});
The personalizedDateSelection property is an dynamic one and its properties are not known now. So, In this case, I have given GraphQLInputObjectType().
But it gives an error stating EventAttendee.personalizedDateSelection field type must be Output Type.
How to define an ObjectType whose properties are not known ?
I believe you want to define an InputObjectType and not an ObjectType as stated.
With that said, what you can do is to declare personalizedDateSelection as a string for now, and then later adapt it as needed with a custom input object, once you know what it should contain. GraphQLInputObjectType is used to define new input object types, it isn't a type per se.
I wanted personalizedDateSelection property of EventAttendee to be of objectType but I dont know those properties in advance, but I am sure that it is of Object type.
So declaring this as GraphQLScalarType was the correct way to do it. Check out about GraphQLScalarType. But we need to create a custom scalar type. All scalar types can be of input type. So here is my implementation:
const PersonalizedDateSelection = new GraphQLScalarType({
name: 'PersonalizedDateSelection',
serialize: value => {
return value;
},
parseValue: value => {
return value;
},
parseLiteral: ast => {
console.log("coming in parseLiteral");
console.log(ast);
let value = {};
if (ast.kind !== Kind.OBJECT) {
throw new GraphQLError("Query error: Can only parse object but got a: " + ast.kind, [ast]);
}
ast.fields.forEach(field => {
value[field.name.value] = parseJSONLiteral(field.value);
});
return value;
}
});

Resources