Latest Blog Posts

library_booksRead more at Medium

arrow_back

Serverless React Web App with AWS Amplify — Part Two

Following on from my previous post, Serverless React Web App with AWS Amplify — Part One, today we’ll layout the Front End and implement our basic CRUD Functions.

All source code for the project can be found here.

Front End

To bootstrap the front end, we’ll use React Semantic UI, a simple UI Framework that provides a great set of React Components.

#bash
$ yarn add semantic-ui-react
[...]
$ yarn add semantic-ui-css

To get the CSS styling, you’ll need to import it in your index.js file, like so.

# index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import 'semantic-ui-css/semantic.css'
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();

Now let’s setup the folder structure. I like to keep screens in one folder and re-useable components in another. With that it mind, let’s make two new directories within the src directory.

#bash
$ mkdir src/components
$ mkdir src/screens
# Project Layout
├── README.md
├── awsmobilejs
│ ├── #current-backend-info
│ │ ├── aws-exports.js
│ │ ├── backend-details.json
│ │ ├── cloud-api
│ │ │ └── sampleLambda
│ │ │ ├── app.js
│ │ │ ├── lambda.js
│ │ │ ├── package-lock.json
│ │ │ └── package.json
│ │ └── mobile-hub-project.yml
│ └── backend
│ ├── cloud-api
│ │ └── sampleLambda
│ │ ├── app.js
│ │ ├── lambda.js
│ │ ├── package-lock.json
│ │ └── package.json
│ └── mobile-hub-project.yml
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── aws-exports.js
│ ├── components
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── registerServiceWorker.js
│ └── screens
└── yarn.lock

We’ll obviously need a few different views here, one to display all our items, one to create our items, one to edit/delete our items and one which shows a little more information about the item. We’ll use our App.js as the dashboard/index view for our app and add a navbar to navigate around the app.

# App.js
[...]
render() {
return (
<Segment>
<Menu>
<Menu.Item name='home'> <Icon name="shop"/></Menu.Item>
<Menu.Item name='Items'/>
<Menu.Item name='aboutUs' />
</Menu>
</Segment>
);
}
}

Now, let’s create an ItemDashboard page that shows all of our Item’s.

#bash
touch src/screens/itemDashboard.js
--------------------------------------------------------------------
#itemDashboard.js
import React, { Component } from 'react';
import {Container, Card} from 'semantic-ui-react'
class ItemDashboard extends Component {
render() {
return (
<div>
<Container style={{padding: 10}}>
<Card.Group>
<Card>
<Card.Content>
<Card.Header>
Item Name
</Card.Header>
<Card.Meta>
Item Price
</Card.Meta>
<Card.Description>
Description of the Item
</Card.Description>
</Card.Content>
</Card>
</Card.Group>
</Container>
</div>
);
}
}
export default ItemDashboard;

Now we’ll import this into our App.js and render it within our <Segment> section.

# App.js
[...]

render() {
return (
<Segment>
<Menu>
<Menu.Item name='home'> <Icon name="shop"/></Menu.Item>
<Menu.Item name='Items'/>
<Menu.Item name='aboutUs' />
</Menu>
<ItemDashboard />
</Segment>
);
}
}

At the moment our app should look nice and basic, something like this.

Basic Item Dashboard

Next, let’s implement a modal, which will serve as our create page.

We’ll use Semantic UI’s Modal and Form components to create the basic outline of our page. Let’s create a new file createItem.js which will live in our screens directory.

#bash
$ touch src/screens/createItem.js
--------------------------------------------------------------------
#createItem.js
import React, { Component } from 'react';
import { Form, Modal, Button, Container } from 'semantic-ui-react'
class CreateItemModal extends Component {
constructor(props) {
super(props)
this.state = {}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event, {name, value}) {
this.setState({ [name]: value });
}
handleSubmit(event) {
}
handleOpen = () => this.setState({ modalOpen: true })
handleClose = () => this.setState({ modalOpen: false })
render () {
return (
<Modal trigger={<Button onClick={this.handleOpen}>+ Add Item</Button>} closeIcon={true} open={this.state.modalOpen} onClose={this.handleClose}>
<Modal.Header>Add an Item</Modal.Header>
<Modal.Content>
<Form>
<Form.Group unstackable widths={2}>
<Form.Input name='itemName' label='Item Name' placeholder='Enter Item Name...' onChange={this.handleChange} />
<Form.Input name='itemPrice' label='Item Price' placeholder='Enter Item Price...' onChange={this.handleChange} type='number' />
</Form.Group>
<Form.TextArea name='item_description' label='Item Description' placeholder='Add a Description of the Item...' onChange={this.handleChange} />
<Form.Button type='submit'>Submit</Form.Button>
</Form>
</Modal.Content>
</Modal>
);
}
}
export default CreateItemModal;

Most of the above code is recycled from examples on Semantic’s website. Studying the code, you’ll notice I’ve also added two functions that handleChange and handleSubmit within our form. This ensures all data is captured and submitted correctly, we’ll revisit both of these later when we connect everything together.

One thing I should mention, that I encountered during my own development, is a funky bug with the modal where half of it would be cut, off screen. The issue is tracked here and the solution below worked perfectly, for me. Please do let me know if you run into any funky issues around the modal that aren’t resolved with the below.

Proposed Workaround for Modal Issue:

#App.css
.ui.page.modals.transition.visible {
display: flex !important;
}
# suggested by loopmode on GitHub Issues thread.

Make sure to import and add the component in our ItemDashboard.

