I am trying to set a static cacheControl on some fields, as done here
From my understanding, I need to use a directive, so I used the following nest documentation to declare directives
So, I built a cacheControl directive, this is what my GraphQLModule.forRootAsync has in the buildSchemaOptions:
buildSchemaOptions: {
directives: [
new GraphQLDirective({
name: 'cacheControl',
locations: [
DirectiveLocation.FIELD_DEFINITION,
DirectiveLocation.OBJECT,
DirectiveLocation.INTERFACE,
DirectiveLocation.UNION
],
args: {
maxAge: { type: GraphQLInt },
scope: {
type: new GraphQLEnumType({
name: 'CacheControlScope',
values: {
PUBLIC: {
astNode: {
kind: 'EnumValueDefinition',
description: undefined,
name: {
kind: 'Name',
value: 'PUBLIC'
},
directives: []
}
},
PRIVATE: {
astNode: {
kind: 'EnumValueDefinition',
description: undefined,
name: {
kind: 'Name',
value: 'PRIVATE'
},
directives: []
}
}
}
})
},
inheritMaxAge: { type: GraphQLBoolean }
}
})
]
}
And it did create the directive in the schema:
directive #cacheControl(maxAge: Int, scope: CacheControlScope, inheritMaxAge: Boolean) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
enum CacheControlScope {
PUBLIC
PRIVATE
}
Now, I try to use it on my field declaration in my #ObjectType like so:
import { Directive, Field, Int, ObjectType } from '#nestjs/graphql'
#ObjectType('User')
export class User {
#Directive('#cacheControl(maxAge:60)')
#Field(() => Int)
id!: number
#Directive('#cacheControl(maxAge:60)')
#Field()
text!: string
}
But when doing some query to get all users, it does not seem to cache anything - and does not send a cache-control header and does the whole query each time.
I tried to do a transformer, but not sure how to implement caching for the resolvers.
What am I missing?
Related
i Have following response that needs to go under GraphQL Query:
{
'0xF7446f0...9a496aE94Cf9d42': { balances: [ [Object], [Object] ] },
'0xc01679F6496...95c86f9DEc63a': { balances: [ [Object], [Object] ] }
}
Using nestjs together with graphql-type-json and my code looks like this
#ObjectType()
export class BalancesResponse {
#Field({ nullable: true })
error?: string;
#Field((type) => JSON)
balances: any;
}
but when i try to execute the requests i got this error:
"message": "Cannot return null for non-nullable field BalancesResponse.balances."
Any idea how to return it, i want to return all of the key-value pairs in the object and my key to be dynamic
GraphQL does not support dictionary types. You can only query defined (i.e. known in advance) fields.
You could, however, refactor the response object to be an array:
[
{ key: '0xF7446f0...9a496aE94Cf9d42', balances: [ ... ] },
{ key: '0xc01679F6496...95c86f9DEc63a', balances: [ ... ] }
]
The corresponding type definition would be:
#ObjectType()
export class BalanceResponse {
#Field()
key: string;
#Field({ nullable: true })
error?: string;
#Field((type) => JSON)
balances: any;
}
...
#Query(() => [BalanceResponse])
async getBalance(): Promise<BalanceResponse[]>
Alternatively, you are free to lose the typing by declaring the whole response object as a GraphQL JSON. Then you can return your original response from the endpoint:
// not a GraphQL class anymore
export class BalancesResponse {
error?: string;
balances: Balance;
}
...
#Query(() => JSON)
async getBalance(): Promise<Record<string, BalancesResponse>>
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',
})
},
})
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.
I have a stitched graphql schema. Some type fields are resolved with info.mergeInfo.delegateToSchema
Here's an example (which is from the apollo docs):
const mergedSchema = mergeSchemas({
schemas: [
transformedChirpSchema,
authorSchema,
linkTypeDefs,
],
resolvers: {
User: {
chirps: {
fragment: `... on User { id }`,
resolve(user, args, context, info) {
return info.mergeInfo.delegateToSchema({
schema: chirpSchema,
operation: 'query',
fieldName: 'chirpsByAuthorId',
args: {
authorId: user.id,
},
context,
info,
});
},
},
},
});
Is it possible to access root in chirps resolver? So that in the root there were all the parent fields? Another way is, of course, to use context for this purpose, but using root, I guess, would be better from a code perspective as I'm already using root value in some cases.
Under the hood info.mergeInfo.delegateToSchema can call remote GraphQL application (more details).
So by design remote resolver don't have access to local root/context/info/arg, you need send all required data in arguments for remote field. For example:
const mergedSchema = mergeSchemas({
schemas: [
transformedChirpSchema,
authorSchema,
linkTypeDefs,
],
resolvers: {
User: {
chirps: {
fragment: `... on User { id }`,
resolve(user, args, context, info) {
return info.mergeInfo.delegateToSchema({
schema: chirpSchema,
operation: 'query',
fieldName: 'chirpsByAuthorId',
args: {
// author is InputType at remove schema with similar user structure
author: user,
},
context,
info,
});
},
},
},
});
I don't know your case, but don't forgot about schema-transforms during working with remove schemas.
I'm trying to get directives from a type.
Part of schema:
directive #translates(
type: String
) on OBJECT | FIELD_DEFINITION
type DocumentTranslated #translates(type: Document) {
...
}
Reading schema:
info.schema.getTypeMap()['DocumentTranslated'].astNode
Result:
{ kind: 'ObjectTypeDefinition',
description: undefined,
name:
{ kind: 'Name',
value: 'DocumentTranslated',
loc: { start: 8893, end: 8911 } },
interfaces: [],
directives: [],
fields:
I don't get, why directives are not present in AST? How can I read type directives?