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

Page updated May 21, 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
import { a, defineData, type ClientSchema } from '@aws-amplify/backend';
const schema = a.schema({
Todo: a.model({
content: a.string(),
isDone: a.boolean()
})
.authorization(allow => [allow.publicApiKey()])
});
// Used for code completion / highlighting when making requests from frontend
export type Schema = ClientSchema<typeof schema>;
// defines the data resource to be deployed
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'apiKey',
apiKeyAuthorizationMode: { expiresInDays: 30 }
}
});

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
  • createdAt and updatedAt fields that help you keep track of when each record was initially created or when it was last updated
  • 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 --outputs-out-dir <path_to_app/src/main/res/raw/>

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

Under Gradle Scripts, open build.gradle (Module :app), add the following lines:

app/build.gradle.kts
android {
compileOptions {
// Support for Java 8 features
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
// Amplify API dependencies
implementation("com.amplifyframework:aws-api:ANDROID_VERSION")
// ... other dependencies
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
}

Click Sync Now in the notification bar above the file editor to sync these dependencies.

Next, configure the Amplify client library with the generated amplify_outputs.json file to make it aware of the backend API endpoint. *Note: verify that the amplify_outputs.json file is present in your res/raw/ folder.

Create a new MyAmplifyApp class that inherits from Application with the following code:

Before calling the Amplify.configure function, make sure to either download the amplify_outputs.json file from the console, or generate it with the following command:

Terminal
npx ampx generate outputs --app-id <app-id> --branch main --out-dir app/src/main/res/raw

Next, be sure the file you generated or downloaded is in the appropriate resource directory for your application (for example, app/src/main/res/raw) in your Android project. Otherwise, you will not be able to compile your application.

package com.example.myapplication
import android.app.Application
import android.util.Log
import com.amplifyframework.AmplifyException
import com.amplifyframework.api.aws.AWSApiPlugin
import com.amplifyframework.core.Amplify
import com.amplifyframework.core.configuration.AmplifyOutputs
class MyAmplifyApp : Application() {
override fun onCreate() {
super.onCreate()
try {
// Adds the API plugin that is used to issue queries and mutations
// to your backend.
Amplify.addPlugin(AWSApiPlugin())
// Configures the client library to be aware of your backend API
// endpoint and authorization modes.
Amplify.configure(AmplifyOutputs(R.raw.amplify_outputs), applicationContext)
Log.i("Tutorial", "Initialized Amplify")
} catch (error: AmplifyException) {
Log.e("Tutorial", "Could not initialize Amplify", error)
}
}
}

This overrides the onCreate() to initialize Amplify when your application is launched.

Next, configure your application to use your new custom Application class. Open manifests > AndroidManifest.xml, and add an android:name attribute with the value of your new class name:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".MyAmplifyApp"
...
>
<!-- ... -->
</application>
</manifest>

Build and run the application. In Logcat, you'll see a log line indicating success:

Logcat
com.example.MyAmplifyApp I/MyAmplifyApp: Initialized Amplify

Finally, let's generate the GraphQL client code for your Android application. Amplify Data uses GraphQL under the hood to make query, mutation, and subscription requests. The generated GraphQL client code helps you to author fully-typed API requests without needing to hand-author GraphQL requests and manually map them to Kotlin or Java code.

Terminal
npx ampx generate graphql-client-code --format modelgen --model-target java --out <path_to_app/src/main/java/>

Write data to your backend

In your MainActivity, add a button to create a new todo.

MainActivity.kt
// imports
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column {
Button(onClick = {
val todo = Todo.builder()
.content("My first todo")
.isDone(false)
.build()
Amplify.API.mutate(ModelMutation.create(todo),
{ Log.i("MyAmplifyApp", "Added Todo with id: ${it.data.id}")},
{ Log.e("MyAmplifyApp", "Create failed", it)},
)
}) {
Text(text = "Create Todo")
}
}
}
}
}
}
}

Build and run your app. Then, click on "Create Todo" on the app. Your Logcat should show you that a todo was successfully added:

Logcat
com.example.MyAmplifyApp I/MyAmplifyApp: Added Todo with id: SOME_TODO_ID

Read data from your backend

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

Start by creating a new TodoList @Composable that fetches the data on the initial display of the TodoList:

MainActivity.kt
@Composable
fun TodoList() {
var todoList by remember { mutableStateOf(emptyList<Todo>()) }
LaunchedEffect(Unit) {
// API request to list all Todos
Amplify.API.query(ModelQuery.list(Todo::class.java),
{
todoList = it.data.items.toList()
},
{ Log.e("MyAmplifyApp", "Failed to query.", it)})
}
LazyColumn {
items(todoList) { todo ->
Row {
// Render your activity item here
Checkbox(checked = todo.isDone, onCheckedChange = null)
Text(text = todo.content)
}
}
}
}

If you build and rerun the application, you should see the todo that was created in the previous build. But notice how when you click on the "create Todo" button, it doesn't add any new todos to the list below until the next time your app relaunches. To solve this, let's add real-time updates to the todo list.

Subscribe to real-time updates

To add real-time updates, you can use the subscription feature of Amplify Data. It allows to subscribe to onCreate, onUpdate, and onDelete events of the application. In our example, let's append the list every time a new todo is added.

MainActivity.kt
@Composable
fun TodoList() {
var todoList by remember { mutableStateOf(emptyList<Todo>()) }
LaunchedEffect(Unit) {
Amplify.API.query(ModelQuery.list(Todo::class.java),
{
todoList = it.data.items.toList()
},
{ Log.e("MyAmplifyApp", "Failed to query.", it)})
Amplify.API.subscribe(ModelSubscription.onCreate(Todo::class.java),
{ Log.i("ApiQuickStart", "Subscription established") },
{ Log.i("ApiQuickStart", "Todo create subscription received: ${it.data}")
todoList = todoList + it.data
},
{ Log.e("ApiQuickStart", "Subscription failed", it) },
{ Log.i("ApiQuickStart", "Subscription completed") }
)
}
LazyColumn {
items(todoList) { todo ->
Row {
// Render your activity item here
Checkbox(checked = todo.isDone, onCheckedChange = null)
Text(text = todo.content)
}
}
}
}

Now call TodoList() from the onCreate() function:

MainActivity.kt
setContent {
MyAmplifyAppTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Authenticator { state ->
Column {
Text(
text = "Hello ${state.user.username}!",
)
....
TodoList()

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: