React & React Native
This tutorial walks you through how to use AWS Amplify to build a React application. You can use a similar process with a React Native application (omitting hosting).
Installation
$ npm install -g @aws-amplify/cli
$ amplify configure
If you’re using Windows, we recommend the Windows Subsystem for Linux.
- Ensure you have Create React App installed.
- Create a new project as follows:
yarn create react-app myapp
cd myapp
Note This example usesyarn
, but you can usenpm
instead.
Getting Started with the CLI To get started, initialize your project in the new directory:
amplify init
After you answer the provided questions, you can use amplify help
at any time to see the overall command structure, and amplify help <category>
to see actions for a specific category.
The Amplify CLI uses AWS CloudFormation, and you can add or modify configurations locally before you push them for execution in your account. To see the status of the deployment at any time, run amplify status
.
Publishing Your Web App
Without making any changes to your React application, add web hosting as follows:
amplify add hosting
You would be prompted next to select the environment setup. Select DEV (S3 only with HTTP) for quick prototyping and testing, and once production ready you could run the amplify update hosting
command to publish your app to Amazon CloudFront (a CDN service).
Note: when using the PROD option there could be a 15-20 minute delay for the CDN setup and content replication.
When you’re prompted for information, such as the bucket name or application files, you can use the default values by pressing Enter.
Note You can use an order alias to add or remove category features. You can also run amplify hosting add
.
Run amplify status
to see that status (not deployed). Next, build and deploy your site by running amplify publish
or amplify publish --invalidate-cache
- for cache invalidation in the distribution network (if CloudFront is added via the hosting category). After it’s complete, your application is available in an S3 hosting bucket for testing. It’s also fronted with an Amazon CloudFront distribution. (if it is added via the hosting category in the prior bucket)
Add Auth
Now that your app is in the cloud, you can add some features like enabling users to register for your site and log in. Run amplify add auth
and select the Default configuration. This adds the auth resource configurations locally in your amplify/backend/auth
directory.
Run amplify push
to provision your auth resources in the cloud. The ./src/aws-exports.js
file that’s created has all of the appropriate cloud resources defined for your application.
Next, add the Amplify library to your web application as follows:
yarn add aws-amplify aws-amplify-react
If integrating with a React Native app, use:
yarn add aws-amplify aws-amplify-react-native
react-native link amazon-cognito-identity-js # DO NOT run this when using Expo or ExpoKit
Edit ./src/App.js
to include the Amplify library, configurations, and React HOC. Then, initialize the library as follows:
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
import { withAuthenticator } from 'aws-amplify-react'; // or 'aws-amplify-react-native';
Amplify.configure(awsconfig);
Wrap the default App
component using withAuthenticator
at the bottom of the file as follows:
export default withAuthenticator(App, true);
You can now use amplify publish
to build and publish your app again. This time you’ll be able to register a new user and sign in before opening the main application.
API & property details for the
Authenticator
andwithAuthenticator
HOC are available in the Authentication Guide.
SignUp Configuration
The SignUp component provides your users with the ability to sign up. It is included as part of the Authenticator
component.
Usage:
<Authenticator signUpConfig={signUpConfig}/>
It can also be used as part of the authentication HOC:
export default withAuthenticator(App, { signUpConfig });
The SignUp Component accepts a ‘signUpConfig’ object which allows you to customize it.
Attribute | Type | Description | Default | Required |
---|---|---|---|---|
header | string | the component header | 'Create a new account' | no |
signUpFields | array | see below | see below | no |
defaultCountryCode | string | the preselected value in the country code dropdown | '1' | no |
hideAllDefaults | boolean | determines whether all default signup fields are hidden | N/A | no |
hiddenDefaults | array | determines whether particular default fields are hidden | N/A (possible values include 'username', 'password', 'phone_number', and 'email') | no |
The signUpFields array in turn consist of an array of objects, each describing a field that will appear in sign up form that your users fill out:
Attribute | Type | Description | Possible Values |
---|---|---|---|
label | string | label for the input field | N/A |
key | string | key name for the attribute as defined in the User Pool | N/A |
required | boolean | whether or not the field is required | N/A |
displayOrder | number | number indicating the order in which fields will be displayed | N/A |
type | string | the type attribute for the html input element | ‘string’, ‘number’, ‘password’, etc |
custom | boolean | flag which indicates whether or not the field is ‘custom’ in the User Pool | N/A |
The following example will replace all the default sign up fields with the ones defined in the signUpFields
array.
import React, { Component } from 'react';
import { withAuthenticator } from 'aws-amplify-react';
class App extends Component {
}
const signUpConfig = {
header: 'My Customized Sign Up',
hideAllDefaults: true,
defaultCountryCode: '1',
signUpFields: [
{
label: 'Email',
key: 'email',
required: true,
displayOrder: 1,
type: 'string'
},
{
label: 'Password',
key: 'password',
required: true,
displayOrder: 2,
type: 'password'
},
{
label: 'PhoneNumber',
key: 'phone_number',
required: true,
displayOrder: 3,
type: 'string'
},
{
label: 'Custom Attribute',
key: 'custom_attr',
required: false,
displayOrder: 4,
type: 'string',
custom: true
}
]
};
export default withAuthenticator(App, { signUpConfig });
Sign up/in with email/phone number
If the user pool is set to allow email addresses/phone numbers as the username, you can then change the UI components accordingly by using usernameAttributes
.
When you are using email
as the username:
import { withAuthenticator, Authenticator } from 'aws-amplfy-react';
// When using Authenticator
class App {
// ...
render() {
return (
<Authenticator usernameAttributes='email'/>
);
}
}
export default App;
// When using withAuthenticator
class App2 {
// ...
}
export default withAuthenticator(App2, { usernameAttributes: 'email' });
When you are using phone number
as the username:
import { Authenticator, withAuthenticator } from 'aws-amplfy-react';
class App {
// ...
render() {
return (
<Authenticator usernameAttributes='phone_number'/>
);
}
}
export default App;
// When using withAuthenticator
class App2 {
// ...
}
export default withAuthenticator(App2, { usernameAttributes: 'phone_number' });
Note: if you are using custom signUpFields to customize the username
field, then you need to make sure either the label of that field is the same value you set in usernameAttributes
or the key of the field is username
.
For example:
import React, { Component } from 'react';
import { withAuthenticator } from 'aws-amplify-react';
class App extends Component {
}
const signUpConfig = {
header: 'My Customized Sign Up',
hideAllDefaults: true,
defaultCountryCode: '1',
signUpFields: [
{
label: 'My user name',
key: 'username',
required: true,
displayOrder: 1,
type: 'string'
},
{
label: 'Password',
key: 'password',
required: true,
displayOrder: 2,
type: 'password'
},
{
label: 'PhoneNumber',
key: 'phone_number',
required: true,
displayOrder: 3,
type: 'string'
},
{
label: 'Custom Attribute',
key: 'custom_attr',
required: false,
displayOrder: 4,
type: 'string',
custom: true
}
]
};
const usernameAttributes = 'My user name';
export default withAuthenticator(App, {
signUpConfig,
usernameAttributes
});
Add Analytics and Storage
Next, we’ll add some features, like tracking user behavior analytics and uploading/downloading images in the cloud. Start by running amplify add analytics
in your project. You can enable analytics for authenticated users only, or for users that aren’t authenticated. You would be prompted to ask whether you want to allow guests and unauthenticated users to send analytics events, so you can choose Yes
. You can also try a new project without authentication configured to test this feature.
Run amplify add storage
and then select Content (Images, audio, video, etc.). You’ll then be prompted for authorization related questions. Choose Auth and guest users to give both authorized and guest users access. In the next prompts, based on your previous selection you would be asked to configure read/write permissions for the authorized and guest users. When complete, run amplify push
to create the cloud resources.
Edit your App.js
file in the React project again and modify your imports so that the Analytics
and Storage
categories are included in addition to the S3Album
component, which we’ll use to upload and download photos.
import Amplify, { Analytics, Storage } from 'aws-amplify';
import { withAuthenticator, S3Album } from 'aws-amplify-react';
The Analytics
category automatically tracks user session data such as sign-in events. However, you can record custom events or metrics at any time. You can also use the Storage
category to upload files to a private user location after someone has logged in. First, add the following line after Amplify.configure()
has been called:
Storage.configure({ level: 'private' });
Next, add the following methods before the component’s render
method as follows:
uploadFile = (evt) => {
const file = evt.target.files[0];
const name = file.name;
Storage.put(name, file).then(() => {
this.setState({ file: name });
})
}
componentDidMount() {
Analytics.record('Amplify_CLI');
}
Finally, modify the render
method so that you can upload files and also view any of the private
photos that have been added for the logged in user as follows:
render() {
return (
<div className="App">
<p> Pick a file</p>
<input type="file" onChange={this.uploadFile} />
<S3Album level="private" path='' />
</div>
);
}
Save your changes and run amplify publish
. You’ve already pushed the changes earlier so just the local build is created and uploaded to the hosting bucket. Log in (if necessary) and upload photos, which are protected by the user. You can refresh the page to view the photos after you upload them.
Add GraphQL Backend
Now that your application is set up, it’s time to add a backend API with data that can be persisted in a database. The Amplify CLI comes with a GraphQL Transformer that converts annotated GraphQL schema files into the appropriate AWS CloudFormation template based on your data requirements. This includes options such as the following:
@model
for storing types in Amazon DynamoDB.@auth
to define different authorization strategies.@connection
for specifying relationships between@model
object types.@searchable
for streaming the data of an@model
object type to Amazon Elasticsearch Service.
To get started run amplify add api
and select GraphQL
. When prompted, choose Amazon Cognito User Pool
and the project will leverage your existing authentication setup. For annotated schema
, choose No. For guided schema creation
, choose Yes.
The guided steps provide some default schemas that are pre-annotated to help you learn. The following steps take you through the Single object with fields
option, but feel free to revisit these steps later in another project. If you choose this option you’ll see the following annotated schema in your text editor:
type Todo @model {
id: ID!
name: String!
description: String
}
This is the GraphQL schema that you’ll deploy to AWS AppSync. If you’re familiar with GraphQL you can rename or add fields and types, but you’ll need to change the client code too. When you’re ready press Enter
in the CLI and then run amplify push
.
After the deployment is complete, open your App.js
again and update the import to include both the API
category and graphqlOperation
method as follows:
import Amplify, { Analytics, Storage, API, graphqlOperation } from 'aws-amplify';
Add the following query and mutations in your code, before the class App extends Component {...}
definition as follows:
const listTodos = `query listTodos {
listTodos{
items{
id
name
description
}
}
}`
const addTodo = `mutation createTodo($name:String! $description: String!) {
createTodo(input:{
name:$name
description:$description
}){
id
name
description
}
}`
Now, inside the App
component add the following two methods before the render()
method:
todoMutation = async () => {
const todoDetails = {
name: 'Party tonight!',
description: 'Amplify CLI rocks!'
};
const newTodo = await API.graphql(graphqlOperation(addTodo, todoDetails));
alert(JSON.stringify(newTodo));
}
listQuery = async () => {
console.log('listing todos');
const allTodos = await API.graphql(graphqlOperation(listTodos));
alert(JSON.stringify(allTodos));
}
You can now make GraphQL calls from your application. Update the render()
method so that it has the following buttons to invoke the mutation and query:
render() {
return (
<div className="App">
<p> Pick a file</p>
<input type="file" onChange={this.uploadFile} />
<button onClick={this.listQuery}>GraphQL Query</button>
<button onClick={this.todoMutation}>GraphQL Mutation</button>
<S3Album level="private" path='' />
</div>
);
}
Save the file and run amplify publish
. After the backend is deployed, you can choose GraphQL Mutation to enter data into the database and GraphQL Query to retrieve a list of all entries. You can validate this in the AWS AppSync console too.
Add REST API Calls to a Database
For this example, we use a REST backend with a NoSQL database. Run amplify add api
and follow the prompts. Select the REST option and provide a friendly name for your API, such as myapi or something else that you can remember. Use the default /items
path and choose Create a new lambda function. Choose the option titled CRUD function for Amazon DynamoDB table (Integration with Amazon API Gateway and Amazon DynamoDB) when prompted. This creates an architecture using Amazon API Gateway with Express running in an AWS Lambda function that reads and writes to Amazon DynamoDB. You can modify the routes in the Lambda function later to meet your needs and update it in the cloud.
Since you do not have a database provisioned yet, the CLI workflow prompts you for this information. Alternatively, you can run amplify add storage
beforehand to create a DynamoDB table and use it in this setup. When the CLI prompts you for the primary key structure, use an attribute named id
of type String
. Don’t select any other options like sort keys or global secondary indexes (GSIs).
Next, for the API security type questions, choose Yes when prompted for Restriction of API access. Similar to the storage category, when prompted for Who should have access?, choose Auth and guest users to give both authorized and guest users access. In the next prompts, based on your previous selection you would be asked to configure read/write permissions for authorized and guest user.
In the React project, edit your App.js
file again and modify your imports so that the API
category is included so that you can make API calls from the app.
import Amplify, { Analytics, Storage, API } from 'aws-amplify';
In App.js
, add the following code before the render()
method and update myapi
if you used an alternative name during the setup:
post = async () => {
console.log('calling api');
const response = await API.post('myapi', '/items', {
body: {
id: '1',
name: 'hello amplify!'
}
});
alert(JSON.stringify(response, null, 2));
}
get = async () => {
console.log('calling api');
const response = await API.get('myapi', '/items/object/1');
alert(JSON.stringify(response, null, 2));
}
list = async () => {
console.log('calling api');
const response = await API.get('myapi', '/items/1');
alert(JSON.stringify(response, null, 2));
}
Update the render()
method to include calls to the following methods:
render() {
return (
<div className="App">
<p> Pick a file</p>
<input type="file" onChange={this.uploadFile} />
<button onClick={this.post}>POST</button>
<button onClick={this.get}>GET</button>
<button onClick={this.list}>LIST</button>
<S3Album level="private" path='' />
</div>
);
}
Save the file and run amplify publish
. After the API is deployed along with the Lambda function and database table, your app is built and updated in the cloud. You can then add a record to the database by choosing POST, and then using GET or LIST to retrieve the record, which has been hard coded in this simple example.
In your project directory, open ./amplify/backend/function
and you’ll see the Lambda function that you created. The app.js
file runs the Express function and all of the HTTP method routes are available for you to manipulate. For example, the API.post()
in your React app corresponded to the app.post(path, function(req, res){...})
code in this Lambda function. If you choose to customize the Lambda function, you can update it in the cloud using amplify push
.
Adding XR Sumerian Scene
To load Amazon Sumerian scenes you will need to activate the Amplify Auth category.
$ amplify add auth
The ./src/aws-exports.js
file that’s created has all of the appropriate cloud resources defined for your application. Edit ./src/App.js
to include the Amplify library and configurations. Then, initialize the library as follows:
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
import { SumerianScene } from 'aws-amplify-react';
import scene1Config from './sumerian_exports_<sceneId>'; // This file will be generated by the Sumerian AWS Console
Amplify.configure({...awsconfig,
XR: {
region: "us-east-1", // Sumerian region
scenes: {
"scene1": { // Friendly scene name
sceneConfig: scene1Config // Scene configuration from Sumerian publish
}
}
}
});
Update the render()
method to include the Sumerian Scene component:
Note: The UI component will inherit the height and width of the direct parent DOM element. Make sure to set the width and height styling on the parent DOM element to your desired size.
render() {
return (
<div className="App">
// sceneName: the configured friendly scene you would like to load
<SumerianScene sceneName="scene1" />
</div>
);
}
See the XR documentation for information on creating and publishing a Sumerian scene.
Testing Serverless Functions
Amplify CLI supports local testing of Lambda functions. Run amplify status
to get the resource name of the Lambda function created earlier, and then run the following:
amplify function invoke <resourcename>
In this case, the function runs, but it doesn’t exit because this Lambda example starts an Express server which you need to manually close when testing it from the CLI. Use ctrl-c
to close and open the ./amplify/backend/function/resourcename
directory to see the local structure that is packaged for Lambda invocation from API Gateway. The Lambda function is inside the src
directory, along with event.json
, which is used for the amplify function invoke
command you just ran. index.js
is also in this directory, which is the main entry point for the Serverless Express library that echoed out the test event and instantiated the server inside app.js
. Since the Express routes defined in app.js
don’t have a path that’s called via the test event, it responded with a 404 message. For more information, see https://github.com/awslabs/aws-serverless-express.
Note on JWT Storage
Data is stored unencrypted when using standard storage adapters (localStorage
in the browser and AsyncStorage
on React Native). Amplify gives you the option to use your own storage object to persist data. With this, you could write a thin wrapper around libraries like:
react-native-keychain
react-native-secure-storage
- Expo’s secure store