Designing your
schema
The GraphQL schema is the foundation of any GraphQL server implementation.
Each GraphQL API is defined by a
single GraphQL schema. The GraphQL type
system describes the capabilities of a GraphQL server and is used to determine if a query is
valid. A server’s type system is referred to as that server’s schema. It is made up of a set of object
types, scalars, input types, interfaces, enums, and unions. It defines the shape of the data that flows
through your API and also the operations that can be performed. GraphQL is a strongly typed protocol and all
data operations are validated against this schema.
AWS AppSync allows you to define and configure GraphQL schemas. The following section describes how to create
GraphQL schemas from scratch using AWS AppSync's services.
Creating an empty schema
Schema files are text files, usually named schema.graphql
. AWS AppSync allows users to
create new schemas for their GraphQL APIs using the console, CLI, or
APIs. In this example, we'll be
creating a blank API along with a blank schema.
- Console
-
-
Sign in to the AWS Management Console and open the AppSync
console.
-
In the APIs dashboard, choose your GraphQL
API.
-
In the Sidebar,
choose Schema.
-
You can configure your schema.graphql
file and submit it to AWS AppSync
by adding the following into the Schema window:
schema {
}
-
Choose Save schema.
Every schema begins with the schema
root for processing. This fails
to process until you add a root query type.
- API
-
- CLI
-
-
If you haven't already done so,
install the AWS CLI, then add your
configuration.
-
Create a GraphQL API object by running the create-graphql-api
command.
You'll need to type in two parameters for this particular command:
-
The name
of your API.
-
The authentication-type
, or the type of credentials used to
access the API (IAM, OIDC, etc.).
There are other parameters such as Region
that must be configured but
will usually default to your CLI configuration values.
An example command may look like this:
aws appsync create-graphql-api --name testAPI123 --authentication-type API_KEY
An output will be returned in the CLI. Here's an example:
{
"graphqlApi": {
"xrayEnabled": false,
"name": "testAPI123",
"authenticationType": "API_KEY",
"tags": {},
"apiId": "abcdefghijklmnopqrstuvwxyz",
"uris": {
"GRAPHQL": "https://zyxwvutsrqponmlkjihgfedcba.appsync-api.us-west-2.amazonaws.com/graphql",
"REALTIME": "wss://zyxwvutsrqponmlkjihgfedcba.appsync-realtime-api.us-west-2.amazonaws.com/graphql"
},
"arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz"
}
}
-
Run the start-schema-creation
command.
You'll need to type in two parameters for this particular command:
-
Your api-id
from the previous step.
-
The schema definition
, which is a base-64 encoded binary
blob.
An example command may look like this:
aws appsync start-schema-creation --api-id abcdefghijklmnopqrstuvwxyz --definition "aa1111aa-123b-2bb2-c321-12hgg76cc33v"
An output will be returned:
{
"status": "PROCESSING"
}
This command will not return the final output after processing. You must use a
separate command, get-schema-creation-status
, to see the result. Note that
these two commands are asynchronous, so you can check the output status even while the
schema is still being created.
Adding a root query type
Each GraphQL schema must have a root query type. You can think of these as the entrypoints (or endpoints)
of your GraphQL server.
For this guide, we will create an example Todo
application. We will add a root type named
Query
with a single getTodos
field that returns a list containing
Todo
objects.
- Console
-
-
Sign in to the AWS Management Console and open the AppSync
console.
-
In the APIs dashboard, choose your GraphQL
API.
-
In the Sidebar, choose Schema.
-
Add the following to your schema.graphql
file:
schema {
query:Query
}
type Query {
getTodos: [Todo]
}
- API
-
-
Update the root schema by calling the UpdateType
API.
-
Create your Query
type by calling the CreateType
API.
- CLI
-
-
Update your root schema by running the update-type
command.
You'll need to enter in a few parameters for this particular command:
-
The api-id
of your API.
-
The type-name
of your type. In the console example, this was
Schema
.
-
The definition
, or the content of your type. In the console
example, this was
schema {
query:Query
}
-
The format
of your input. In this example, we're using
SDL
.
An example command may look like this:
aws appsync update-type --api-id abcdefghijklmnopqrstuvwxyz --type-name schema --definition "schema {query:Query}" --format SDL
An output will be returned in the CLI. Here's an example:
{
"type": {
"definition": "schema {query:Query}",
"name": "schema",
"arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/schema",
"format": "SDL"
}
}
-
Create a Query
type by running the create-type
command.
You'll need to enter in a few parameters for this particular command:
-
The api-id
of your API.
-
The definition
, or the content of your type. In the console
example, this was
type Query {
getTodos: [Todo]
}
-
The format
of your input. In this example, we're using
SDL
.
An example command may look like this:
aws appsync create-type --api-id abcdefghijklmnopqrstuvwxyz --definition "type Query {getTodos: [Todos]}" --format SDL
An output will be returned in the CLI. Here's an example:
{
"type": {
"definition": "Query {getTodos: [Todos]}",
"name": "Query",
"arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Query",
"format": "SDL"
}
}
Notice that we haven’t yet defined the Todo
object type. Let’s do that now.
Defining a todo
type
In the previous section, we declared the Todo
object type without defining its
behaviors.
- Console
-
-
Sign in to the AWS Management Console and open the AppSync
console.
-
In the APIs dashboard, choose your GraphQL
API.
-
In the Sidebar, choose Schema.
-
Now, create a type that contains the data for a Todo
object:
schema {
query:Query
}
type Query {
getTodos: [Todo]
}
type Todo {
id: ID!
name: String
description: String
priority: Int
}
- API
-
- CLI
-
-
Create a Todo
type by running the create-type
command.
You'll need to enter in a few parameters for this particular command:
-
The api-id
of your API.
-
The definition
, or the content of your type. In the console
example, this was
type Todo {
id: ID!
name: String
description: String
priority: Int
}
-
The format
of your input. In this example, we're using
SDL
.
An example command may look like this:
aws appsync create-type --api-id abcdefghijklmnopqrstuvwxyz --definition "type Todo{id:ID! name:String description:String priority:Int}" --format SDL
An output will be returned in the CLI. Here's an example:
{
"type": {
"definition": "type Todo{id:ID! name:String description:String priority:Int}",
"name": "Todo",
"arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Todo",
"format": "SDL"
}
}
Notice that the Todo
object type has fields that
are scalar types, such as strings and
integers. AWS AppSync also has enhanced Scalar types in AWS AppSync
in addition to the base GraphQL scalars that you can use in a schema. Any field that ends in an exclamation
point is a required field. The ID
scalar type is a unique identifier that can be either
String
or Int
. You can control these in your resolver
code for automatic
assignment, which is covered later.
There are similarities between the Query
and Todo
types. In GraphQL, the root
types (that is, Query
, Mutation
, and Subscription
) are similar to the
ones you define, but they’re different in that you expose them from your schema as the entry point for your
API. For more information, see The
query
and
mutation
types.
Adding a
mutation
type
Now that you have an object type and can query the data, if you want to add, update, or delete data via
the API you need to add a mutation type to your schema. For the Todo
example, add this as a
field named addTodo
on a mutation type:
- Console
-
-
Sign in to the AWS Management Console and open the AppSync
console.
-
In the APIs dashboard, choose your GraphQL
API.
-
In the Sidebar, choose Schema.
-
Add the mutation to your schema and define its type:
schema {
query:Query
mutation: Mutation
}
type Query {
getTodos: [Todo]
}
type Mutation {
addTodo(id: ID!, name: String, description: String, priority: Int): Todo
}
type Todo {
id: ID!
name: String
description: String
priority: Int
}
- API
-
- CLI
-
-
Update your root schema by running the update-type
command.
You'll need to enter in a few parameters for this particular command:
-
The api-id
of your API.
-
The type-name
of your type. In the console example, this was
Schema
.
-
The definition
, or the content of your type. In the console
example, this was
schema {
query:Query
mutation: Mutation
}
-
The format
of your input. In this example, we're using
SDL
.
An example command may look like this:
aws appsync update-type --api-id abcdefghijklmnopqrstuvwxyz --type-name schema --definition "schema {query:Query mutation: Mutation}" --format SDL
An output will be returned in the CLI. Here's an example:
{
"type": {
"definition": "schema {query:Query mutation: Mutation}",
"name": "schema",
"arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/schema",
"format": "SDL"
}
}
-
Create a Mutation
type by running the create-type
command.
You'll need to enter in a few parameters for this particular command:
-
The api-id
of your API.
-
The definition
, or the content of your type. In the console
example, this was
type Mutation {
addTodo(id: ID!, name: String, description: String, priority: Int): Todo
}
-
The format
of your input. In this example, we're using
SDL
.
An example command may look like this:
aws appsync create-type --api-id abcdefghijklmnopqrstuvwxyz --definition "type Mutation {addTodo(id: ID! name: String description: String priority: Int): Todo}" --format SDL
An output will be returned in the CLI. Here's an example:
{
"type": {
"definition": "type Mutation {addTodo(id: ID! name: String description: String priority: Int): Todo}",
"name": "Mutation",
"arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Mutation",
"format": "SDL"
}
}
Note that the mutation is also added to this schema type because it is a root type.
Modifying the
todo
example
with a
status
At this point, your GraphQL API is functioning structurally for reading and writing
Todo
objects–it just doesn’t have a data source, which is described in the next section. You can modify this API
with more advanced functionality, such as adding a status to your
Todo
,
which comes from a set of values defined as an
ENUM
. For example, see
the snippet
below:
schema {
query:Query
mutation: Mutation
}
type Query {
getTodos: [Todo]
}
type Mutation {
addTodo(id: ID!, name: String, description: String, priority: Int, status: TodoStatus): Todo
}
type Todo {
id: ID!
name: String
description: String
priority: Int
status: TodoStatus
}
enum TodoStatus {
done
pending
}
An ENUM
is like a string, but it can take one of a set of values. In the previous example,
you added this type, modified the Todo type, and added the Todo field to contain this functionality.
You can skip directly to Attaching a data source and begin connecting your schema to a data source now, or keep
reading to modify your schema with paginations and relational structure.
Subscriptions
Subscriptions in AWS AppSync are invoked as a response to a mutation. You configure this with a
Subscription
type and @aws_subscribe()
directive in the schema to denote which
mutations invoke one or more subscriptions. For more information about configuring subscriptions, see Real-time
data.
Relations and pagination
(advanced)
Suppose you had a million todos
. You wouldn’t want to fetch all of these every time, instead
you would want to paginate through them. Make the following changes to your schema:
-
To the getTodos
field, add two input arguments: limit
and
nextToken
.
-
Add a new TodoConnection
type that has todos
and nextToken
fields.
-
Change getTodos
so that it returns TodoConnection
(not a list of
Todos).
schema {
query:Query
mutation: Mutation
}
type Query {
getTodos(limit: Int, nextToken: String): TodoConnection
}
type Mutation {
addTodo(id: ID!, name: String, description: String, priority: Int, status: TodoStatus): Todo
}
type Todo {
id: ID!
name: String
description: String
priority: Int
status: TodoStatus
}
type TodoConnection {
todos: [Todo]
nextToken: String
}
enum TodoStatus {
done
pending
}
The TodoConnection
type allows you to return a list of todos
and a
nextToken
for getting the next batch of todos
. Note that it is a single
TodoConnection
and not a list of connections. Inside the connection is a list of todo items
([Todo]
) which gets returned with a pagination token. In AWS AppSync, this is connected
to Amazon DynamoDB
through a resolver
and automatically generated as an encrypted token. This converts the value of the limit
argument to the maxResults
parameter and the nextToken
argument to the
exclusiveStartKey
parameter. For examples and the built-in template samples in the
AWS AppSync console, see Resolver reference
(JavaScript) for JavaScript or Resolver
mapping
template
reference
(VTL) for
VTL.
Next, suppose your todos have comments, and you want to run a query that returns all the comments for a
todo
. This is handled through GraphQL connections
. Modify your schema to have
a Comment
type, add a comments
field to the Todo
type, and add an
addComment
field on the Mutation
type as follows:
schema {
query: Query
mutation: Mutation
}
type Query {
getTodos(limit: Int, nextToken: String): TodoConnection
}
type Mutation {
addTodo(id: ID!, name: String, description: String, priority: Int, status: TodoStatus): Todo
addComment(todoid: ID!, content: String): Comment
}
type Comment {
todoid: ID!
commentid: String!
content: String
}
type Todo {
id: ID!
name: String
description: String
priority: Int
status: TodoStatus
comments: [Comment]
}
type TodoConnection {
todos: [Todo]
nextToken: String
}
enum TodoStatus {
done
pending
}
Note that the Comment
type has the todoid
that it’s associated with,
commentid
, and content
. This corresponds to a primary key
and sort key
combination in the Amazon DynamoDB table you create later.
The application graph on top of your existing data sources in AWS AppSync enables you to return data
from two separate data sources in a single GraphQL query. In the example, the assumption is that there is
both a Todos table and a Comments table. We’ll show how to do this in Attaching a
data
source
and Configuring
resolvers.