Business Productivity
Quickly launch an Amazon Chime SDK application with AWS Amplify
Building a server-less full stack meeting application enabled with the Amazon Chime SDK for Javascript requires you to configure a back end cloud infrastructure to allow for the creation and deletion of Amazon Chime meetings and attendees within the application. This walkthrough shows how to leverage AWS Amplify to simplify and speed up the creation of the back end infrastructure, making it faster and simpler to create a meeting application powered by the Amazon Chime SDK for JavaScript.
This walkthrough will guide you through the steps of building a basic server-less full stack meeting web application with collaborative audio and video capabilities by leveraging the Amazon Chime SDK React Components to build the front end and Amplify to bootstrap back end resources. The Amazon Chime SDK React Components Library is built on the Amazon Chime SDK for JavaScript. The React Components Library features pre-built React components – such as VideoGrid, MeetingControls, and DeviceSelection – which are already integrated with the Amazon Chime SDK for Javascript and provide an interface for builders to quickly create any type of web application that requires audio or video collaboration capabilities. Refer to the Amazon Chime SDK React Component Library meeting demo for an example of building a meeting application with Amazon Chime SDK React Components.
The first part of this walkthrough shows how to build a basic responsive User Interface (UI) by integrating Amazon Chime SDK React Components into your web application. The second part of the walkthrough will show you how to use Amplify to configure and generate required backend resources – such as an AWS AppSync API, AWS Lambda function, and Amazon DynamoDB Tables. Amplify reduces the amount of time it takes to configure and create back end resources in your AWS account by providing an easy-to-use CLI that automatically generates and configures all of the required back end resources.
Solution Overview
The application’s front end consists of two basic components which rely on the Amazon Chime SDK React Component Library. The first component is a basic home page consisting of a UI form for taking in the meeting title and attendee name as input. The second component is a simple meeting view that displays attendees’ videos and access basic meeting controls – such as choosing alternative audio and video devices or leaving the meeting.
The application’s back end infrastructure is configured and provisioned with the Amplify CLI. The back end infrastructure is comprised of a GraphQL-based API and multiple data sources to resolve the API calls. Data sources are resources in your AWS account that GraphQL APIs can interact with. Resolvers are data sources that fulfill a GraphQL API request and return a response. Behind the scenes, Amplify configures a GraphQL API in AWS AppSync and connects the API to either the AWS Lambda or Amazon DynamoDB resolvers that are also created by Amplify.
Once the back end is provisioned and your API is ready to use, the newly created APIs are called from the client code to initialize a new meeting with audio and video capabilities.
When the application is completed, you have the option of using Amplify to automatically provision both an Amazon Simple Storage Service (Amazon S3) bucket and Amazon CloudFront instance to host your server-less application. You can find the complete demo code in the Amazon Chime Amplify Demo Github Repository.
Demo Setup Walkthrough
Prerequisites
Please note, deploying and receiving traffic from the demos created in this walkthrough can incur AWS charges. For more information on Amazon Chime pricing, please refer to the Amazon Chime official documentation.
Before getting started, you must have the following:
- An AWS account
- Node.js v12+
- NPM 6.11 or higher
Key Steps
- Create a starter React application
- Initialize and configure Amplify in your project directory
- Create the application front end using the Amazon Chime SDK React Component Library
- Use Amplify to create a Lambda handler and a GraphQL-based API
- Call the GraphQL APIs from within your React application
- Optional: Use Amplify to host your application on S3 and CloudFront
Create a starter React application
- First, create a basic app using:
- Change directory into your newly created React app:
- Install dependencies:
- Next, install the Amazon Chime SDK React Component Library and peer dependencies at the root directory of your React application:
Install and Configure Amplify
- Install Amplify CLI onto your machine:
- Then, configure Amplify and create a new AWS Identity and Access Management (IAM) User:
- Now, initialize your project as a Amplify project by running:
Create the application front end using the Amazon Chime SDK React Component Library
-
-
-
- Remove all of the extraneous files in
./public
except for index.html - Replace
./public/index.html
with the following html starter code:<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Amazon Chime SDK Amplify Demo</title> <link rel="icon" href="data:,"> </head> <body> <div id="root"></div> </body> </html>
- Create a new directory
./src/components/
- Create the Meeting component in
src/components/Meeting.tsx
. This starter code shows how to use the ControlBar, AudioInputControl, VideoInputControl, and AudioOutputControl components from the Amazon Chime SDK React Library. Copy the following code intosrc/components/Meeting.tsx:
import React, { FC } from 'react'; import { AudioInputControl, AudioOutputControl, ControlBar, ControlBarButton, Phone, useMeetingManager, MeetingStatus, useMeetingStatus, VideoInputControl, VideoTileGrid } from 'amazon-chime-sdk-component-library-react'; import { endMeeting } from '../utils/api'; const Meeting: FC = () => { const meetingManager = useMeetingManager(); const meetingStatus = useMeetingStatus(); const clickedEndMeeting = async () => { const meetingId = meetingManager.meetingId; if (meetingId) { await endMeeting(meetingId); await meetingManager.leave(); } } return ( <div style={{marginTop: '2rem', height: '40rem', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center'}}> <VideoTileGrid/> {meetingStatus === MeetingStatus.Succeeded ? <ControlBar layout="undocked-horizontal" showLabels > <AudioInputControl /> <VideoInputControl /> <AudioOutputControl /> <ControlBarButton icon={<Phone />} onClick={clickedEndMeeting} label="End" /> </ControlBar> : <div/> } </div> ); }; export default Meeting;
- Remove all of the extraneous files in
-
-
- Create the MeetingForm component in
./src/components/MeetingForm.tsx
. This component is responsible for getting the meeting title, attendee name, and region. Submitting the form calls the API to create or join an existing meeting using the data in the form. The joinMeeting function placeholder will be replaced in a later step. Copy the following code into./src/components/MeetingForm.tsx
:import React, { ChangeEvent, FC, FormEvent, useState } from 'react'; import { Flex, FormField, Input, PrimaryButton, useMeetingManager, } from 'amazon-chime-sdk-component-library-react'; import { addAttendeeToDB, addMeetingToDB, createMeeting, getAttendeeFromDB, getMeetingFromDB, joinMeeting } from '../utils/api'; const MeetingForm: FC = () => { const meetingManager = useMeetingManager(); const [meetingTitle, setMeetingTitle] = useState(''); const [attendeeName, setName] = useState(''); function getAttendeeCallback() { return async (chimeAttendeeId: string, externalUserId?: string) => { const attendeeInfo: any = await getAttendeeFromDB(chimeAttendeeId); const attendeeData = attendeeInfo.data.getAttendee; return { name: attendeeData.name }; } } //Placeholder - we'll replace this function implementation later const clickedJoinMeeting = async (event: FormEvent) => { event.preventDefault(); }; return ( <form> <FormField field={Input} label='Meeting Id' value={meetingTitle} fieldProps={{ name: 'Meeting Id', placeholder: 'Enter a Meeting ID', }} onChange={(e: ChangeEvent<HTMLInputElement>) => { setMeetingTitle(e.target.value); }} /> <FormField field={Input} label="Name" value={attendeeName} fieldProps={{ name: 'Name', placeholder: 'Enter your Attendee Name' }} onChange={(e: ChangeEvent<HTMLInputElement>) => { setName(e.target.value); }} /> <Flex container layout="fill-space-centered" style={{ marginTop: '2.5rem' }} > <PrimaryButton label="Join Meeting" onClick={clickedJoinMeeting} /> </Flex> </form> ); }; export default MeetingForm;
- Remove all of the extraneous code in
./src
except foraws-exports.js
and./index.tsx.
- Configure Amplify on the client code so that we can call the GraphQL APIs. Replace
./index.tsx
with the following starter code:import ReactDOM from 'react-dom'; import { ThemeProvider } from 'styled-components'; import { MeetingProvider, lightTheme } from 'amazon-chime-sdk-component-library-react'; import Meeting from './components/Meeting'; import MeetingForm from './components/MeetingForm'; import Amplify from 'aws-amplify'; import awsconfig from './aws-exports'; Amplify.configure(awsconfig); window.addEventListener('load', () => { ReactDOM.render( <ThemeProvider theme={lightTheme}> <MeetingProvider> <MeetingForm /> <Meeting/> </MeetingProvider> </ThemeProvider> , document.getElementById('root')); });
Create back end resources for your React Application using Amplify
Amplify provides a CLI command to generate a predefined set of back end resources, referred to as a “category”. By adding the ‘function’ and ‘api’ categories to your Amplify application, Amplify automatically generates the required back end resources needed to run your server-less application. When you add categories and make updates to your back end configuration using the Amplify CLI, the configuration in ./aws-exports.js
will update automatically.
- Adding the ‘function’ category configures a Lambda function, which can resolve API requests. From the project’s root level, add the ‘function’ category using the Amplify CLI and answer the prompted questions as follows:
Replace */amplify/backend/function/reactSampleLambda/src/index.js
with the code provided in ./lambda/index.js. Then, go back to the terminal and press enter to finish the setup.
2. Adding the ’api’ category will create a GraphQL API in AWS AppSync. Add the ‘api’ category using the Amplify CLI and answer the prompted questions as follows:
When the editor opens, replace the schema file with the contents of the following schema:
type Meeting @model(mutations: {create: "createMeetingGraphQL", delete: "deleteMeetingGraphQL"}, subscriptions: null) @key(fields: ["title"]){
meetingId: String!
title: String!
data: String!
}
type Attendee @model(mutations: {create: "createAttendeeGraphQL", delete: "deleteAttendeeGraphQL"}, subscriptions: null) @key(fields: ["attendeeId"]){
attendeeId: String!
name: String!
}
type Query {
createChimeMeeting(title: String, name: String, region: String): Response @function(name: "reactSampleLambda-${env}")
joinChimeMeeting(meetingId: String, name: String): Response @function(name: "reactSampleLambda-${env}")
endChimeMeeting(meetingId: String): Response @function(name: "reactSampleLambda-${env}")
}
type Response {
statusCode: String!
headers: String
body: String
isBase64Encoded: String
}
3. Then, to push the changes to the cloud, run the following command – you may accept all of the defaults for the prompt:
4. Lastly, modify the IAM Role policy to include Amazon Chime Full Access to allow your Lambda function to call Amazon Chime APIs:
-
- Navigate to the AWS Console using the same account that you used to configure Amplify.
- Navigate to IAM
- Click on Roles in the side menu.
- Find the role created and click on the name of the Role – {projectName}LambdaRoleXXX-{environment}
- Click on the “Attach Policies” button
- Type in the search box: AmazonChimeFullAccess
- Click on the checkbox for: AmazonChimeFullAccess
- Click on “Attach Policy” at the bottom right of the screen
Connecting your front end to your back end using GraphQL
Once you have pushed to your AWS account, you can check out the generated GraphQL APIs inside the ./src/graphql/*
directories. You should see ./src/graphql/mutations.js
and ./src/graphql/queries.js.
These files contain the GraphQL APIs that have been generated by Amplify that you can now use within your application.
- Create a new directory
./src/utils/
- Create a new file called api.ts in
./src/utils/api.ts.
Copy the code below into the new file:
import { API, graphqlOperation } from 'aws-amplify';
import { createAttendeeGraphQL, createMeetingGraphQL, deleteMeetingGraphQL } from '../graphql/mutations';
import { createChimeMeeting, getAttendee, endChimeMeeting, getMeeting, joinChimeMeeting } from '../graphql/queries';
export async function createMeeting(title: string, attendeeName: string, region: string) {
const joinInfo: any = await API.graphql(graphqlOperation(createChimeMeeting, {title: title, name: attendeeName, region: region }));
const joinInfoJson = joinInfo.data.createChimeMeeting;
const joinInfoJsonParse = JSON.parse(joinInfoJson.body);
return joinInfoJsonParse;
}
export async function joinMeeting(meetingId: string, name: string) {
const joinInfo: any = await API.graphql(graphqlOperation(joinChimeMeeting, {meetingId: meetingId, name: name}));
const joinInfoJson = joinInfo.data.joinChimeMeeting;
const joinInfoJsonParse = JSON.parse(joinInfoJson.body);
return joinInfoJsonParse;
}
export async function endMeeting(meetingId: string) {
const endInfo: any = await API.graphql(graphqlOperation(endChimeMeeting, {meetingId: meetingId}));
const endInfoJson = endInfo.data.endChimeMeeting;
await API.graphql(graphqlOperation(deleteMeetingGraphQL, {input: {title: meetingId}}));
return endInfoJson;
}
export async function addMeetingToDB(title: string, meetingId: string, meetingData: string) {
await API.graphql(graphqlOperation(createMeetingGraphQL, {input: {title: title, meetingId: meetingId, data: meetingData, }}));
}
export async function addAttendeeToDB(attendeeID: string, attendeeName: string) {
await API.graphql(graphqlOperation(createAttendeeGraphQL, {input: {attendeeId: attendeeID, name: attendeeName }}));
}
export async function getMeetingFromDB(title: string) {
const meetingInfo = await API.graphql(graphqlOperation(getMeeting, {title: title }));
return meetingInfo;
}
export async function getAttendeeFromDB(attendeeId: string) {
const attendeeInfo = await API.graphql(graphqlOperation(getAttendee, {attendeeId: attendeeId }));
return attendeeInfo;
}
3. Open the ./src/components/MeetingForm.tsx
file. Copy this implementation of the getAttendeeCallback and joinMeeting function into the joinMeeting
method placeholder in ./src/components/MeetingForm.tsx
.
const clickedJoinMeeting = async (event: FormEvent) => {
event.preventDefault();
meetingManager.getAttendee = getAttendeeCallback();
const title = meetingTitle.trim().toLocaleLowerCase();
const name = attendeeName.trim();
// Fetch the Meeting via AWS AppSync - if it exists, then the meeting has already
// been created, and you just need to join it - you don't need to create a new meeting
const meetingResponse: any = await getMeetingFromDB(title);
const meetingJson = meetingResponse.data.getMeeting;
try {
if (meetingJson) {
const meetingData = JSON.parse(meetingJson.data);
const joinInfo = await joinMeeting(meetingData.MeetingId, name);
await addAttendeeToDB(joinInfo.Attendee.AttendeeId, name);
await meetingManager.join({
meetingInfo: meetingData,
attendeeInfo: joinInfo.Attendee
});
} else {
const joinInfo = await createMeeting(title, name, 'us-east-1');
await addMeetingToDB(title, joinInfo.Meeting.MeetingId, JSON.stringify(joinInfo.Meeting)); await addAttendeeToDB(joinInfo.Attendee.AttendeeId, name);
await meetingManager.join({
meetingInfo: joinInfo.Meeting,
attendeeInfo: joinInfo.Attendee
});
}
} catch (error) {
console.log(error);
}
// At this point you can let users setup their devices, or start the session immediately
await meetingManager.start();
};
4. From here, you can now run the meeting client locally. To do so, navigate to the repo’s root level directory and run this command:
Congratulations – you should now have successfully set up a full stack meeting application using the Amazon Chime SDK React Component Library and Amplify. After joining a meeting, press the video icon to turn on your local video tile.
Optional: Hosting your application using Amplify
You can use Amplify to set up a S3 bucket and CloudFormation stack to host your application in just a few minutes.
Add the hosting
category from the Amplify CLI:
Then, publish your changes to the cloud:
Once finished, navigate to the endpoint that is generated to try the server-less application.
Cleanup
To avoid incurring any unintended charges as a result of deploying the demo application, it is important to delete any resources in your AWS account that you are not using.
When you first called ‘amplify init,’ you created a local Amplify environment. You can have multiple environments on your AWS account, but calling ‘amplify remove <category>’ removes the corresponding resources in your local Amplify environment.
Removing the category in your local Amplify environment does not affect the resources created in the cloud. Once you’ve removed the category locally, remove the resources from your AWS account by publishing those changes to the cloud:
Alternatively, you can clean up your entire Amplify environment in the cloud and all local files created by Amplify by running:
Next Steps
In this blog post, we demonstrated how you can quickly set up the back end stack for a basic Amazon Chime meeting application built using the Amazon Chime SDK React Component Library. Refer to the documentation below for further information.
- Amazon Chime Amplify Demo Github Repository
- Amazon Amplify CLI Documentation
- Amazon Amplify Libraries Documentation
- Amazon Chime API Reference Documentation
- Amazon Chime Developer Guide
- Getting started with Amazon Chime SDK for JavaScript
- Getting started with Amazon Chime SDK React Components
- Amazon Chime SDK Components React Library Repository
- Amazon Chime JS SDK Repo