DEV Community

Cover image for Building a chat application using AWS AppSync and Serverless
We're Serverless! for Serverless Inc.

Posted on • Originally published at serverless.com

3 3

Building a chat application using AWS AppSync and Serverless

Originally posted at Serverless on November 19th, 2018

GraphQL gets a lot of praise for its expressiveness, for the idea of batching requests for data, and for its great development tooling. But there is an additional benefit that mostly goes unnoticed.

Namely-many frontend GraphQL frameworks make a distinction between the data in the app state and the data on a remote server. This is what allows React apps powered by GraphQL APIs to seem so fast, even if they are moving a lot of data: the moving of data happens in the background.

Users get from more responsive frontend apps, while also saving bandwidth. Developers can now model the data better, and deliver a more pleasant experience to the end user.

AppSync, AWS’s managed GraphQL layer, builds on the benefits of GraphQL and adds a few more cool things in its mobile and web SDKs: subscriptions, convenient authentication via Cognito Pools, and the ability to plug in directly to a bunch of AWS services for data.

AppSync can do a lot while still being a fully managed service, which works out great for Serverless applications. No more GraphQL resolvers in Lambda functions. No more hand-rolled authentication. It’s the best of GraphQL with less complexity than before.

In this article, we show how you can get started with AWS AppSync in a Serverless project, and talk about the benefits and drawbacks of using AppSync for your Serverless applications. Let’s get to it!

Building a chat app with AppSync

We broadly divided the process of getting a chat app running on Serverless with AWS AppSync into two parts: setting up the backend part of the service to fetch the data and deliver it via the GraphQL API, and creating a simple frontend to consume the API.

The backend

We start by defining how we will be using AppSync in our Serverless project. We are using the Serverless AppSync plugin to simplify the configuration, and all we need to provide, in addition to the authentication config, is:

  • A set of mapping templates that will help AppSync understand how to resolve each GraphQL you send out

  • A GraphQL schema that describes our API

  • A data source, in our case a DynamoDB database.

The AppSync section in our serverless.yml looks like this:

custom:
stage: dev
output:
file: ./front/src/stack.json
appSync:
name: ${self:service}-${self:custom.stage}
# ... boring authentication details.
mappingTemplates:
# Here we show AppSync how to resolve our GraphQL queries.
- dataSource: Messages
type: Mutation
field: createMessage
request: "createMessage-request-mapping-template.txt"
response: "createMessage-response-mapping-template.txt"
- dataSource: Messages
type: Query
field: getMessages
request: "getMessages-request-mapping-template.txt"
response: "getMessages-response-mapping-template.txt"
schema: schema.graphql
dataSources:
# Here we describe the DynamoDB table we’ll be using as the data source.
- type: AMAZON_DYNAMODB
name: Messages
description: Messages Table
config:
tableName: { Ref: MessagesTable }
serviceRoleArn: { Fn::GetAtt: [AppSyncDynamoDBServiceRole, Arn] }
iamRoleStatements:
- Effect: "Allow"
Action:
- "dynamodb:*"
Resource:
- "arn:aws:dynamodb:::table/Messages"
- "arn:aws:dynamodb:::table/Messages/*"
view raw .yml hosted with ❤ by GitHub

Our mapping templates for DynamoDB are almost an identical copy of the example from the AppSync docs, and allow us to get and create items in the Messages table. We place all mapping templates in the mapping-templates subdirectory.

For our GraphQL schema, we are starting simple, with only a few actions that are strictly necessary for a useful chat app:

  • A way to create a message — in this case, the createMessage mutation.

  • A way to get all messages — the getMessages query.

  • A subscription for all incoming messages, addMessage.

  • A description of the fields of the Message object — in this case, we want a message ID, the text of the message, the date it was posted, and the handle of the person who posted it.

With all those things our schema looks like this:

type Mutation {
createMessage(
body: String!
): Message!
}
type Query {
getMessages(filter: String): [Message!]!
}
type Subscription {
addMessage: Message
@aws_subscribe(mutations: ["createMessage"])
}
type Message {
messageId: String!
body: String!
createdAt: String!
handle: String!
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
view raw .graphql hosted with ❤ by GitHub

This is all we need on the backend side to get AppSync up and running. We can now deploy the service:
$ serverless deploy
view raw .sh hosted with ❤ by GitHub

And then watch all resources get created.

Frontend

On the frontend, we use the GraphQL operations and the Authentication module from AWS Amplify. The core of the app is the App.js file where we configure Amplify with all our authentication settings and point it to our GraphQL endpoint.

The whole user interface, in addition to the login / sign up screens provided by Amplify, consists of two components: MessagesList and SendMessage. We use react-chat-ui for the messages list:

// Components/MessagesList.js
import React from "react";
import { ChatFeed, Message } from "react-chat-ui";
export default ({ messages, username }) => (
<ChatFeed
maxHeight={window.innerHeight - 80}
messages={messages.map(
msg =>
new Message({
id: msg.handle === username ? 0 : msg.messageId,
senderName: msg.handle,
message: msg.body,
}),
)}
isTyping={false}
showSenderName
bubblesCentered={false}
/>
);
view raw .js hosted with ❤ by GitHub

We then create our own Send Message box that allows us to type in it and save the contents in the component’s state:
// Components/SendMessage.js
import React, { Component } from "react";
export default class extends Component {
state = {
body: "",
};
handleChange(name, ev) {
this.setState({ [name]: ev.target.value });
}
async submit(e) {
e.preventDefault();
await this.props.onCreate({ body: this.state.body });
this.message.value = "";
}
render() {
return (
<form onSubmit={e => this.submit(e)} style={{
...
}}>
<input
ref={m => {
this.message = m;
}}
name="body"
placeholder="body"
onChange={e => this.handleChange("body", e)}
className="message-input"
style={{
...
}}
/>
</form>
);
}
}
view raw .js hosted with ❤ by GitHub

We then use the two components in App.js. We use the Auth info that's coming from Amplify to get the username that we need to associate each sent message with. The getMessages subscription that we defined before plugs into the MessagesList component neatly, and the submit action from the SendMessage component triggers a GraphQL mutation that sends the message to the backend:
class App extends Component {
async componentDidMount() {
const { username } = await Auth.currentAuthenticatedUser();
this.setState({
username,
});
}
render() {
return (
<div
style={{
...
}}
>
<Connect
query={graphqlOperation(queries.getMessages)}
subscription={graphqlOperation(subscriptions.addMessage)}
onSubscriptionMsg={(prev, data) => ({
getMessages: [...prev.getMessages, data.addMessage],
})}
>
{({ data: { getMessages }, loading, error }) => {
if (error) return <h3>Error</h3>;
if (loading || !getMessages) return <h3>Loading...</h3>;
return (
<MessagesList
messages={getMessages}
username={this.state.username}
/>
);
}}
</Connect>
<Connect
mutation={graphqlOperation(mutations.createMessage)}
>
{({ mutation }) => <SendMessage onCreate={mutation} />}
</Connect>
</div>
);
}
}
view raw .js hosted with ❤ by GitHub

This is all for our frontend! Once we install all the dependencies we can run it via:
$ yarn run
view raw .sh hosted with ❤ by GitHub

We land on the authentication screen provided by AppSync, where we can pick a username and a password. We can then sign in and see the list of messages, send some messages, and get responses from other users:

Originally published at https://www.serverless.com.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Best practices for optimal infrastructure performance with Magento

Running a Magento store? Struggling with performance bottlenecks? Join us and get actionable insights and real-world strategies to keep your store fast and reliable.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️