Page updated Mar 12, 2024

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}

Connect frontend to API

First, create a todos Angular component:

1npx ng generate component --standalone todos

In src/app/app.component.ts update it to import TodosComponent and add it to the Component imports:

1import { Component } from '@angular/core';
2import { CommonModule } from '@angular/common';
3import { RouterOutlet } from '@angular/router';
4import { TodosComponent } from './todos/todos.component';
5
6@Component({
7 selector: 'app-root',
8 standalone: true,
9 imports: [CommonModule, RouterOutlet, TodosComponent],
10 templateUrl: './app.component.html',
11 styleUrls: ['./app.component.css']
12})
13export class AppComponent {
14 title = 'amplify-app';
15}

Open src/app/app.component.html, and replace the default content with the Angular component you created:

1<app-todos></app-todos>

In your src/app/todos/todos.component.ts file, add data to your database with a mutation by using the API.ts file which was generated when you ran amplify add api. Additionally, make sure to import the needed form helpers as well as adding ReactiveFormsModule to your Component imports:

1import { Component } from '@angular/core';
2import {
3 FormBuilder,
4 FormGroup,
5 ReactiveFormsModule,
6 Validators
7} from '@angular/forms';
8import { generateClient, type Client } from 'aws-amplify/api';
9import { Todo, ListTodosQuery } from '../../API';
10import * as mutations from '../../graphql/mutations';
11
12@Component({
13 standalone: true,
14 imports: [ReactiveFormsModule],
15 selector: 'app-todos',
16 templateUrl: './todos.component.html',
17 styleUrls: ['./todos.component.css']
18})
19export class TodosComponent {
20 public createForm: FormGroup;
21 public client: Client;
22
23 constructor(private fb: FormBuilder) {
24 this.createForm = this.fb.group({
25 name: ['', Validators.required],
26 description: ['', Validators.required]
27 });
28
29 this.client = generateClient();
30 }
31
32 public async onCreate(todo: Todo) {
33 try {
34 const response = await this.client.graphql({
35 query: mutations.createTodo,
36 variables: {
37 input: todo
38 }
39 });
40 console.log('item created!', response);
41 this.createForm.reset();
42 } catch (e) {
43 console.log('error creating todo...', e);
44 }
45 }
46}

Next, add a form that will be used for creating todos. Add the following to your src/app/todos/todos.component.html:

1<div class="form-body">
2 <form
3 autocomplete="off"
4 [formGroup]="createForm"
5 (ngSubmit)="onCreate(createForm.value)"
6 >
7 <div>
8 <label>Name: </label>
9 <input type="text" formControlName="name" autocomplete="off" />
10 </div>
11 <div>
12 <label>Description: </label>
13 <input type="text" formControlName="description" autocomplete="off" />
14 </div>
15 <button type="submit">Submit</button>
16 </form>
17</div>

Next, update your TodosComponent class so that it will list all todos in the database when the app starts. To do so, implement OnInit and add a ListTodos query in src/app/todos/todos.component.ts. Store the query results in an array.

1import { Component, OnInit } from '@angular/core';
2import {
3 FormBuilder,
4 FormGroup,
5 ReactiveFormsModule,
6 Validators
7} from '@angular/forms';
8
9import { generateClient, type Client } from 'aws-amplify/api';
10import { ListTodosQuery, Todo } from '../../API';
11import * as mutations from '../../graphql/mutations';
12import * as queries from '../../graphql/queries';
13
14@Component({
15 standalone: true,
16 selector: 'app-todos',
17 imports: [ReactiveFormsModule],
18 templateUrl: './todos.component.html',
19 styleUrls: ['./todos.component.css']
20})
21export class TodosComponent implements OnInit {
22 public createForm: FormGroup;
23 public client: Client;
24
25 /* declare todos variable */
26 public todos: ListTodosQuery['listTodos'];
27
28 constructor(private fb: FormBuilder) {
29 this.createForm = this.fb.group({
30 name: ['', Validators.required],
31 description: ['', Validators.required]
32 });
33
34 this.client = generateClient();
35 }
36
37 async ngOnInit() {
38 /* fetch todos when app loads */
39 try {
40 const response = await this.client.graphql({
41 query: queries.listTodos
42 });
43 this.todos = response.data.listTodos;
44 } catch (e) {
45 console.log('error fetching todos', e);
46 }
47 }
48
49 public async onCreate(todo: Todo) {
50 try {
51 const response = await this.client.graphql({
52 query: mutations.createTodo,
53 variables: {
54 input: todo
55 }
56 });
57 console.log('item created!', response);
58 this.createForm.reset();
59 } catch (e) {
60 console.log('error creating todo...', e);
61 }
62 }
63}

