Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated May 6, 2024

Set up Amplify Data

In this guide, you will learn how to set up Amplify Data. This includes building a real-time API and database using TypeScript to define your data model, and securing your API with authorization rules. We will also explore using AWS Lambda to scale to custom use cases.

Before you begin, you will need:

  • Node.js v18.16.0 or later
  • npm v6.14.4 or later
  • git v2.14.1 or later

With Amplify Data, you can build a secure, real-time API backed by a database in minutes. After you define your data model using TypeScript, Amplify will deploy a real-time API for you. This API is powered by AWS AppSync and connected to an Amazon DynamoDB database. You can secure your API with authorization rules and scale to custom use cases with AWS Lambda.

Building your data backend

If you've run npm create amplify@latest already, you should see an amplify/data/resource.ts file, which is the central location to configure your data backend. The most important element is the schema object, which defines your backend data models (a.model()) and custom queries (a.query()), mutations (a.mutation()), and subscriptions (a.subscription()).

amplify/data/resource.ts
1import { a, defineData, type ClientSchema } from '@aws-amplify/backend';
2
3const schema = a.schema({
4 Todo: a.model({
5 content: a.string(),
6 isDone: a.boolean()
7 })
8 .authorization(allow => [allow.publicApiKey()])
9});
10
11// Used for code completion / highlighting when making requests from frontend
12export type Schema = ClientSchema<typeof schema>;
13
14// defines the data resource to be deployed
15export const data = defineData({
16 schema,
17 authorizationModes: {
18 defaultAuthorizationMode: 'apiKey',
19 apiKeyAuthorizationMode: { expiresInDays: 30 }
20 }
21});

Every a.model() automatically creates the following resources in the cloud:

  • a DynamoDB database table to store records
  • query and mutation APIs to create, read (list/get), update, and delete records
  • real-time APIs to subscribe for create, update, and delete events of records

The allow.publicApiKey() rule designates that anyone authenticated using an API key can create, read, update, and delete todos.

To deploy these resources to your cloud sandbox, run the following CLI command in your terminal:

Terminal
npx ampx sandbox

Connect your application code to the data backend

Once the cloud sandbox is up and running, it will also create an amplify_outputs.json file, which includes the relevant connection information to your data backend, like your API endpoint URL and API key.

To connect your frontend code to your backend, you need to:

  1. configure the Amplify library with the Amplify client configuration file (amplify_outputs.json)
  2. generate a new API client from the Amplify library
  3. make an API request with end-to-end type-safety

First, install the Amplify client library to your project:

Terminal
npm add aws-amplify

In your app's entry point, typically main.tsx for React apps created using Vite, make the following edits:

src/main.tsx
1import { Amplify } from 'aws-amplify';
2import outputs from '../amplify_outputs.json';
3
4Amplify.configure(outputs);

Write data to your backend

Let's first add a button to create a new todo item. To make a "create Todo" API request, generate the data client using generateClient() in your frontend code, and then call .create() operation for the Todo model. The Data client is a fully typed client that gives you in-IDE code completion. To enable this in-IDE code completion capability, pass in the Schema type to the generateClient function.

src/TodoList.tsx
1import type { Schema } from '../amplify/data/resource'
2import { generateClient } from 'aws-amplify/data'
3
4const client = generateClient<Schema>()
5
6export default function TodoList() {
7 const createTodo = async () => {
8 await client.models.Todo.create({
9 content: window.prompt("Todo content?"),
10 isDone: false
11 })
12 }
13
14 return <div>
15 <button onClick={createTodo}>Add new todo</button>
16 </div>
17}

Run the application in local development mode and check your network tab after creating a todo. You should see a successful request to a /graphql endpoint.

Try playing around with the code completion of .update(...) and .delete(...) to get a sense of other mutation operations.

Read data from your backend

Next, list all your todos and then refetch the todos after a todo has been added:

src/TodoList.tsx
1import { useState, useEffect } from "react";
2import type { Schema } from "../amplify/data/resource";
3import { generateClient } from "aws-amplify/data";
4
5const client = generateClient<Schema>();
6
7export default function TodoList() {
8 const [todos, setTodos] = useState<Schema["Todo"]["type"][]>([]);
9
10 const fetchTodos = async () => {
11 const { data: items, errors } = await client.models.Todo.list();
12 setTodos(items);
13 };
14
15 useEffect(() => {
16 fetchTodos();
17 }, []);
18
19 const createTodo = async () => {
20 await client.models.Todo.create({
21 content: window.prompt("Todo content?"),
22 isDone: false,
23 });
24
25 fetchTodos();
26 }
27
28 return (
29 <div>
30 <button onClick={createTodo}>Add new todo</button>
31 <ul>
32 {todos.map(({ id, content }) => (
33 <li key={id}>{content}</li>
34 ))}
35 </ul>
36 </div>
37 );
38}

Subscribe to real-time updates

You can also use observeQuery to subscribe to a live feed of your backend data. Let's refactor the code to use a real-time observeQuery instead.

src/App.tsx
1import type { Schema } from "../amplify/data/resource";
2import { useState, useEffect } from "react";
3import { generateClient } from "aws-amplify/data";
4
5const client = generateClient<Schema>();
6
7export default function TodoList() {
8 const [todos, setTodos] = useState<Schema["Todo"]["type"][]>([]);
9
10 useEffect(() => {
11 const sub = client.models.Todo.observeQuery().subscribe({
12 next: ({ items }) => {
13 setTodos([...items]);
14 },
15 });
16
17 return () => sub.unsubscribe();
18 }, []);
19
20 const createTodo = async () => {
21 await client.models.Todo.create({
22 content: window.prompt("Todo content?"),
23 isDone: false,
24 });
25 // no more manual refetchTodos required!
26 // - fetchTodos()
27 };
28
29 return (
30 <div>
31 <button onClick={createTodo}>Add new todo</button>
32 <ul>
33 {todos.map(({ id, content }) => (
34 <li key={id}>{content}</li>
35 ))}
36 </ul>
37 </div>
38 );
39}

Now try to open your app in two browser windows and see how creating a todo in one window automatically adds the todo in the second window as well.

You can also use .onCreate, .onUpdate, or .onDelete to subscribe to specific events. Review Subscribe to real-time events to learn more about subscribing to specific mutation events.

Conclusion

Success! You've learned how to create your first real-time API and database with Amplify Data.

Next steps

There's so much more to discover with Amplify Data. Learn more about: