Page updated Dec 13, 2023

Preview: AWS Amplify's new code-first DX (Gen 2)

The next generation of Amplify's backend building experience with a TypeScript-first DX.

Get started

Connect API and database to the app

Now that you’ve created and configured your application and initialized a new Amplify project, you can add a feature. The first feature you will add is an API.

The Amplify CLI supports creating and interacting with two types of API categories: REST and GraphQL.

The API you will be creating in this step is a GraphQL API using AWS AppSync (a managed GraphQL service) and the database will be Amazon DynamoDB (a NoSQL database).

Create a GraphQL API and database

Add a GraphQL API to your app and automatically provision a database by running the following command from the root of your application directory:

1amplify add api

Accept the default values which are highlighted below:

1? Select from one of the below mentioned services: GraphQL
2? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue
3? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)

The CLI will prompt you to open this GraphQL schema in your text editor.

amplify/backend/api/your-api-name/schema.graphql

1# This "input" configures a global authorization rule to enable public access to
2# all models in this schema. Learn more about authorization rules here: https://docs.amplify.aws/react/build-a-backend/graphqlapi/customize-authorization-rules/
3
4input AMPLIFY {
5 globalAuthRule: AuthRule = { allow: public }
6} # FOR TESTING ONLY!
7type Todo @model {
8 id: ID!
9 name: String!
10 description: String
11}

The schema generated is for a Todo app. You'll notice a @model directive on the Todo type. This directive is part of the Amplify GraphQL API category.

Amplify GraphQL API provides custom GraphQL directives that allow you to define data models, set up authorization rules, configure serverless functions as resolvers, and more.

A GraphQL type decorated with the @model directive will scaffold out the database table for the type (Todo table), the schema for CRUD (create, read, update, delete) and list operations, and the GraphQL resolvers needed to make everything work together.

From the command line, press enter to accept the schema and continue to the next steps.

Deploying the API

To deploy this backend, run the push command:

1amplify push

Choose the following values for each prompt:

1✔ Are you sure you want to continue? (Y/n) · yes
2...
3? Do you want to generate code for your newly created GraphQL API: Yes
4? Choose the code generation language target: typescript
5? Enter the file name pattern of graphql queries, mutations and subscriptions: src/graphql/**/*.ts
6? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions: Yes
7? Enter maximum statement depth [increase from default if your schema is deeply nested]: 2
8? Enter the file name for the generated code: src/API.ts
1✔ Are you sure you want to continue? (Y/n) · yes
2...
3? Do you want to generate code for your newly created GraphQL API Yes
4? Choose the code generation language target javascript
5? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js
6? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
7? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
8? Enter the file name for the generated code src/API.js

Now the API is live and you can start interacting with it! The API you have deployed includes operations for creating, reading, updating, deleting, and listing posts.

Review deployment status

Next, run the following command to check Amplify's status:

1amplify status

This will give us the current status of the Amplify project, including the current environment, any categories that have been created, and what state those categories are in. It should look similar to this:

1Current Environment: dev
2
3┌──────────┬───────────────────────┬───────────┬───────────────────┐
4│ Category │ Resource name │ Operation │ Provider plugin │
5├──────────┼───────────────────────┼───────────┼───────────────────┤
6│ Api │ your-api-name │ No Change │ awscloudformation │
7└──────────┴───────────────────────┴───────────┴───────────────────┘
Review deployed API in AWS console

To view the GraphQL API in the AppSync console at any time, run the following command:

1amplify console api

To view your entire app in the Amplify console at any time, run the following command:

1amplify console
Test API with local mocking

To test this out locally, you can run the mock command. Note: Refer to the instructions to setup mocking.

If you'd like to go ahead and connect the front end, you can jump to the next step.

1amplify mock api

Note: amplify mock api requires Java.

1# If you have not already deployed your API, you will be walked through the following steps for GraphQL code generation
2? Choose the code generation language target: javascript (or preferred target)
3? Enter the file name pattern of graphql queries, mutations and subscriptions: src/graphql/**/*.js
4? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions: Yes
5? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

This will open the GraphiQL explorer on a local port. From the test environment you can try out different operations locally, like queries and mutations.

In the GraphiQL toolbar, select Use: User Pool and try creating a todo:

1mutation CreateTodo {
2 createTodo(input: { name: "Test Todo", description: "Todo description" }) {
3 id
4 owner
5 name
6 updatedAt
7 createdAt
8 description
9 }
10}

Next, update auth to Use: API Key and try querying the list of todos:

1query ListTodos {
2 listTodos {
3 items {
4 description
5 createdAt
6 id
7 owner
8 name
9 }
10 }
11}

API with Server-Side Rendering (SSR)

In this section you will create a way to list and create todos from the Next.js application. To do this, you will fetch & render the latest todos from the server as well as create a new todo.

Generate the Amplify GraphQL API client

