Designing your schema - AWS AppSync

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
  1. Sign in to the AWS Management Console and open the AppSync console.

    1. In the APIs dashboard, choose your GraphQL API.

    2. In the Sidebar, choose Schema.

  2. You can configure your schema.graphql file and submit it to AWS AppSync by adding the following into the Schema window:

    schema { }
  3. Choose Save schema.

    Note

    Every schema begins with the schema root for processing. This fails to process until you add a root query type.

API
  1. Create a GraphQL API object by calling the CreateGraphqlApi API.

  2. Call the StartSchemaCreation API.

CLI
  1. If you haven't already done so, install the AWS CLI, then add your configuration.

  2. Create a GraphQL API object by running the create-graphql-api command.

    You'll need to type in two parameters for this particular command:

    1. The name of your API.

    2. The authentication-type, or the type of credentials used to access the API (IAM, OIDC, etc.).

    Note

    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" } }
  3. Run the start-schema-creation command.

    You'll need to type in two parameters for this particular command:

    1. Your api-id from the previous step.

    2. 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
  1. Sign in to the AWS Management Console and open the AppSync console.

    1. In the APIs dashboard, choose your GraphQL API.

    2. In the Sidebar, choose Schema.

  2. Add the following to your schema.graphql file:

    schema { query:Query } type Query { getTodos: [Todo] }
API
  1. Update the root schema by calling the UpdateType API.

  2. Create your Query type by calling the CreateType API.

CLI
  1. Update your root schema by running the update-type command.

    You'll need to enter in a few parameters for this particular command:

    1. The api-id of your API.

    2. The type-name of your type. In the console example, this was Schema.

    3. The definition, or the content of your type. In the console example, this was

      schema { query:Query }
    4. 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" } }
  2. Create a Query type by running the create-type command.

    You'll need to enter in a few parameters for this particular command:

    1. The api-id of your API.

    2. The definition, or the content of your type. In the console example, this was

      type Query { getTodos: [Todo] }
    3. 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
  1. Sign in to the AWS Management Console and open the AppSync console.

    1. In the APIs dashboard, choose your GraphQL API.

    2. In the Sidebar, choose Schema.

  2. 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
  • Create your Todo type by calling the CreateType 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:

    1. The api-id of your API.

    2. The definition, or the content of your type. In the console example, this was

      type Todo { id: ID! name: String description: String priority: Int }
    3. 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
  1. Sign in to the AWS Management Console and open the AppSync console.

    1. In the APIs dashboard, choose your GraphQL API.

    2. In the Sidebar, choose Schema.

  2. 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
  • Create your Todo type by calling the CreateType API.

CLI
  1. Update your root schema by running the update-type command.

    You'll need to enter in a few parameters for this particular command:

    1. The api-id of your API.

    2. The type-name of your type. In the console example, this was Schema.

    3. The definition, or the content of your type. In the console example, this was

      schema { query:Query mutation: Mutation }
    4. 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" } }
  2. Create a Mutation type by running the create-type command.

    You'll need to enter in a few parameters for this particular command:

    1. The api-id of your API.

    2. 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 }
    3. 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.

Note

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.