#src/screens/itemDashboard.js
[...]
render() {
return (
<div>
<CreateItemModal/>
<Container style={{padding: 10}}>
<Card.Group>
<Card>
<Card.Content>
<Card.Header>
Item Name
</Card.Header>
<Card.Meta>
Item Price
</Card.Meta>
<Card.Description>
Description of the Item
</Card.Description>
</Card.Content>
</Card>
</Card.Group>
</Container>
</div>
);
[...]

The finished createItemModal should look something like the below.

Looking good! Now let’s hook everything up so that we can actually create items that display on our Dashboard.

Create

Now that we have a form we’ll need to populate the function handleSubmit we wrote in earlier.

Similar to our get request previously, we’ll have to set the apiName, path, and this time, the body of what we’re sending. We’ll then use Amplify’s API component to post and log the contents to the console.

#createItem.js
import Amplify, { API } from 'aws-amplify';
class CreateItemModal extends Component {
constructor(props) {
super(props)
this.state = {}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event, {name, value}) {
this.setState({ [name]: value });
}
handleSubmit(event) {
let apiName = 'sampleCloudApi';
let path = '/items';
let newItem = {
body: {
name: this.state.itemName, price: this.state.itemPrice, description: this.state.itemDescription
}
}
API.post(apiName, path, newItem).then(response => {
console.log(response)
}).catch(error => {
console.log(error.response)
});
event.preventDefault();
this.handleClose()
}
handleOpen = () => this.setState({ modalOpen: true })
handleClose = () => this.setState({ modalOpen: false })
render () {
return (
<Modal trigger={<Button onClick={this.handleOpen}>+ Add Item</Button>} closeIcon={true} open={this.state.modalOpen} onClose={this.handleClose}>
<Modal.Header>Add an Item</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Group unstackable widths={2}>
<Form.Input name='itemName' label='Item Name' placeholder='Enter Item Name...' onChange={this.handleChange} value={this.state.itemName} />
<Form.Input name='itemPrice' label='Item Price' placeholder='£0.00' onChange={this.handleChange} value={this.state.itemPrice} />
</Form.Group>
<Form.TextArea name='itemDescription' label='Item Description' placeholder='Add a Description of the Item...' onChange={this.handleChange} value={this.state.itemDescription} />
<Form.Button type='submit'>Submit</Form.Button>
</Form>
</Modal.Content>
</Modal>
);
}
}
export default CreateItemModal;

The handleSubmit function declares the variables necessary for posting to our backend. The body is formed of the values we set in each form element: itemName, itemPrice and itemDescription. The last thing we need to do is bind our function to our component so that it doesn’t show up undefined when we call our function.

Now let’s give the form a quick test to see if everything’s working.

After submitting the form, with some content obviously, you should see the contents in the console, like above.

Great! However, this isn’t saving anywhere, we still need to configure our Database.

DynamoDB Setup

Earlier on we setup a Database with the default example setup, we’re going to remove this and configure the database from scratch. DynamoDB requires us to specify a Primary Key, which determines how items within our Table are uniquely organised. There’s a lot of different schools of thought on what’s best to do here, which are way out of scope of this post. To keep things simple I’ll create a UUID, which will be generated when a user submits a new Item using the form.

#bash
$ awsmobile database configure
? Select from one of the choices below. Remove table from the project
? Select table to be deleted AWSMobileTable
? Are you sure you want to delete the table Yes
$ awsmobile database configure
Welcome to NoSQL database wizard
You will be asked a series of questions to help determine how to best construct your NoSQL database table.
? Should the data of this table be open or restricted by user? Open
? Table name ServerlessReactExample
You can now add columns to the table.
? What would you like to name this column ID
? Choose the data type string
? Would you like to add another column Yes
? What would you like to name this column ItemName
? Choose the data type string
? Would you like to add another column Yes
? What would you like to name this column ItemPrice
? Choose the data type number
? Would you like to add another column Yes
? What would you like to name this column ItemDescription
? Choose the data type string
? Would you like to add another column No
Before you create the database, you must specify how items in your table are uniquely organized. This is done by specifying a Primary key. The primary key uniquely identifies each item in the table, so that no two items can have the same key.
This could be and individual column or a combination that has "primary key" and a "sort key".
To learn more about primary key:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey
? Select primary key ID
? Select sort key (No Sort Key)
You can optionally add global secondary indexes for this table. These are useful when running queries defined by a different column than the primary key.
To learn more about indexes:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.SecondaryIndexes
? Add index No
$ awsmobile push
[...]
contents in #current-backend-info/ is synchronized with the latest in the aws cloud

Again, to keep things simple I’ve not added a sort key or an index, just the Primary key which is require for setting up the db.

Let’s quickly tweak our handleSubmit function to generate a uuid whenever we submit. In order to achieve this we’ll use node-uuid. There’s a few different versions you can generate, but we’ll use the uuidv1, which creates a timestamp uuid. This can be converted and tested on a site like this.

#bash 
$ yarn add node-uuid
------------------------------------------------------------------
#createItem.js
const uuidv1 = require('uuid/v1');
[...]
handleSubmit(event) {
console.log(this);
let apiName = 'sampleCloudApi';
let path = '/items';
let newItem = {
body: {
ID: uuidv1(), name: this.state.itemName, price: this.state.itemPrice, description: this.state.itemDescription
}
}
API.post(apiName, path, newItem).then(response => {
console.log(response)
}).catch(error => {
console.log(error.response)
});
event.preventDefault();
this.handleClose()
}
[...]

Lastly, we need to configure our cloud-api to use our newly created database.

#bash
awsmobile cloud-api configure
This feature will create an API using Amazon API Gateway and AWS Lambda. You can optionally have the lambda function perform CRUD operations against your Amazon DynamoDB table.
? Select from one of the choices below. Create CRUD API for an existing Amazon DynamoDB table
? Select Amazon DynamoDB table to connect to a CRUD API ServerlessReactExample
Adding lambda function code on:
/Users/jameshamann/Documents/Development/serverless-web-app-example/awsmobilejs/backend/cloud-api/rverlessReactExample/
...
Path to be used on API for get and remove an object should be like:
/ServerlessReactExample/object/:ID
Path to be used on API for list objects on get method should be like:
/ServerlessReactExample/:ID
JSON to be used as data on put request should be like:
{
"ID": "INSERT VALUE HERE",
"ItemDescription": "INSERT VALUE HERE",
"ItemName": "INSERT VALUE HERE",
"ItemPrice": "INSERT VALUE HERE"
}
To test the api from the command line (after awsmobile push) use this commands
awsmobile cloud-api invoke ServerlessReactExampleCRUD <method> <path> [init]
$ awsmobile push

This does change the name of your API, you’ll notice mine is now called ServerlessReactExampleCRUD, with a path of /ServerlessReactExample. So in order to get things to work, you’ll need to update the apiName and path in your code to reflect this.

Now let’s create a new item in our form and hit Submit. We still don’t see anything, but if we head over to the AWS DynamoDB Console, we’ll see our item has been successfully saved in our database!

Read

Now that we can successfully Create items, let’s work on Reading them in our dashboard. Instead of reading one Item at a time, I’m going to fetch all Items in our database and display them on our itemDashboard. To do this, we’ll need to slightly alter our Lambda code to include a function that returns all items from our DynamoDB Table.

DynamoDB provides us with a .scan method, which returns all items, similar to a SELECT * from tablename in SQL. Let’s start off by editing our app.js within our backend folder and include a route that retrieves all data.

#awsmobilejs/backend/yourLambdaFunctionName/app.js
[...]
app.get('/ServerlessReactExample', function(req, res) {
var params = {
TableName: tableName,
Select: 'ALL_ATTRIBUTES',
};
dynamodb.scan(params, (err, data) => {
if (err) {
res.json({error: 'Could not load items: ' + err.message});
}
res.json({
data: data.Items.map(item => {
return item;
})
});
});
});
[...]
-------------------------------------------------------------------
#bash
$ awsmobile push
[...]

Be sure to add the function just above the other routes and to push your changes to the aws cloud.

Once everything has been pushed and updated, reload your app and, if everything’s setup correctly, you should see all items from the database.

After refreshing

Let’s update our itemDashboard so that it correctly displays the data fetched from the database. We’ll iterate through our JSON response and for each item, we’ll display the name, price and description on a Card. We’ll require another library, lodash, which basically makes working with objects a lot easier.

#bash 
$ yarn add loadash
-------------------------------------------------------------------
#src/screens/itemDashboard.js
import React, { Component } from 'react';
import {Container, Card} from 'semantic-ui-react'
import Amplify, { API } from 'aws-amplify';
import _ from 'lodash';
let apiName = 'ServerlessReactExampleCRUD';
let path = '/ServerlessReactExample';
class ItemDashboard extends Component {
constructor(props){
super(props)
this.state = {itemData: {}}
}
getItems(){
API.get(apiName, path).then(response => {
console.log(response)
this.setState({
itemData: response.data
});
});
}
componentDidMount(){
this.getItems()
}
render() {
const itemData = this.state.itemData;
return (
<div>
<CreateItemModal/>        
<Container style={{padding: 10}}>
<Card.Group>
{_.map(itemData, ({ID, ItemName, ItemPrice, ItemDescription }) => (
<Card>
<Card.Content>
<Card.Header>
{ItemName}
</Card.Header>
<Card.Meta>
£ {ItemPrice}
</Card.Meta>
<Card.Description>
{ItemDescription}
</Card.Description>
</Card.Content>
</Card>
))}
</Card.Group>
</Container>
</div>
);
}
}
export default ItemDashboard;

We move our GET request into it’s own function, getItems() and call it during the componentDidMount function. Then, in our render function, we set itemData equal to this.state.itemData, which is set during our getItems() function. In the UI, we _.map over our itemData and for each Item we return a Card with the ItemName, ItemPrice and ItemDescription. After reloading your app, the result should look something like the below.

Before moving on, you’ll notice in order to see your results after a form submission you’ll need to refresh. Let’s fix that with a callback and set the state of this.state.itemData to get updated when we submit our form.

#src/screens/itemDashboard.js
[...]
render() {
const itemData = this.state.itemData;
return (
<div>
<CreateItemModal getItems={this.getItems}/>
<Container style={{padding: 10}}>
<Card.Group>
{_.map(itemData, ({ID, ItemName, ItemPrice, ItemDescription }) => (
<Card>
<Card.Content>
<Card.Header>
{ItemName}
</Card.Header>
<Card.Meta>
£ {ItemPrice}
</Card.Meta>
<Card.Description>
{ItemDescription}
</Card.Description>
</Card.Content>
</Card>
))}
</Card.Group>
</Container>
</div>
);
}
}
[...]
-------------------------------------------------------------------
#src/screens/createItem.js
[...]
handleSubmit(event) {
console.log(this);
let apiName = 'ServerlessReactExampleCRUD';
let path = '/ServerlessReactExample';
let newItem = {
body: {
"ID": uuidv1(),
"ItemName": this.state.itemName,
"ItemPrice": this.state.itemPrice,
"ItemDescription": this.state.itemDescription
}
}
API.post(apiName, path, newItem).then(response => {
console.log(response)
}).catch(error => {
console.log(error.response)
});
event.preventDefault();
this.props.getItems()
this.handleClose()
}
[...]