To make any GraphQL API requests service-side, we need to first generate an API client that we can use on server-side. To generate a new API client, import generateServerClientUsingCookies from @aws-amplify/adapter-nextjs/api and use it to generate a cookiesClient using the amplifyconfiguration.json in our project as config and cookies from next/headers.

Amplify offers two API clients for Next.js server-side runtimes. Use generateServerClientUsingCookies primarily for use cases where cookies from next/headers is available, such as in App Router's React Server Components, Server Actions. Use generateServerClientUsingReqRes for use cases where a NextRequest/NextResponse are available, such as in the Pages Router or Middleware. Review Connect to data from server-side runtimes to review in-depth which API client to use for which use cases.

Open src/app/page.tsx and replace it with the following code:

1import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
2import { cookies } from 'next/headers';
3
4import config from '@/amplifyconfiguration.json';
5
6const cookiesClient = generateServerClientUsingCookies({
7 config,
8 cookies
9});
10
11export default async function Home() {
12 return (
13 <div
14 style={{
15 maxWidth: '500px',
16 margin: '0 auto',
17 textAlign: 'center',
18 marginTop: '100px'
19 }}
20 >
21 <form>
22 <input name="name" placeholder="Add a todo" />
23 <button type="submit">Add</button>
24 </form>
25 </div>
26 );
27}

Create a form for submitting the todos

In Next.js you can use a Server Action to handle form submission server-side. Let's add a Server Action which submits its data to the createTodo function. When called, createTodo should send a GraphQL mutation via cookiesClient.graphql(...) to the GraphQL API, then call revalidatePath from the Next.js cache to invalidate the page cache and fetch the latest todos.

Update the src/app/page.tsx with the following code:

1import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
2import { cookies } from 'next/headers';
3// 1. Add the following two imports
4import { revalidatePath } from 'next/cache';
5import * as mutations from '@/graphql/mutations';
6
7import config from '@/amplifyconfiguration.json';
8
9const cookiesClient = generateServerClientUsingCookies({
10 config,
11 cookies
12});
13
14// 2. Create a new Server Action
15async function createTodo(formData: FormData) {
16 'use server';
17 const { data } = await cookiesClient.graphql({
18 query: mutations.createTodo,
19 variables: {
20 input: {
21 name: formData.get('name')?.toString() ?? ''
22 }
23 }
24 });
25
26 console.log('Created Todo: ', data?.createTodo);
27
28 revalidatePath('/');
29}
30
31export default async function Home() {
32 return (
33 <div
34 style={{
35 maxWidth: '500px',
36 margin: '0 auto',
37 textAlign: 'center',
38 marginTop: '100px'
39 }}
40 >
41 {/* 3. Update the form's action to use the
42 new create Todo Server Action*/}
43 <form action={createTodo}>
44 <input name="name" placeholder="Add a todo" />
45 <button type="submit">Add</button>
46 </form>
47 </div>
48 );
49}

List todos

Using cookiesClient.graphql(...) we make GraphQL queries as well. Pass in the listTodos query and assign the items returned to todos then iterate over them to display in a <ul> tag. If there are no todos, we display the message "No todos, please add one".

Update the src/app/page.tsx with the following code:

1import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
2import { cookies } from 'next/headers';
3import { revalidatePath } from 'next/cache';
4import * as mutations from '@/graphql/mutations';
5// 1. Add the queries as an import
6import * as queries from '@/graphql/queries';
7
8import config from '@/amplifyconfiguration.json';
9
10const cookiesClient = generateServerClientUsingCookies({
11 config,
12 cookies
13});
14
15async function createTodo(formData: FormData) {
16 'use server';
17 const { data } = await cookiesClient.graphql({
18 query: mutations.createTodo,
19 variables: {
20 input: {
21 name: formData.get('name')?.toString() ?? ''
22 }
23 }
24 });
25
26 console.log('Created Todo: ', data?.createTodo);
27
28 revalidatePath('/');
29}
30
31export default async function Home() {
32 // 2. Fetch additional todos
33 const { data, errors } = await cookiesClient.graphql({
34 query: queries.listTodos
35 });
36
37 const todos = data.listTodos.items;
38
39 return (
40 <div
41 style={{
42 maxWidth: '500px',
43 margin: '0 auto',
44 textAlign: 'center',
45 marginTop: '100px'
46 }}
47 >
48 <form action={createTodo}>
49 <input name="name" placeholder="Add a todo" />
50 <button type="submit">Add</button>
51 </form>
52
53 {/* 3. Handle edge cases & zero state & error states*/}
54 {(!todos || todos.length === 0 || errors) && (
55 <div>
56 <p>No todos, please add one.</p>
57 </div>
58 )}
59
60 {/* 4. Display todos*/}
61 <ul>
62 {todos.map((todo) => {
63 return <li style={{ listStyle: 'none' }}>{todo.name}</li>;
64 })}
65 </ul>
66 </div>
67 );
68}

Run locally

Next, run the app and you should see the updated UI with the ability to create and view the list of todos:

1npm run dev

You have successfully deployed your API and connected it to your app!