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?
Related
I'm using AWS CDK version 2.64.0 to configure AWS AppSync to setup my GraphQL API.
I created my graphql.schema with the following configuration:
type message {
id_message: String!
id_collection: String!
id_user: String!
created_at: String!
text: String!
}
type Query {
getMessages(idCollectionIdUser: String!): [ message! ]
}
And then I used the configuration above to create my GraphQL API using these commands:
const api = new appsync.GraphqlApi(this, 'GraphQLDatabase', {
name: 'my-graphql-database',
schema: appsync.SchemaFile.fromAsset(path.join(__dirname, 'schema.graphql')),
xrayEnabled: true,
});
I also created one dynamodb table (with one global secondary index) using the following commands:
const messageTable = new dynamodb.Table(this, 'MessageTable', {
partitionKey: {
name: 'id_message',
type: dynamodb.AttributeType.STRING,
},
tableName: 'message.dynamodb-table',
removalPolicy: RemovalPolicy.DESTROY,
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
messageTable.addGlobalSecondaryIndex({
indexName: 'id_collection-id_user-created_at-index',
partitionKey: {
name: 'id_collection-id_user',
type: dynamodb.AttributeType.STRING,
},
sortKey: {
name: 'created_at',
type: dynamodb.AttributeType.NUMBER,
},
projectionType: ProjectionType.ALL,
});
Then I added the dynamodb table as datasource for the graphql database:
const messageDataSource = api.addDynamoDbDataSource('messageDataSource', messageTable);
I'm now trying to create a resolver to get all the messages created by the user in a collection with the following code:
messageDataSource.createResolver('QueryGetMessagesResolver', {
typeName: 'Query',
fieldName: 'getMessages',
requestMappingTemplate: appsync.MappingTemplate.dynamoDbQuery(
appsync.KeyCondition.eq('id_collection-id_user', 'idCollectionIdUser'),
'id_collection-id_user-created_at-index',
false,
),
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultList(),
});
When I test my graph api, I always get this error:
ExpressionAttributeNames contains invalid key: Syntax error; key: "#id_collection-id_user"
What am I doing wrong?
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?
I have a simplified schema.graphql file with one type and one custom directive (at the field and object level). In Typescript, how can I programmatically get the type and type directive, and iterate over the fields and field directives?
type Test #myCustomDirective() {
id: String! #myCustomDirective()
}
This post says "This is not currently supported by GraphQL":
Is there any way to read GraphQL Directives on a Type with Query Introspection?
And the GitHub issue says this feature is being considered:
https://github.com/graphql/graphql-spec/issues/300
Then how is AWS AppSync doing this? See below.
// The following keeps custom directives:
parse(schema); // Return type: graphql.DocumentNode
// The following removes custom directives:
buildSchema(schema); // Return type: GraphQLSchema
AWS AppSync > Export Schema > Schema.json does include custom directives but is an AWS AppSync specific solution and is an expensive API operation.
aws appsync get-introspection-schema --api-id abc123 --format JSON --include-directives output.json
I tried GraphQL Code Generator Introspection plugin. But it removes custom directives from types and fields.
https://www.graphql-code-generator.com/plugins/introspection
I tried graphql/utilities but it also removes custom directives from types and fields.
graphql.introspectionFromSchema(graphqlSchema)
// or
graphql.graphqlSync({ schema: graphqlSchema, source: graphql.getIntrospectionQuery() }).data as unknown as graphql.IntrospectionQuery;
https://graphql.org/graphql-js/utilities/
This "GraphQL Tools" approach creates an iteratable schema but may not be the most efficient approach:
"The graphql-tools package allows you to create a GraphQL.js GraphQLSchema instance from GraphQL schema language using the function makeExecutableSchema"
https://www.graphql-tools.com/docs/generate-schema
import { promises as fs } from 'fs';
import { makeExecutableSchema } from '#graphql-tools/schema';
import gql from 'graphql-tag';
const schemaFile = await fs.readFile(
'./schema.graphql'
);
const executableSchema = makeExecutableSchema({
typeDefs: gql(schemaFile.toString())
});``
const output = executableSchema.getType('Test');
// output.astNode.directives sample output:
{
kind: 'Directive',
name: { kind: 'Name', value: 'myCustomDirective1' },
arguments: [ [Object] ]
},
{
kind: 'Directive',
name: { kind: 'Name', value: 'myCustomDirective' },
arguments: [ [Object] ]
}
// output.astNode['fields'] sample output:
[{
kind: 'FieldDefinition',
description: undefined,
name: { kind: 'Name', value: 'id' },
arguments: [],
type: { kind: 'NamedType', name: [Object] },
directives: [ [Object] ]
}]
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!
...
}
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"))
};