We pass the function down from ItemDashboard to createItem.

Update

Next, we’ll look at Updating our Items, if we want to change the price, for example.

Let’s create another modal, exactly the same as our createItem.js, but let’s call it editItem.js. Alot of the contents will be similar as it’s essentially the same form, just for editing an item.

#src/screens/editItem.js
import React, { Component } from 'react';
import { Form, Modal, Button, Container, Icon } from 'semantic-ui-react'
import Amplify, { API } from 'aws-amplify';
const uuidv1 = require('uuid/v1');
class EditItemModal extends Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = { item: this.props.item };
}
handleChange(event, {name, value}) {
this.setState({ [name]: value });
}
handleSubmit(event) {
let apiName = 'ServerlessReactExampleCRUD';
let path = '/ServerlessReactExample';
let editItem = {
body: {
"ID": this.props.item[0].ID,
"ItemName": this.state.itemName,
"ItemPrice": this.state.itemPrice,
"ItemDescription": this.state.itemDescription
}
}
API.put(apiName, path, editItem).then(response => {
console.log(response)
}).catch(error => {
console.log(error.response)
});
    this.props.getItems()
this.handleClose()
event.preventDefault();
}
handleOpen = () => this.setState({ modalOpen: true })
handleClose = () => this.setState({ modalOpen: false })
render () {
return (
<Container style={{padding: 10}}>
<Modal trigger={<Button icon onClick={this.handleOpen}><Icon name='edit' /></Button>}open={this.state.modalOpen} closeIcon
onClose={this.handleClose}>
<Modal.Header>Edit</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Group unstackable widths={2}>
<Form.Input name='itemName' label='Item Name' placeholder='Edit Item Name...' onChange={this.handleChange} value={this.state.itemName} />
<Form.Input name='itemPrice' label='Item Price' placeholder='£0.00' onChange={this.handleChange} value={this.state.itemPrice} />
</Form.Group>
<Form.TextArea name='itemDescription' label='Item Description' placeholder='Edit Description of the Item...' onChange={this.handleChange} value={this.state.itemDescription} />
<Form.Button type='submit'>Submit</Form.Button>
</Form>
</Modal.Content>
</Modal>
</Container>

);
}
}
export default EditItemModal;

Essentially this is the same code as what’s in createItem.js, the only difference is we’re receiving props we’ve sent from our parent component itemDashboard.js, in order to retrieve the item’s ID. This is important, as we require the ID in order to make sure we’re updating the correct item in our database.

To send the Item to our EditItemModal , we need to create a function that fetches the item. We’ll do this in our ItemDashboard and send the response to our EditItemModal through props.

#src/screens/ItemDashboard.js
[...]
import EditItemModal from './editItem.js'
[...]
getItem(id){
let single_path = '/ServerlessReactExample/' + id
console.log(single_path)
API.get(apiName, single_path).then(response => {
console.log(response)
this.setState({
item: response
})
});
}
[...]
return (
<div>
<Container style={{padding: 10}}>
<Card.Group>
{_.map(itemData, ({ID, ItemName, ItemPrice, ItemDescription }) => (
<Card onClick={() => this.getItem(ID)}>
<Card.Content>
<Card.Header>
{ItemName}
</Card.Header>
<Card.Meta>
£ {ItemPrice}
</Card.Meta>
<Card.Description>
{ItemDescription}
</Card.Description>
</Card.Content>
<EditItemModal item={Object.values(this.state.item) getItems={(this.getItems)} />
</Card>
))}
</Card.Group>
</Container>
</div>
);

Don’t forget to import the EditItemModal, as we’ll be rendering this within our dashboard on each card. Our function, getItem, takes an ID as it’s argument, the ID is then appended to the path used within our GET request, which returns all information on the requested Item. We then set the state of item equal to the response of our call and send that data through to our EditItemModal.

Once Complete, you should have a dashboard similar to this, with the ability to edit the item you’ve clicked on.

Awesome! Lastly, let’s quickly implement our last CRUD function, delete.

Delete

Implementing a delete function is fairly quick, as most of the work is already done. We’ll add the delete function to our EditItemModal, as it’s probably the best place for it.

#src/screens/editItem.js
[...]
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.deleteItem = this.deleteItem.bind(this);
this.state = { item: this.props.item };
}
deleteItem(){
let apiName = 'ServerlessReactExampleCRUD';
let path = "/ServerlessReactExample/object/" + this.props.item[0].ID
API.del(apiName, path).then(response => {
console.log(response)
}).catch(error => {
console.log(error.response)
});
this.props.getItems()
    this.handleClose()
}
[...]

Very simply, our function makes a delete request, using API component, with the ID appended to the path. The ID is required so the function know’s which Item to delete from our database. Don’t forget to bind our function to this, otherwise we’ll get undefined errors when trying to retrieve our ID.

Lastly, let’s render a Delete button on our Modal and link it all together.

