How to defined non-null elements inside an array in GraphQL Nexus? - graphql

I'm using GraphQL Nexus to implement my GraphQL schema.
The target GraphQL type I want to create is this:
input UserCreateInput {
email: String!
name: String
posts: [PostCreateInput!]!
}
However, I'm not sure how I can create the PostCreateInput array such that the elements of the posts are are required as well.
Right now this is what I have:
input UserCreateInput {
email: String!
name: String
posts: [PostCreateInput]!
}
Which is backed by this Nexus type definition:
const UserCreateInput = inputObjectType({
name: 'UserCreateInput',
definition(t) {
t.nonNull.string('email')
t.string('name')
t.nonNull.list.field('posts', {
type: 'PostCreateInput',
})
},
})
Is there a way how I can tell Nexus that each array element should be non-null?

In this case, adding a nonNull after the list should suffice. So something like the following:
const UserCreateInput = inputObjectType({
name: 'UserCreateInput',
definition(t) {
t.nonNull.string('email')
t.string('name')
t.nonNull.list.nonNull.field('posts', {
type: 'PostCreateInput',
})
},
})

Related

Custom schema, interface, #fileByRelativePath and gatsby-image

I'm trying to get an interface working with the new #fileByRelativePath resolver extension, to keep compatible with v3.
I'm using Prismic for my content, and gatsby-source-prismic v2. I have two content types in Prismic, and created the interface to be able to more easily query and map over both for a home page index.
Here's the functioning (but with deprecated inferred resolvers) schema:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
interface indexPosts #nodeInterface {
id: ID!
uid: String!
data: Data!
type: String!
}
type Data {
title: Title!
date: Date!
featured: String!
featured_image: Featured_image!
body: Body!
}
type Title {
text: String!
}
type Featured_image {
localFile: File!
}
type Body {
html: String!
}
type PrismicGallery implements Node & indexPosts {
uid: String!
data: Data!
type: String!
}
type PrismicEssay implements Node & indexPosts {
uid: String!
data: Data!
type: String!
}
`
createTypes(typeDefs)
}
The problem comes after adding #fileByRelativePath to the Featured_image type definition. Doing so gives me an error during build:
"The "path" argument must be of type string. Received type undefined"
I'm unsure how to provide the necessary path argument, considering my images are third-party hosted. I'm trying to follow the brief guide at the end of this page and suspect the way to do it might be with a resolver or type builder and using 'source' to access the url field provided by both localFile and its parent, featured_image, but I can't figure it out!
I'm using gatsby-image and the childImageSharp convenience field to present the images, if that makes a difference at all!
I had exactly the same problem when I tried to use #fileByRelativePath. I managed to solve my problem by using #infer on the type that contained the File.
Try this:
type Featured_image #infer {
localFile: File!
}

How to add multiple resolvers in a type (Apollo-server)

I have used express-graphql and there i used to do something like this.
const SubCategoryType = new ObjectType({
name: 'SubCategory',
fields: () => ({
id: { type: IDType },
name: { type: StringType },
category: {
type: CategoryType,
resolve: parentValue => getCategoryBySubCategory(parentValue.id)
},
products: {
type: List(ProductType),
resolve: parentValue => getProductsBySubCategory(parentValue.id)
}
})
});
Here I have multiple resolvers, id and name are fetched directly from the result. and the category and products have there own database operation. and so on.
Now I am working on apollo-server and I can't find a way to replicate this.
for example I have a type
type Test {
something: String
yo: String
comment: Comment
}
type Comment {
text: String
createdAt: String
author: User
}
and in my resolver I want to split it up, for example something like this
text: {
something: 'value',
yo: 'value',
comment: getComments();
}
NOTE: this is just a representation of what I need.
You can add type-specific resolvers to handle specific fields. Let's say you have the following schema (based on your example):
type Query {
getTest: Test
}
type Test {
id: Int!
something: String
yo: String
comment: Comment
}
type Comment {
id: Int!
text: String
createdAt: String
author: User
}
type User {
id: Int!
name: String
email: String
}
Let's also assume you have the following DB methods:
getTest() returns an object with fields something, yo and
commentId
getComment(id) returns an object with fields id, text, createdAt and userId
getUser(id) returns an object with fields id, name and email
Your resolver will be something like the following:
const resolver = {
// root Query resolver
Query: {
getTest: (root, args, ctx, info) => getTest()
},
// Test resolver
Test: {
// resolves field 'comment' on Test
// the 'parent' arg contains the result from the parent resolver (here, getTest on root)
comment: (parent, args, ctx, info) => getComment(parent.commentId)
},
// Comment resolver
Comment: {
// resolves field 'author' on Comment
// the 'parent' arg contains the result from the parent resolver (here, comment on Test)
author: (parent, args, ctx, info) => getUser(parent.userId)
},
}
Hope this helps.

Get list of types from operation variables

I have an operation and I want to get a list containing its variables and types.
Eg. given the operation
query getResource($id: ID!, $title: String) {
resource(id: $id, title: $title) {
id
title
content
}
}
I want to get something like this
[{type: "ID", required: true, name: "id"}, {type: "String", required: false, name: "title"}]
Is this easily attainable?
Edit: I guess what I want to do is to go from a VariableDefinitionNode to an object containing all the information I want, such as type, defaultValue, required, name.
You should be able to implement this if you change your schema (assuming you can generate this data on the server into this shape)
type Query {
resource ($id: ID!, $title: String): [Variable]
}
type Variable {
type: String!
required: Boolean!
name: String!
...
}
This is a very generalised syntax however, returning an array of values like that. If the structure of your resources is known, you would be better off defining that and removing the name parameter as it becomes redundant. (I'm thinking about lookups inside your client-side application)
type Query {
resource ($id: ID!, $title: String): Resource
}
type Resource {
id: Variable!
title: Variable
content: Variable
}
type Variable {
type: String!
required: Boolean!
...
}

Can you make a graphql type both an input and output type?

I have some object types that I'd like to use as both input and output - for instance a currency type or a reservation type.
How do I define my schema to have a type that supports both input and output - I don't want to duplicate code if I don't have to. I'd also prefer not to create duplicate input types of things like currency and status enums.
export const ReservationInputType = new InputObjectType({
name: 'Reservation',
fields: {
hotelId: { type: IntType },
rooms: { type: new List(RoomType) },
totalCost: { type: new NonNull(CurrencyType) },
status: { type: new NonNull(ReservationStatusType) },
},
});
export const ReservationType = new ObjectType({
name: 'Reservation',
fields: {
hotelId: { type: IntType },
rooms: { type: new List(RoomType) },
totalCost: { type: new NonNull(CurrencyType) },
status: { type: new NonNull(ReservationStatusType) },
},
});
In the GraphQL spec, objects and input objects are distinct things. Quoting the spec for input objects:
Fields can define arguments that the client passes up with the query, to configure their behavior. These inputs can be Strings or Enums, but they sometimes need to be more complex than this.
The Object type... is inappropriate for re‐use here, because Objects can contain fields that express circular references or references to interfaces and unions, neither of which is appropriate for use as an input argument. For this reason, input objects have a separate type in the system.
An Input Object defines a set of input fields; the input fields are either scalars, enums, or other input objects. This allows arguments to accept arbitrarily complex structs.
While an implementation might provide convenience code to create an object and a corresponding input object from a single definition, under the covers, the spec indicates that they'll have to be separate things (with separate names, such as Reservation and ReservationInput).
While working on a project I had a similar problem with code duplication between input and type objects. I did not find the extend keyword very helpful as it only extended the fields of that specific type. So the fields in type objects cannot not be inherited in input objects.
In the end I found this pattern using literal expressions helpful:
const UserType = `
name: String!,
surname: String!
`;
const schema = graphql.buildSchema(`
type User {
${UserType}
}
input InputUser {
${UserType}
}
`)
You can do something like this:
export const createTypes = ({name, fields}) => {
return {
inputType: new InputObjectType({name: `${name}InputType`, fields}),
objectType: new ObjectType({name: `${name}ObjectType`, fields})
};
};
const reservation = createTypes({
name: "Reservation",
fields: () => ({
hotelId: { type: IntType },
rooms: { type: new List(RoomType) },
totalCost: { type: new NonNull(CurrencyType) },
status: { type: new NonNull(ReservationStatusType) }
})
});
// now you can use:
// reservation.inputType
// reservation.objectType
this is something that i did for my project (works good):
const RelativeTemplate = name => {
return {
name: name,
fields: () => ({
name: { type: GraphQLString },
reference: { type: GraphQLString }
})
};
};
const RelativeType = {
input: new GraphQLInputObjectType(RelativeTemplate("RelativeInput")),
output: new GraphQLObjectType(RelativeTemplate("RelativeOutput"))
};

Real world example of GraphQLInterfaceType and GraphQLUnionType

I'm having a hard time understanding when to use GraphQLInterfaceType and GraphQLUnionType.
I've RTFMs:
http://graphql.org/docs/api-reference-type-system/#graphqluniontype
https://github.com/mugli/learning-graphql/blob/master/7.%20Deep%20Dive%20into%20GraphQL%20Type%20System.md
Can anyone offer up a real world example when these would be useful to get it through my thick head?
Both are meant to help you design a schema with a heterogeneous set of types, and you could achieve the same functionality using both, but GraphQLInterfaceType is more suitable when the types are basically the same but some of the fields are different, and GraphQLUnionType when the types are totally different and have totally different fields.
Ultimately whether to use one or the other depending on your schema design.
For a real world example, let's say you have a list of blogs, but blogs using framework A use username and password as authentication, and blog using framework B use email and password. We design it with a GraphQLInterfaceType like this:
const BlogType = new GraphQLInterfaceType({
name: 'Blog',
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
password: { type: new GraphQLNonNull(GraphQLString) }
},
resolveType: resolveBlogType
});
const BlogAType = new GraphQLObjectType({
name: 'BlogA',
interfaces: [Blog],
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
username: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const BlogBType = new GraphQLObjectType({
name: 'BlogB',
interfaces: [Blog],
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
function resolveBlogType(value) {
return value.username ? BlogAType : BlogBType;
}
When we create a new blog sending username, it will create a BlogA.
We can query like this:
query MyQuery {
blogs: {
url
password
... on BlogA {
email
}
... on BlogB {
username
}
}
}
Now let's get the same functionality but using GraphQLUnionType, because we prefer to use simply one type of blog, and 2 types of authentication methods:
const AuthAType = new GraphQLObjectType({
name: 'AuthA',
fields: {
username: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const AuthBType = new GraphQLObjectType({
name: 'AuthB',
fields: {
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const AuthType = new GraphQLUnionType({
name: 'Auth',
types: [AuthAType, AuthBType]
resolveType: resolveAuthType
});
const BlogType = new GraphQLInterfaceType({
name: 'Blog',
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
auth: { type: AuthType }
},
});
function resolveAuthType(value) {
return value.username ? AuthAType : AuthBType;
}
We can query like this:
query MyQuery {
blogs: {
url
auth {
... on AuthA {
username
password
}
... on AuthB {
email
password
}
}
}
}
As you can see in this example we achieve the same thing with the interface or the union, but one or the other might be more appropriate depending on your schema design.
For example, let's say you want to add a blog framework C that also use email and password. You would need to include another field to be able to differentiate it from blog framework B in our resolveBlogType function. Let's add the type field. In our Union example, since we only have access to the fields within the Union, you would to add type to the Union. If in the future we wanted to add another Union with same fields for multiple frameworks, we would need to add the type field there as well. Not so nice to have type duplicated multiple times in our schema. It could be a better idea to use a Interface, and have a single type field accessible at the resolveBlogType function by all the Objects using the Interface.
The sematic of GraphQLInterfaceType is like most program language's interface . and graphql add some more behaviors for it. like check if the derived class implement all the fields,dynamic resolving to derived instance.
The sematic of GraphQLUnionType is not a Union ,but something like OR.(a little bit like the flowtype's type check?)
A real world example is example in Relay => Relay's Node design .
GraphQLInterfaceType is completely unrelated to GraphQLUnionType.
I think maybe you was confused by this?
interface Node{
id: string
}
type Dog implements Node{
id: string
}
type Cat implements Node{
id: string
}
union Animal = Dog | Cat
type User{
node: Node
animal: Animal
}
If be confused by this, you should get some book of strong type language to read.(like C# or Java or something else. maybe you should have a look at Flow too, this usage Dog|Cat is a type restriction)

Resources