Add the following to your src/app/todos/todos.component.html to display any of the todos you have added:

1<div>
2 @for (todo of todos?.items; track todo?.id) {
3 <div>{{ todo?.name }}</div>
4 <div>{{ todo?.description }}</div>
5 } @empty {
6 <div>There are no items.</div>
7 }
8</div>

To subscribe to realtime data, declare a subscription class variable and update ngOnInit in src/app/todos.component.ts. When the app starts, this code will set up a subscription. The subscription will update the todos array when new events are received (when a new todo is created):

1...
2
3import * as subscriptions from '../../graphql/subscriptions'
4
5export class TodosComponent implements OnInit {
6
7 private subscription: any = null;
8
9 ...
10
11 async ngOnInit() {
12
13 ...
14
15 try {
16 const res = await this.client.graphql({
17 query: queries.listTodos
18 })
19 console.log(res)
20
21 this.todos = res.data.listTodos.items;
22 } catch(e) {
23 console.log(e)
24 };
25
26 /* subscribe to new todos being created */
27 this.subscription = this.client.graphql({
28 query: subscriptions.onCreateTodo
29 }).subscribe({
30 next: (event: any) => {
31 const newTodo = event.data.onCreateTodo;
32 if(this.todos) {
33 this.todos.items = [newTodo, ...this.todos.items];
34 }
35 }
36 });
37 }
38}

Finally, unsubscribe from the subscription when the component is destroyed. Import and add OnDestroy in src/app/todos.component.ts:

1import { Component, OnDestroy, OnInit } from '@angular/core';
2
3...
4
5export class TodosComponent implements OnInit, OnDestroy {
6
7 ...
8
9 ngOnDestroy() {
10 if (this.subscription) {
11 this.subscription.unsubscribe();
12 }
13 this.subscription = null;
14 }
15}

The final todos.component.ts file should look like the following:

1import { Component, OnInit, OnDestroy } from '@angular/core';
2import {
3 FormBuilder,
4 FormGroup,
5 ReactiveFormsModule,
6 Validators
7} from '@angular/forms';
8
9import { generateClient, type Client } from 'aws-amplify/api';
10import { Todo, ListTodosQuery } from '../../API';
11import * as mutations from '../../graphql/mutations';
12import * as queries from '../../graphql/queries';
13import * as subscriptions from '../../graphql/subscriptions';
14
15@Component({
16 standalone: true,
17 selector: 'app-todos',
18 imports: [ReactiveFormsModule],
19 templateUrl: './todos.component.html',
20 styleUrls: ['./todos.component.css']
21})
22export class TodosComponent implements OnInit, OnDestroy {
23 public todos: ListTodosQuery['listTodos'];
24 public createForm: FormGroup;
25 public client: Client;
26
27 private subscription: any = null;
28
29 constructor(private fb: FormBuilder) {
30 this.createForm = this.fb.group({
31 name: ['', Validators.required],
32 description: ['', Validators.required]
33 });
34
35 this.client = generateClient();
36 }
37
38 async ngOnInit() {
39 /* fetch todos when app loads */
40 try {
41 const response = await this.client.graphql({
42 query: queries.listTodos
43 });
44 this.todos = response.data.listTodos;
45 } catch (e) {
46 console.log('error fetching todos', e);
47 }
48
49 this.subscription = this.client
50 .graphql({
51 query: subscriptions.onCreateTodo
52 })
53 .subscribe({
54 next: (event: any) => {
55 const newTodo: Todo = event.data.onCreateTodo;
56 if (this.todos) {
57 this.todos.items = [newTodo, ...this.todos.items];
58 }
59 }
60 });
61 }
62
63 ngOnDestroy(): void {
64 if (this.subscription) {
65 this.subscription.unsubscribe();
66 }
67 this.subscription = null;
68 }
69
70 public async onCreate(todo: Todo) {
71 try {
72 const response = await this.client.graphql({
73 query: mutations.createTodo,
74 variables: {
75 input: todo
76 }
77 });
78 console.log('item created!', response);
79 this.createForm.reset();
80 } catch (e) {
81 console.log('error creating todo...', e);
82 }
83 }
84}

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 start

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