#src/screens/editItem.js
return (
<Container style={{padding: 10}}>
<Modal trigger={<Button icon onClick={this.handleOpen}><Icon name='edit' /></Button>} open={this.state.modalOpen} closeIcon onClose={this.handleClose}>
<Modal.Header>Edit</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Group unstackable widths={2}>
<Form.Input name='itemName' label='Item Name' placeholder='Edit Item Name...' onChange={this.handleChange} value={this.state.itemName} />
<Form.Input name='itemPrice' label='Item Price' placeholder='£0.00' onChange={this.handleChange} value={this.state.itemPrice} />
</Form.Group>
<Form.TextArea name='itemDescription' label='Item Description' placeholder='Edit Description of the Item...' onChange={this.handleChange} value={this.state.itemDescription} />
<Form.Button type='submit'>Submit</Form.Button>
</Form>
</Modal.Content>
<Modal.Actions>
<Button icon labelPosition='left' onClick={this.deleteItem}>
<Icon name='delete' />
Delete Item
</Button>
</Modal.Actions>

</Modal>
</Container>

Once completed, your EditItemModal should look like this. When clicking delete, and after a refresh, you’ll notice the item has been removed.

This has been quite a long post, so well done if you’ve made it this far! You now have a, real basic, Serverless React Web App! There are still a lot of things that need to be cleaned up and refactored, like perhaps adding the Item Title to the EditItemModal, so we know what we’re editing and adding maybe adding some validation to our forms? There’s a lot of scope for improvement and enhancement here, but this gives you a solid foundation to move forward.

The next, and final, part on this mini series will be focused around Adding Authentication, with Amplify, as well as the Deployment and Distribution of your app through AWS.

All source code for the project can be found here.

As always, thanks for reading, hit 👏 if you like what you read and be sure to follow to keep up to date with future posts.

Serverless React Web App with AWS Amplify — Part One

Having previously posted about Accelerating Mobile Development with AWS Amplify, I thought I’d do the same for the Web, using AWS Amplify, Amazon’s new JavaScript Library for app development.

All source code for the project can be found here.

Before diving in, it’s probably worth understanding, at least at a high level, the architecture behind serverless apps and how they work.

What is a Serverless Application?

High Level Serverless Application Model

This diagram represents, at a high level, the architecture of a serverless application. Our static content (React Web App) is stored in an S3 bucket served up to the client, either from CloudFront or directly. This communicates with API Gateway. From here this triggers a Lambda function (which handles all our back end logic) and communicates with DynamoDB to get, save, delete or whatever depending on what request was sent from the client.

What are the benefits?

Cost. You only pay for the compute time you use. This works great if you have large fluctuations in traffic/requests. It also takes the hassle of maintaining a server away and some what simplifies things so you can focus on building your product. Obviously, every app has it’s own requirements and serverless architecture may not fit in all cases, but for the most part it provides a good framework to deploy applications at low cost, with minimal configuration.

What we’ll be building

To keep things nice and simple, and to avoid the stale recycled todo example, we’ll build an online inventory with basic CRUD functions. I’m sure it goes without saying but you’d need an AWS Account, which you can get get here. I’d also advise you to keep an eye on your billing statement so you don’t incur any unexpected charges.

Getting Started

Firstly, we’ll need to install the awsmobile-cli and configure it with our AWS Keys.

#bash
$ npm install -g awsmobile-cli
[...]

$ awsmobile configure
$ configure aws
? accessKeyId: [ENTER YOUR ACCESS KEY ID] #hit enter
? secretAccessKey: [ENTER YOUR SECRET ACCESS KEY] #hit enter
? region: eu-west-2 #select your region using arrows and hit enter

Next, we’ll use create-react-app to scaffold up a React App for us.

#bash 
$ create-react-app serverless-web-app-example
[...]
✨ Done in 17.74s.
Success! Created serverless-web-app-example at /Users/jameshamann/Documents/Development/serverless-web-app-example
Inside that directory, you can run several commands:
yarn start
Starts the development server.
yarn build
Bundles the app into static files for production.
yarn test
Starts the test runner.
yarn eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
cd serverless-web-app-example
yarn start
Happy hacking!
$ cd serverless-web-app-example

Let’s fire up our app to make sure it’s all setup correctly.

React Starter Page

Great, looks good! Now lets install aws-amplify and aws-amplify-react (which just contains helpers and higher order components for react) within our project.

#bash 
$ npm install aws-amplify --save 
[...]
$ npm install aws-amplify-react --save
[...]

Once this is all installed, we’ll need to setup our backend. To initialise a project, we use the awsmobile init command within the root of our project. You’ll be prompted a few questions, usually the default answers provided are correct so you should be able to hit enter for each of them.

#bash 
$ awsmobile init
[...]
✨  Done in 5.30s.
yarn add aws-amplify-react returned 0
Success! your project is now initialized with awsmobilejs
awsmobilejs/.awsmobile
is the workspace of awsmobile-cli, please do not modify its contents
awsmobilejs/#current-backend-info
contains information of the backend awsmobile project from the last
synchronization with the cloud
awsmobilejs/backend
is where you develop the codebase of the backend awsmobile project
awsmobile console
opens the web console of the backend awsmobile project
awsmobile run
pushes the latest development of the backend awsmobile project to the cloud,
and runs the frontend application locally
awsmobile publish
pushes the latest development of the backend awsmobile project to the cloud,
and publishes the frontend application to aws S3 for hosting
Happy coding with awsmobile!

This command creates all the necessary resources in AWS for your backend, as well as creating a awsmobilejs folder within the root of your project, which contains basic information about your project.

Lastly, we’ll need to hook up our client (React app) to our newly created backend. In your app’s entry point (usually App.js) include the bolded code in the snippet. This just imports the Amplify Library and configures it using a file called aws_exports which is generated when you initialise your backend, in the previous step.

# App.js 
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Amplify from 'aws-amplify';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;

Lambda and API Setup

Now we’ve got the basic infrastructure setup, let’s setup Lambda, API Gateway and DynamoDB for our basic CRUD functions. To do this, run the awsmobile features command. From here, you can see what features you’re able to activate and what features are available as default. For now we only require cloud-api and database to be selected and activated.

#bash 
awsmobile features
? select features:
◯ user-signin
◯ user-files
◉ cloud-api
❯◉ database
◉ analytics
◉ hosting
# hit space to select the features you'd like and enter to confirm

Whenever you change something locally, as we’ve just done, we’ll need to push it to AWS so the changes can take effect.

#bash
$ awsmobile push

Once everything’s been initialised and pushed, have a poke around the backend directory, within awsmobilejs. There should be a cloud-api directory that contains your Lambda Project and a bunch of boilerplate code to get you started. By default, Lambda is setup to use AWS’s Serverless Express Framework for Node.js, so if you’re experienced with Node.js and Express, everything should look pretty familiar.

At this point, I like to do a quick check to make sure the Client and Backend are setup and talking to each other. To do this, I write a get request within the componentDidMount function, so as soon as our Component mounts, it fetches data from our backend and logs it to the console. AWS Amplify provides a component, API, to handle all requests to our backend.

# App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Amplify, { API } from 'aws-amplify';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
let apiName = 'sampleCloudApi';
let path = '/items';
class App extends Component {
componentDidMount(){
API.get(apiName, path).then(response => {
console.log(response)
});
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;

If you’ve poked around the Lambda app.js file, you’ll notice we should be expecting a response of ({success: ‘get call succeed!’, url: req.url}).

Ok, you might need to squint, but if you check the console you’ll see our request object {success: “get call succeeded!”, url: /items”}. Great! Everything’s hooked up and ready to go.

In the next part we’ll look at setting our Front End Pages up and creating the basic CRUD Functions.

As always, thanks for reading, hit 👏 if you like what you read and be sure to follow to keep up to date with future posts.

Serverless React Web App with AWS Amplify — Part Two Available Here

This story is published in Noteworthy, where thousands come every day to learn about the people & ideas shaping the products we love.

Follow our publication to see more product & design stories featured by the Journal team.


Serverless React Web App with AWS Amplify — Part One was originally published in Noteworthy — The Journal Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Accelerating Mobile App Development with AWS Amplify

Over the last couple of months AWS have released a new Library, AWS Amplify, both for Web Development and React Native Mobile Development. The purpose is to provide a declarative and easy-to-use interface across different categories of cloud operations. If you’re already using AWS Services, it plugs directly in, but it also designed in such a way to plug in with any other backed/cloud service that you use.

Having used it on a few projects myself, it made key areas of app development a breeze and accelerates the development of an app greatly. Services include Authentication through AWS Cognito, No-SQL Database’s through DynamoDB and hosting through S3 and CloudFont. It connects everything so easily and quickly that it gives you the time to focus on actually developing the app.

I’ll briefly walkthrough the process of scaffolding a simple awsmobile project, to demonstrate how easy and quick it is to get going. I’ll assume you have the basics, like node, react-native-cli or create-react-native-cli installed already.

Obviously, first things first, make sure you’ve got aws and awsmobile CLI (npm install awsmobile-cli — global) installed and ready to go, you’ll also want to make sure you’ve got your AWS_ACCESS_KEY and AWS_SECRET_KEY to hand.

Let’s setup our CLI to ensure it can connect to AWS.

#bash 
$ awsmobile configure
$ configure aws
? accessKeyId: [ENTER YOUR ACCESS KEY ID] #hit enter
? secretAccessKey: [ENTER YOUR SECRET ACCESS KEY] #hit enter
? region: eu-west-2 #select your region using arrows and hit enter

Now you’re all setup and connected to AWS let’s setup your app.

Setup

I’ll leave it up to you to decide whether you choose to use create-react-native-app or react-native-init, both have there advantages and disadvantages that are beyond the spec of this post. I’ll be using react-native init.

#bash 
react-native init awsMobileApp
This will walk you through creating a new React Native project in /Users/jameshamann/Documents/Development/awsMobileApp
Using yarn v1.3.2
Installing react-native...
[...]
✨ Done in 6.10s.
To run your app on iOS:
cd /Users/jameshamann/Documents/Development/awsMobileApp
react-native run-ios
- or -
Open ios/awsMobileApp.xcodeproj in Xcode
Hit the Run button
To run your app on Android:
cd /Users/jameshamann/Documents/Development/awsMobileApp
Have an Android emulator running (quickest way to get started), or a device connected
react-native run-android

Next you’ll probably want to set up your Github repo and push everything up. Then install both AWS Amplify and AWS Amplify React Native.

#bash 
$ npm install aws-amplify --save
$ npm install aws-amplify-react-native --save

Now we’ll initialise a awsmobile project, which will setup a backend for our app and connect it to AWSMobile hub.

#bash
awsmobile init
Please tell us about your project:
? Where is your project's source directory: /
? Where is your project's distribution directory that stores build artifacts: /
? What is your project's build command: npm run-script build
? What is your project's start command for local test run: npm run-script start
? What awsmobile project name would you like to use:  awsMobileAppExample
Successfully created AWS Mobile Hub project: awsMobileAppExample
retrieving the latest backend awsmobile project information
awsmobile project's details logged at: awsmobilejs/#current-backend-info/backend-details.json
awsmobile project's access information logged at: awsmobilejs/#current-backend-info/aws-exports.js
awsmobile project's access information copied to: aws-exports.js
awsmobile project's specifications logged at: awsmobilejs/#current-backend-info/mobile-hub-project.yml
contents in #current-backend-info/ is synchronized with the latest in the aws cloud
Executing yarn add aws-amplify ...
[...]
Success! your project is now initialized with awsmobilejs
awsmobilejs/.awsmobile
is the workspace of awsmobile-cli, please do not modify its contents
awsmobilejs/#current-backend-info
contains information of the backend awsmobile project from the last
synchronization with the cloud
awsmobilejs/backend
is where you develop the codebase of the backend awsmobile project
awsmobile console
opens the web console of the backend awsmobile project
awsmobile run
pushes the latest development of the backend awsmobile project to the cloud,
and runs the frontend application locally
awsmobile publish
pushes the latest development of the backend awsmobile project to the cloud,
and publishes the frontend application to aws S3 for hosting
Happy coding with awsmobile!

That simple command pretty much sets everything up for you within a awsmobilejs directory within your app. If you head over to the AWS console, you should see your project, like this.

AWS Mobile Hub

AWS Mobile includes the main features you’d need for your mobile app, and there’s obviously plenty of other AWS Services you can tie into your mobile app. The ones that come bundled in can be found by using the awsmobile command.

#bash
$ awsmobile features
? select features:  (Press <space> to select, <a> to toggle all, <i> to inverse selection)
❯◯ user-signin
◯ user-files
◯ cloud-api
◯ database
◉ analytics
◉ hosting
[...]
enabled: user-signin
backend awsmobile project enabled features:
analytics, hosting, user-signin

From here you can select whatever feature you’d like to add, for arguments sake let’s add authentication.

Next, we’ll use AWS Amplify’s component withAuthenticator.

Let’s edit our App.js to include the component and then wrap our entire app around it. The main thing we’ll need to edit is changing the class from export default class App extends Component to Class App extends Component and then export the App at the bottom of the file as an argument in our withAuthenticator component. We’ll also need to

#javascript - App.js
[...]
import Amplify from 'aws-amplify';
import { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
[...]
class App extends Component<Props> { 
[...]
}
export default withAuthenticator(App);

Now let’s boot our app up in an emulator. It should look similar to the below, with all the basics setup, including routing.

Authentication Front End Setup

When trying to sign up, however, you’ll notice there’s an error No userPool. That’s because we haven’t synced our backend with the cloud yet.

#bash 
$awsmobile push
[...]

Once this is complete, reboot your app on the emulator and try signing up. Boom, you’ve got a full authentication system ready to go, built in with SMS confirmations. (please check AWS Billing/Charges to make sure you don’t incur any unwanted charges). Once a user Signs up, they’ll be visible in your AWS Cognito Pool.

It’s super simple to get setup and going with AWS, plus with the addition of AWS Amplify, it’s even easier to connect essential AWS Services to your app.

As always, thanks for reading, hit 👏 if you like what you read and be sure to follow to keep up to date with future posts.

A brief guide to Semantic Versioning

Whenever releasing a product or app, you’ll want to version it. This helps users understand what stage the app is at and also helps when integrating the app into package managers. In Software Development, we version apps by following Semantic Versioning, which is a general template that everyone uses and understands.

Why bother?

Versioning our apps helps us keep track of what’s been added/removed at what point. It can get real hectic, real fast, especially in the early stages when you’re releasing new features and fixing bugs. You’re gonna want to reference things that have been added or things that have been fixed.

Where would I keep a track of this?

You can tag your releases on Github using git tag. Essentially, you just tag a version number to a specific commit, like a merge, push it and this appears on the repository under “releases”, with any notes you’d like to add. It’s best to keep track of all these changes in a CHANGELOG, there’s a nice format to follow outlined here. This way you can keep track of what’s been fixed and any upcoming features that are being released into the next version. It’s also important to remember, software is built for people. So people need to actually understand what’s going on and at what stage your apps currently at, having a well documented CHANGELOG helps provide a clear timeline and explanation of your app.

Ok, so how do I go about versioning my app/package?

0 — Pre-MVP/Alpha

At this stage, you’re developing your app and still building towards an MVP. There’s no real point differentating between versions here because it’s not a useable product yet. During this period, the version will remain at 0.

Major.Minor.Patch — Alpha/Beta

Once you reach your MVP, you might iterate over this and create Alpha/Beta releases for users to test and supply feedback on. When creating your first release, you’ll want to start versioning. This helps you keep track of your app and creates a timeline of what features were added when and what bugs have and have not been fixed yet. You might be wondering, what exactly constitutes a major, minor or patch release.

Major Release — Increment the first digit by 1 if the new features break backwards compatibility/current features

Minor Release — Increment the middle digit by 1 if the new features don’t break any existing features and are compatible with the app in it’s current state.

Patch Release — Increment the last digit by 1 if you’re publishing bug/patch fixes to your app.

Before publishing your first, useable version, you might find yourself incrementing the middle and the last digit to keep track of Alpha/Beta releases. Only once you’re ready for a proper, first release, should you start versioning from 1.0.0.

I can’t stress enough the benefits of keeping a clean, well documented CHANGELOG. Regardless of whether your project is open source or not, it’s vital to keep track of what’s been worked on. It helps guide you when deciding what features need work and gives you good insight into your release cycle.

If you’re interested in reading more, you can get the full breakdown of Semantic Versioning on their site.

As always, thanks for reading, hit 👏 if you like what you read and be sure to follow to keep up to date with future posts.

Hi Ezequiel Frescó Werchowsky, thanks for reading and getting in touch!

Hi Ezequiel Frescó Werchowsky, thanks for reading and getting in touch! Apologies, it seems like I might’ve done, thank you for letting me know, I’ll update the post with the changes.

Regarding your error, it looks like it’s an issue with AWS and not being able to read your credentials. Have you downloaded the AWS CLI (https://docs.aws.amazon.com/cli/latest/userguide/installing.html), if so run the command: aws configure. This will prompt you to enter you AWS_ACCESS_KEY and your AWS_SECRET_ACCESS_KEY, which can be retrieved from the AWS console, under “My Secruity Credentials”, on the top right dropdown by your username.

Try the above and let me know if you run into any other issues, I’m happy to help!

Thanks, James

Ryan Pereira Thanks for reading and getting in touch!

Ryan Pereira Thanks for reading and getting in touch!

Here, I’m explaining that you should setup a brand new jekyll site and add your Gem, that you’ve just built, to the Gemfile and running bundle install. When adding your gem, you need to specify it’s path, like this: gem “YOURTHEME” => :path => “path/to/your/gem”.

Looking at the error from your previous comment, I suggest running gem install bundler and gem install jekyll again, looks like maybe an issue with the Ruby Version. In which case it might be best to upgrade to 2.4 +. To upgrade your ruby version, you can install RVM (ruby version manager).

This is only applicable if you’re looking to build your own Gem Theme and publish it. If you wish to use an existing theme, you can use the one I created (jekyll-material-theme) and customise it or grab one from http://jekyllthemes.org/.

Hope this helps a little! Let me know if you need anything else.

Thanks, James

Configuring your Elastic Beanstalk App for SSL

It’s always a good idea to add a SSL certificate. It gives people piece of mind when visiting your site that information isn’t being accessed by third-parties and also boosts your SEO ranking in Google. Setting your Elastic Beanstalk app up for SSL isn’t too difficult and requires just a few simple steps.

Getting Started

I’m going to assume you have a domain already registered, either living in Route 53 or another domain provider. To start with, if you haven’t done so already, you’ll need to point your domain to your EB app. This can be done by creating an Alias A Record and setting it’s value equal to your EB App’s URL. This can be found on the Elastic Beanstalk Dashboard.

Elastic Beanstalk Management Console

In this example, it’s http://ssl-example.eu-west-2.elasticbeanstalk.com. When adding this to your domain, ensure the A record is set to Yes for Alias. The value will then be your app’s Elastic Beanstalk URL.

Route 53 Configuration for an A Record

Now, if you visit your domain, in my case sslexample.jameshamann.com, you’ll see your app! You can try to type https://yourdomain.com but as there’s no certificate, the request will timeout. So let’s add a certificate!

Configure your App

In order to use a SSL certificate for your Elastic Beanstalk App, you’ll need to change the configuration of your app to use Load Balancers as opposed to a single instance. This can cost more, so please check your billing dashboard to ensure you’re not going over budget or anything.

What are Application Load Balancers?

In essence, instead of running a single instance, a load balancer distributes traffic across multiple targets, instances, across multiple availability zones, which boosts availability of your app.

In our example the SSL certificate is applied to the load balancer, so connections between the Client and Load Balancer are secure and encrypted.

In order to configure your app, head to the Configuration tab of your Elastic Beanstalk dashboard and click the modify link on the Capacity card.

Configuration Tab of Elastic Beanstalk App — Capacity card located at the top right

Once here, the only thing I advise you to change is the maximum number of instances, from 4 to 1, however that’s up to you.

Don’t change anything else, just hit Save.

Adding a Load Balancer

This will then take you back to the configuration page, where you’ll need to hit Apply for your changes to take place. As your app will be unavailable for a short period whilst the changes take place, you’ll need to confirm again after hitting Apply.

Creating an SSL Certificate with ACM

Now we need to actually create our certificate. As we’re using Elastic Beanstalk, it makes sense to create a certificate in ACM (Amazon Certificate Manager).

In my case, I opted for a wildcard certificate for the domain jameshamann.com. This means all alias domains, i.e sslexample.jameshamann.com will be covered by the same SSL certificate. To do this, head over to ACM and request a certificate. Type in your domain, if you wish to setup a wild card add a * to the beginning of your domain.

AWS Certificate Manager Requesting a Certificate

You’ll have two options to validate that you’re the owner of the domain. DNS or Email. I chose DNS, but whatever you choose, just make sure you have access to the correct email domains if choosing email.

DNS Config for domain

To verify via DNS, you’ll need to add a CNAME record with whatever values are generated in your DNS_Configuration.csv file.

Enter the Name value from the .csv file and the Value from the .csv file and hit create.

This will take a little time to verify, but once done, your certificate should move from Pending to Issued.

Bringing it all together

Lastly, we need to apply our newly created SSL Certificate to our App’s Load Balancer. To do this, navigate to the Configuration Tab of your Elastic Beanstalk App. There should be a new card labelled Load Balancer.

Click modify on the Load Balancer card

In order to add the certificate, we’ll need to open up port 443 (SSL Port) and assign our certificate.

If your certificate doesn’t appear in the dropdown, try refreshing and waiting a bit. Once it does, choose it and hit save. Again, you’ll be directed back to the Configuration page where you’ll have to Apply your changes.

Once completeled, navigate to https://yourdomain.com and you should see your site served through HTTPS.

Site Secured!

As always, thanks for reading, hit 👏 if you like what you read and be sure to follow to keep up to date with future posts.

Node.js RESTful API with DynamoDB Local

Node is usually used along side MongoDB in the MEAN stack. However, using Amazon’s DynamoDB has it’s own benefits, not least from speed, scalability, affordability and freeing your time up from configuring database clusters/updates. Through this post I’ll discuss how to setup DynamoDB with your Node project locally.

Versions

Setting up your Node Project

To get things moving quickly, we’ll use the express generator to scaffold a project for us.

#bash
$ express node-dynamo-db

create : node-dynamo-db
create : node-dynamo-db/package.json
create : node-dynamo-db/app.js
create : node-dynamo-db/public
create : node-dynamo-db/routes
create : node-dynamo-db/routes/index.js
create : node-dynamo-db/routes/users.js
create : node-dynamo-db/views
create : node-dynamo-db/views/index.jade
create : node-dynamo-db/views/layout.jade
create : node-dynamo-db/views/error.jade
create : node-dynamo-db/bin
create : node-dynamo-db/bin/www
create : node-dynamo-db/public/javascripts
create : node-dynamo-db/public/images
create : node-dynamo-db/public/stylesheets
create : node-dynamo-db/public/stylesheets/style.css
install dependencies:
$ cd node-dynamo-db && npm install
run the app:
$ DEBUG=node-dynamo-db:* npm start
$ cd node-dynamo-db
$ npm install

Fire up your server to ensure it’s all working as intended.

$ npm start

Navigate to http://localhost:3000 and you’ll see the welcome page from express, like below.

Generic Express Welcome Page

Next, as there’s no live-reloading, we’ll install Nodemon to watch our files and whenever a change is made, it’ll restart the server for us. Without Nodemon, you’re gonna get frustrated real fast. Once installed, we’ll update our start command within the package.json to run the nodemon command as opposed to node.

#bash 
$ npm install -g nodemon
--------------------------------------------------------------------
#package.json 
{
"name": "node-dynamo-db",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "nodemon ./bin/www"
},
"dependencies": {
"body-parser": "~1.18.2",
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.15.5",
"jade": "~1.11.0",
"morgan": "~1.9.0",
"serve-favicon": "~2.4.5"
}
}

Setting up DynamoDB

First download the file from the link above, unpack it and navigate into the directory. You’ll notice DynamoDB is provided as an executable .jar file. In order to start the database up, we need to run the following command within the directory the .jar file is located.

#bash 
$ java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
Initializing DynamoDB Local with the following configuration:
Port: 8000
InMemory: false
DbPath: null
SharedDb: true
shouldDelayTransientStatuses: false
CorsParams: *

Boom, you’ve got a local instance of DynamoDB running! Problem is, unless you’re gifted with photographic memory, you’re probably not going to rememeber the above command and even if you do, it’s ballache to write out each time. Lets speed things up and create an alias command within our .bashrc or .zshrc, depending on what you use. Mine looks like this.

#bash .zshrc or .bashrc
alias ddb="cd path/to/dynamodb_local_latest && java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb"

I’ve named my alias ddb, it navigates to the directory and then executes the .jar, simple as that. Now when reloading my terminal window and running ddb, DynamoDB should spin up.

#bash
$ ddb
Initializing DynamoDB Local with the following configuration:
Port: 8000
InMemory: false
DbPath: null
SharedDb: true
shouldDelayTransientStatuses: false
CorsParams: *

Now we’re all set to start creating our table and to begin seeding some data into our table. For the purpose of this demo, I’ll be making a database revolving around cars.

Before moving forward, let’s just update our package.json to automate some of the commands we’ll be running fairly frequently.

{
"name": "crafty-api",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "nodemon app.js",
"create-db": "cd dynamodb && node createCarsTable.js && cd ..",
"delete-db": "cd dynamodb && node deleteCarsTable.js && cd ..",
"load-data": "cd dynamodb && node loadCarData.js && cd ..",
"read-data": "cd dynamodb && node readDataTest.js && cd .."
},
"dependencies": {
"aws-sdk": "^2.176.0",
"body-parser": "~1.18.2",
"cookie-parser": "~1.4.3",
"cors": "^2.8.4",
"debug": "~2.6.9",
"ejs": "^2.5.7",
"express": "~4.15.5",
"jade": "~1.11.0",
"morgan": "~1.9.0",
"newman": "^3.9.1",
"node-uuid": "^1.4.8",
"serve-favicon": "~2.4.5",
"uuid": "^3.2.1"
}
}

This is what my current one looks like and it just speeds things up so much, so consider adding your own to speed up your workflow.

First things first, we’re gonna need to create a table and choose a partition key. Amazon provided pretty good advice here on what constitutes as a good key. Reason we need a key is because Dynamo DB partitions our data across multiple storage units and uses that key to both store and read the data. Therefore, the partition key must be a unique value. Good examples are user_ids and devices_ids.

For my table I’ve chosen car_id.

#JavaScript - createCarsTable.js
var AWS = require("aws-sdk");
AWS.config.update({
region: "eu-west-2",
endpoint: "http://localhost:8000"
});
var dynamodb = new AWS.DynamoDB();
var params = {
TableName : "Cars",
KeySchema: [
{ AttributeName: "id", KeyType: "HASH"}, //Partition key
],
AttributeDefinitions: [
{ AttributeName: "id", AttributeType: "N" },
],
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5
}
};
dynamodb.createTable(params, function(err, data) {
if (err) {
console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Created table. Table description JSON:", JSON.stringify(data, null, 2));
}
});

Now run you’re create-db command, making sure Dynamo DB is running in the background on another terminal window, on port 8000.

#bash
yarn create-db
yarn run v1.3.2
$ cd dynamodb && node createCarsTable.js && cd ..
Created table. Table description JSON: {
"TableDescription": {
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "N"
}
],
"TableName": "Cars",
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
"TableStatus": "ACTIVE",
"CreationDateTime": "2018-02-01T16:08:25.308Z",
"ProvisionedThroughput": {
"LastIncreaseDateTime": "1970-01-01T00:00:00.000Z",
"LastDecreaseDateTime": "1970-01-01T00:00:00.000Z",
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5
},
"TableSizeBytes": 0,
"ItemCount": 0,
"TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/Cars"
}
}
✨ Done in 0.47s.

Now you’re table is setup and ready to seed data into.

In this example, we’re using Dynamo DB’s PutItem method to seed some data into our Database.

#JSON - carData.json 
[
{ "id": 1,
"type" : "Automatic",
"name" : "Toyota Yaris",
"manufacturer" : "Toyota",
"fuel_type" : "Petrol",
"description" : "A smooth ride"
},
{ "id": 2,
"type" : "Manual",
"name" : "Volkswagen Golf",
"manufacturer" : "Volkswagen",
"fuel_type" : "Petrol",
"description" : "Good Value"
}
]
------------------------------------------------------------------
#JavaScript - loadCarData.js
var AWS = require("aws-sdk");
var fs = require('fs');
AWS.config.update({
region: "eu-west-2",
endpoint: "http://localhost:8000"
});
var docClient = new AWS.DynamoDB.DocumentClient();
console.log("Importing Cars into DynamoDB. Please wait.");
var cars = JSON.parse(fs.readFileSync('carData.json', 'utf8'));
cars.forEach(function(car) {
console.log(car)
var params = {
TableName: "Cars",
Item: {
"id": car.id,
"type": car.type,
"name": car.name,
"manufacturer": car.manufacturer,
"fuel_type": car.fuel_type,
"description": car.description
}
};
docClient.put(params, function(err, data) {
if (err) {
console.error("Unable to add Car", car.name, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("PutItem succeeded:", car.name);
}
});
});

If you run your load-data command, it should seed in the two items in our carData.json file and log the names back in the console, like below.

#bash 
yarn load-data
yarn run v1.3.2
$ cd dynamodb && node loadCarData.js && cd ..
Importing Cars into DynamoDB. Please wait.
{ id: 1,
type: 'Automatic',
name: 'Toyota Yaris',
manufacturer: 'Toyota',
fuel_type: 'Petrol',
description: 'A smooth ride' }
{ id: 2,
type: 'Manual',
name: 'Volkswagen Golf',
manufacturer: 'Volkswagen',
fuel_type: 'Petrol',
description: 'Good Value' }
PutItem succeeded: Toyota Yaris
PutItem succeeded: Volkswagen Golf
✨ Done in 0.46s.

Now our datas in there, but how do we know? Let’s run a quick test using Dynamo DBs DocumentClient .get method. DocumentClient is just a class that simplifies working with DynamoDB Items.

#JavaScript - readDataTest.js 
var AWS = require("aws-sdk");
AWS.config.update({
region: "eu-west-2",
endpoint: "http://localhost:8000"
});
var docClient = new AWS.DynamoDB.DocumentClient()
var table = "Cars";
var id = 1;
var params = {
TableName: table,
Key:{
"id": id
}
};
docClient.get(params, function(err, data) {
if (err) {
console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("GetItem succeeded:", JSON.stringify(data, null, 2));
}
});

Remembering our JSON file, we should expect the Toyota Yaris to be returned to the console…

#bash
$ yarn read-data
yarn run v1.3.2
$ cd dynamodb && node readDataTest.js && cd ..
GetItem succeeded: {
"Item": {
"name": "Toyota Yaris",
"description": "A smooth ride",
"id": 1,
"type": "Automatic",
"fuel_type": "Petrol",
"manufacturer": "Toyota"
}
}
✨ Done in 0.56s.

BAM! DynamoDB is setup and seeded with data, now we just need to bring all the elements together.

Bringing it all together

At the moment, our Node backend isn’t actually talking to Dynamo DB at all, lets change that by incorporating some of the methods we’ve used above and create a route that returns all cars.

To do this we’re going to using DynamoDBs DocClient scan method.

#Javascript app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var AWS = require("aws-sdk");
var app = express();
app.listen(3000, () => console.log('Cars API listening on port 3000!'))
AWS.config.update({
region: "eu-west-2",
endpoint: "http://localhost:8000"
});
var docClient = new AWS.DynamoDB.DocumentClient();
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.set('view engine', 'jade');
app.get('/', function (req, res) {
res.send({ title: "Cars API Entry Point" })
})
app.get('/cars', function (req, res) {
var params = {
TableName: "Cars",
ProjectionExpression: "#id, #name, #type, #manufacturer, #fuel_type, #description",
ExpressionAttributeNames: {
"#id": "id",
"#name": "name",
"#type": "type",
"#manufacturer": "manufacturer",
"#fuel_type": "fuel_type",
"#description": "description"
}
};
console.log("Scanning Cars table.");
docClient.scan(params, onScan);
function onScan(err, data) {
if (err) {
console.error("Unable to scan the table. Error JSON:", JSON.stringify(err, null, 2));
} else {
res.send(data)
// print all the Cars
console.log("Scan succeeded.");
data.Items.forEach(function(car) {
console.log(car.id, car.type, car.name)
});
if (typeof data.LastEvaluatedKey != "undefined") {
console.log("Scanning for more...");
params.ExclusiveStartKey = data.LastEvaluatedKey;
docClient.scan(params, onScan);
}
}
}
})

This is what you want your app.js file to look like. I know we can refactor this and move some code to the routes folder, however for the purposes of keeping this article as to the point as possible, I’ll leave that to you.

As the file shows, we create a new route called /cars and create a params variable, which contains the name of the table and what we want to be returned from our scan. We then create a function called onScan which sends our data to the client and logs our results to console. This also contains some error catching, should there be any issues with your request.

Now, if you navigate to http://localhost:3000/cars you should see something resembling the below.

#JSON - response from http://localhost:3000/cars
{"Items":[{"name":"Volkswagen Golf","description":"Good Value","id":2,"fuel_type":"Petrol","type":"Manual","manufacturer":"Volkswagen"},{"name":"Toyota Yaris","description":"A smooth ride","id":1,"fuel_type":"Petrol","type":"Automatic","manufacturer":"Toyota"}],"Count":2,"ScannedCount":2}

Great job! Now you’ve got building blocks of a Node.js RESTful API using AWS DynamoDB.

Let’s do one more route where we ask DynamoDB to return a car, by id.

Let’s call our route /cars/:id. We’ll pass the ID in via our request url. We’ll then use the ID to query the table and return us the correct car. We get the id value by slicing the string to return us only the number.

Remember, however, when we created our table we specified that the id was a number type. Therefore if we try to pass the value, as it is, to DynamoDB, it’ll spit back an error. We first need to convert our id value from string to integer using parseInt().

#JavaScript - app.js
[...]
app.get('/cars/:id', function (req, res) {
var carID = parseInt(req.url.slice(6));
console.log(req.url)
console.log(carID)
var params = {
TableName : "Cars",
KeyConditionExpression: "#id = :id",
ExpressionAttributeNames:{
"#id": "id"
},
ExpressionAttributeValues: {
":id": carID
}
};
docClient.query(params, function(err, data) {
if (err) {
console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
} else {
console.log("Query succeeded.");
res.send(data.Items)
data.Items.forEach(function(car) {
console.log(car.id, car.name, car.type);
});
}
});
});

We save our converted carID value in a variable and use this in our params object. We then use the query method to gather and return the data to the client. If all is setup correctly, you should be able to navigate to http://localhost:3000/cars/1 and see that the Yaris is returned as JSON. If you check your terminal you’ll see the id, name and type of the car queried.

#JSON - http://localhost:3000/cars/1
[{"name":"Toyota Yaris","description":"A smooth ride","id":1,"type":"Automatic","fuel_type":"Petrol","manufacturer":"Toyota"}]
#bash
$ yarn start
[nodemon] starting `node app.js`
Cars API listening on port 3000!
/cars/1
1
Query succeeded.
1 'Toyota Yaris' 'Automatic'
GET /cars/1 200 47.279 ms - 126

From here you can add additional routes to search by car name, car type and look to implement POSTing to the DB. Hint: this will be similar to our loadCarData.js file, using DynamoDB’s PutItem function.

Next time I’ll look to deploy our sample app to AWS Elastic Beanstalk along with AWS DynamoDB and implement a build pipeline with CircleCI and testing using Postman.

If you wish, you can check all the code out here, at the example Github Repo.

As always, thanks for reading, hit 👏 if you like what you read and be sure to follow to keep up to date with future posts.


Node.js RESTful API with DynamoDB Local was originally published in Quick Code on Medium, where people are continuing the conversation by highlighting and responding to this story.

Automation With Cron

There’s been quite a few projects recently where I’ve found myself doing tedious things that can easily be automated. Enter Cron, the time-based job scheduler.

Once or twice a week I find myself updating/rebuilding my Jekyll Site because it displays my Medium RSS feed as a Blog page. As the site is static, I need to re-build it every time I publish something new. It’s not an issue as it’s only a few commands, but sometimes I forget and weeks can go by without updating my site. Writing a Cron job is so stupidly easy, you’ll find yourself automating pretty much every mundane task you do.

Writing a Cron Job

#bash
$ env EDITOR=nano crontab -e #opens nano in your terminal, with your crontab open. 

You should see something like this now, after running the above command.

Ok so not exactly like this, as this contains my cron job, but it should be a nice empty text file. For my purpose, I decided to create an executable file with the commands and then get cron to run that, however you can run the commands inside here, it’s just tidier to write a script for it.

Cron Time Intervals

Cron works by executing your commands at a determined time, whether that be weekly on a Monday at 2pm or on the 15th of each month. The * * * * * at the start each determine a different time scale.

You’ll notice in my example, I decided to execute every week on a Sunday.

Here’s another example if you want to run a job at 6:30 every Tuesday.

#nano
30 18 * * 2 yourcommand #execute every Tuesday at 18:30
45 07 10 * * yourcommand # execute every 10th day of the month at 07:45
20 15 * * 3 yourcommand # execute at 15:20 every Wednesday

Let’s make this a little easier and add some shortcut commands to our .bashrc or .zshrc (depending on what you’re using).

Using the alias command we can shortcut a long command to something a little easier to remember and type.

#.bashrc or .zshrc
alias newcron="env EDITOR=nano crontab -e" #open a new crontab
alias cronjobs="crontab -l" # list our cronjobs

Now you’re set to automate whatever you want!

As always, thanks for reading, hit 👏 if you like what you read and be sure to follow to keep up to date with future posts.

Circle CI and DynamoDB

After searching around whilst building a Node.js API project, I realised there wasn’t too much documented on how to setup CircleCI with AWS DynamoDB for testing purposes within your build pipeline. I thought I’d do a quick post to summarise the process and hopefully make it clearer for others who are trying to achieve the same.

Getting Started

As I mentioned above, I was building a Node.js API, however this process should translate regardless of the language or tools you’re using. Before going any further, please make sure you’ve entered your AWS_ACCESS_KEY and AWS_SECRET_KEY into your CircleCI Project. This can be done from the setting page of your Project, under the Permissions heading.

Hit the AWS Permissions link and enter your variables

Now we’re going to need to edit our circleci config.yml file. The below is my finished version. I know it’s pretty long and messy and can definitely be refactored, but it does the job for now.

#YAML
version: 2
branches:
only:
- master # list of branches to build
jobs:
build:
docker:
- image: circleci/node
working_directory: ~/repo
steps:
- checkout
- run:
name: Install Java
command: 'sudo apt-get update && sudo apt-get install default-jre default-jdk'
- run:
name: Install Python
command: 'sudo apt-get update && sudo apt-get install -y python-dev'
- run:
name: Install Python
command: 'sudo curl -O https://bootstrap.pypa.io/get-pip.py'
- run:
name: Install Python
command: 'sudo python get-pip.py'
- run:
name: Install AWS CLI
command: 'sudo pip install awsebcli --upgrade'
- run:
name: Setup Container
command: |
curl -k -L -o dynamodb-local.tgz http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.tar.gz
tar -xzf dynamodb-local.tgz
java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
background: true
- run:
name: Update yarn
command: 'yarn global add npm@latest'
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
- run:
name: Install Dependencies
command: yarn install
- save_cache:
key: dependency-cache-{{ checksum "package.json" }}
paths:
- node_modules
- run:
name: Start Server
command: 'yarn ci-start'
background: true
- run:
name: Create Table
command: 'yarn create-db'
- run:
name: Load Data
command: 'yarn load-data'
- run:
name: Run Tests
command: 'yarn test'
- run:
name: Deploy to AWS Elastic Beanstalk
command: 'eb init MyApp -r eu-west-2 -p arn:aws:elasticbeanstalk:eu-west-2::platform/Node.js running on
64bit Amazon Linux/4.4.3'
- run:
name: Deploy to AWS Elastic Beanstalk
command: 'eb deploy your-env'

First up we install Java as DynamoDB requires Java to run. The next part is optional, but as my app deploys to Elastic Beanstalk, I download Python as AWS EB CLI requires Python to run. Lastly we install DynamoDB directly from Amazon, I’ve chosen eu-west-2, but choose whichever location is nearest to you. This downloads as a zip, so we unzip it and then run the .jar file. The important thing here is to note the use of the option background: true. This ensures it runs in the background and doesn’t stall your build from going onto the next stage. From here, you can launch your server as a background task, load your data in and run your tests.

Hope you this helped anyone having trouble incorporating DynamoDB into their build pipelines. If you’re stuck, or have any questions, please ask!

As always, thanks for reading, hit 👏 if you like what you read and be sure to follow to keep up to date with future posts.

library_booksRead more at Medium