Latest Blog Posts

library_booksRead more at Medium

arrow_back

SSL Certificates with Rails, AWS Elastic Beanstalk and Let’s Encrypt

Having recently worked on an App hosted in Elastic Beanstalk, I realised it was difficult to install an SSL Certificate, unless your environment was setup with a load balancer.

What is load balancing?

Load Balancing automatically distributes incoming requests/traffic across multiple targets, for example EC2 instances. This increases performance and reduces downtime, which is great, but if you’re still starting out, prototyping or setting up a staging site, it might be overkill.

As well as this, there’s the cost factor. Roughly speaking, if you configure Elastic Beanstalk to use load balancing, it’ll cost around £20-£30 per month, depending on how many instances you choose.

Obviously, nothing stops you from SSHing into your instance and installing the certificate manually, but if you’re not to experienced with linux or devops it can become a bit confusing.

Setting up your Rails App for SSL with Elastic Beanstalk

Firstly, we’re going to need to create a new directory within the root our Rails App (if it doesn’t exist already) .ebextensions and create a new file certbot.config. Within this file, any script and commands we write will be executed during deployment. Depending on what you want to achieve, you’re able to run commands at any stage of the deployment and build cycle. Please note the below config is only applicable for nginx servers, other servers may require slightly different configs.

Copy and paste the below snippet into your new file, ensure the spacing/indentation remains the same.

Let’s quickly breakdown what’s happening in this file.

First we add port 443, used for SSL connections, to the security group. Next, we create a custom nginx config file, containing the server’s configuration for port 443 and state the location of the SSL Certificate, as well as the location of our app on the server. Lastly, we run a suite of commands which installs certbot and requests a new certifcate for domain certdomain and saves them in the location stated in our SSL config.

Let’s Encrypt issued certificates tend to last around 3 months, but certbot will automatically check the renewal date and renew your certificate if it’s coming up to renewal.

I’ve personally had a few issues with this in the past and have setup a cron job to run on my eb instance every week checking for renewals. If you’re deploying regularly, it shouldn’t be an issue but I’d suggest keeping an eye on your certificate expiration date to see if certbot is renewing when it’s supposed to. You can do this quite easily in Chrome by clicking the padlock in the URL bar and then the Certificate button. A drop down will appear with info on your certificate and it’s expiration date.

Before running a new deployment you will be required to change line 58 to your email address. As well as this, you will need to set the environment variable certdomain equal to your domain, eg: example.com. The latter can be done on the Elastic Beanstalk Configuration Dashboard, under the Software Tab.

Once this is completed and your app has successfully deployed with all the updates, you should be able to visit your new https site. If this is still not working, you might need to reload ngnix. To do this, SSH into your instance and run:

#Your EB Instance
$ sudo service nginx reload
Reloading nginx: [ OK ]

All done! You’ve successfully installed an SSL certificate on a Single Instance Elastic Beanstalk 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 introduction into GraphQL

I know I’m a bit late to the GraphQL party but having had sometime recently, I thought I’d play around with it on a new side project I’m working on. Initially I found myself a bit lost, but after reading through the sites docs and understanding it’s purpose and the problem it solves, it made so much sense.

What exactly is GraphQL?

Created in 2012 and open sourced by Facebook in 2015,

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.

Essentially it’s a library that describes how we retrieve data from the server to the client. Some of the main features are:

Let’s explore these features in a little more depth.

Schema

A huge benefit I found from the outset was being able to define my data, it’s types and relationships in a single Schema document. GraphQL is strongly typed, which means everything in the query is checked before an operation is executed. This ensures the syntax, data types and query are all valid before execution.

#yourSchema.graphql
type Item {
id: ID!
name: String!
comments: [Comment]
description: String
}
type Comment {
id: ID!
content: String!
photo: Photo
}

This is a basic example of a Schema document. There are five default scalar types that come out of the box, those are String, Float, Integer, Boolean and ID. As well as these default types, you’re able to create custom scalar types, for example if you had a Date or Time data type you could define a custom scalar to be used in your Schema. The ! denotes whether a field is required or not.

Having a Schema provides a useful reference of your data and ensures there’s no ambiguity.

Retrieve as many or as little resources as you require in a single request

This is one of the features which is incredibly useful. The example query below will retrieve a list of all the items.

query {
items {
id
name
description
}
};

However, say we’re on a list view, we might only want the name and description.

query {
items {
name
description
}
};

This allows us to query and retrieve only the data we require, making it easier, quicker and more efficient to work with. It also reduces over-fetching.

As well as this, we have the ability to fetch the comments relating to that item with a few extra lines in our query request.

query {
items {
id
name
comment {
id
content
photo
}
description
}
};

Now let’s imagine we’ve got a complex dashboard view where we need to fetch multiple attributes from multiple resources. No problem!

query {
items {
name
description
}
comments {
content
}
todos {
todo
description
}
};

This single request will fetch only the desired attributes we’ve specified, from the multiple resources listed. It negates the need to perform many http requests to receive all the data you require, unlike REST APIs

Continually develop your API without versioning

REST APIs require versioning when deprecating or introducing new fields, as the server dictates the response and therefore the client needs to be able to process that response. The key difference with GraphQL is that the query itself dictates the response, as highlighted above, therefore the client will be compatible regardless of what new fields are added or deprecated.

Pros

Cons

Conclusion

GraphQL offers an alternative way to architect your client — server relationship, it helps decouple your API from it’s consumers and makes it easy to iterate over your product without the fear of introducing breaking changes.

In a future post I’ll be taking a look at setting up a Serverless GraphQL React app in AWS and demonstrating some more of GraphQL’s features and benefits.

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 introduction into GraphQL was originally published in Data Driven Investor on Medium, where people are continuing the conversation by highlighting and responding to this story.

Serverless React Web App with AWS Amplify — Part Three

Following on from my previous post, Serverless React Web App with AWS Amplify — Part Two, today we’ll be adding authentication and looking at deployment and distribution in the final part of this mini series.

All source code for the project can be found here.

At the moment our app should look somewhat similar to the below, with the ability to add/edit/delete items.

Current App

User Authentication

Let’s start by enabling the user-signin feature using the awsmobile CLI.

#bash
$ awsmobile user-signin enable --prompt
? Sign-in is currently disabled, what do you want to do next
Enable sign-in with default settings
#Choose default settings 
$ awsmobile push #push your changes to the cloud

To keep things simple, we’ll use Amplify’s withAuthenticator component to handle our auth flow. This component wraps our entire app up and renders a complete sign up/sign in UI, which is customisable through the AWS Mobile Hub. If you wish, you can also handle the Sign In/Sign Up process using the Auth component, which gives you greater flexibility of the entire auth flow.

# App.js 
[...]
import { withAuthenticator } from 'aws-amplify-react'
[...]
export default withAuthenticator(App, true);

After adding the above code to your App.js file, reload your app and it should look something like this. The added true ensures a sign out button is rendered.

Sign In/Sign Up Page

Try going through the Sign Up flow, you’ll notice MFA (multi-factor auth) is enabled by default, so when putting your mobile number in you’ll get a confirmation code to use each time to sign in/sign up. It’s probably overkill for an app like this, but it does improve security. Once going through the flow and signing in with your newly created user account, you should be directed to your app’s home page.

That’s user authentication done! Pretty simple isn’t it? You can also view all users signed up to your app by heading over to the User Pool on Cognito and clicking on Users and Groups. The easiest way to get to your Cognito Pool is to open up the Mobile Hub, head to the User Sign In card and click “Edit in Cognito.”

AWS Mobile Hub, Cognito Pool Settings

Please remember, whenever you make changes in the hub/on the web app, be sure to pull the changes back into your local project. I’ve caused myself so many stupid issues by not doing this and your project gets real messy, real fast and can get out of sync, so make sure your local copy is always in sync with the cloud.

Federated Identity Login

Now we’ve added user auth, but most modern web apps incorporate some form of social login, so let’s add Google Sign-In to ours.

You’ll need to get a google_client_id. It’s fairly straight forward and Google provide easy to follow instructions here. When asked what you’re developing for, choose Web Server and then add the Authorized JavaScript origins as http://localhost:3000. Be sure to keep this tab open for the time being, as we’ll be going back and fourth retrieving the Client ID as well as changing some settings later on.

Next, we’ll need to add our Client ID to our Cognito Pool, this can be done by configuring the user-signin feature, using the awsmobile CLI tool.

#bash 
→ awsmobile user-signin configure
? Sign-in is currently enabled, what do you want to do next Go to advance settings
? Which sign-in method you want to configure Google sign-in (currently disabled)
? Google Web App Client ID xxxxYOURCLIENTIDxxxxx.apps.googleusercontent.com
? Google Android Client ID xxxxYOURCLIENTIDxxxxx.apps.googleusercontent.com
? Google iOS Client ID xxxxYOURCLIENTIDxxxxx.apps.googleusercontent.com
$ awsmobile push //don't forget to push your changes, otherwise it won't work. 

Finally, we’ll need to tweak our code to include our google_client_id, so that the withAuthenticator component can use it. As long as you restrict the origins from the Google Dev Console, as we did earlier, there isn’t a specific need to hide this ID, so we don’t need to worry about loading environment variables just yet.

The first thing we need to change is in our App.js file. We revert back to exporting the App as it is, not wrapping it in the withAuthenticator component.

#App.js 
[...]
export default App;

Next, in our index.js file, we import App.js and create a new constant named AppWithAuth, which is essentially what we had previously in our App.js file. We then create another constant called federated and set this equal to a JSON config containing our google_client_id. Lastly, we use our constant AppWithAuth within the render function and include our google_client_id from the federated constant we just created.

#index.js
import App from './App';
import { withAuthenticator } from 'aws-amplify-react'
const AppWithAuth = withAuthenticator(App, true);
const federated = {
google_client_id: 'xxxxxYOURCLIENTIDxxxxxx.apps.googleusercontent.com',
};
ReactDOM.render(<AppWithAuth federated={federated}/>, document.getElementById('root'));
registerServiceWorker();

After reloading your app, you should be able to successfully login using Google!

Our new sign in/sign up up form with Google Sign in Button
After successful Google Login, the user’s name is displayed on the header with a Sign Out button.

The process for adding Facebook/Amazon Logins is pretty much the same, you’d just need to acquire a client ID from their developer dashboard.

That’s it for Authentication! We successfully added a sign in/sign up form and incorporated Google Login. It’s still quite rough around the edges and could do with some styling, but functionality wise it’s pretty much there.

Deployment and Distribution

Let’s make sure people can actually access and use our app by deploying it and distributing it. AWS Amplify includes one simple command that sets everything up for deployment and distribution.

#bash
$ awsmobile publish
File sizes after gzip:
379.32 KB  build/static/js/main.c1215b8f.js
99.09 KB build/static/css/main.340cfbbb.css
The project was built assuming it is hosted at the server root.
You can control this with the homepage field in your package.json.
For example, add this to build it for GitHub Pages:
"homepage" : "http://myname.github.io/myapp",
The build folder is ready to be deployed.
You may serve it with a static server:
yarn global add serve
serve -s build
Find out more about deployment here:
http://bit.ly/2vY88Kr
Successful!
your application is published and hosted at:
http://serverlesswebappexam-hosting-mobilehub-1099236211.s3-website.eu-west-2.amazonaws.com
your application is also distributed through aws CloudFront
https://d2x9wan2jbunv.cloudfront.net

This command will build your app and upload the files to a s3 bucket, configured to host static websites. It’ll also go one step further and distribute this through their Global CDN, CloudFront, making your site quicker and easier to access for users all over the world!

Once uploading has finished, you’ll notice two URLS, one is the bucket URL and one is the cloudfront distribution. If you try logging in on either you’ll get this error.

Redirect_URI_Mismatch error — Update the Authorised Origins within the Google Cloud API Settings

This occurs because the request is coming from a domain that hasn’t been approved, so let’s quickly add our new CloudFront URL to the Authorised Javascript origins, in the Google Cloud API Settings.

After adding this and saving, you should be able to login successfully using Google.

As it’s quite simple to do, I thought it’d be useful to finish by showing you how to point your new app to your personal domain, if you have one. In order to do this, you’ll need your domain name in AWS Route 53.

Firstly, we need to configure our Cloudfront Distribution to include our alternate domain name. Open up the CloudFront Dashboard, locate your distribution and update the Alternate Domain Names. I’ll be using the domain serverlessreactexample.jameshamann.com.

You’ll also need to choose Custom SSL Certificate and select the correct certificate for your domain. Setting up a Certificate in AWS Certificate Manager is incredibly easy and straightforward, you can follow the docs here. One thing to note, make sure the certificates you create are stored in US-East (N. Virginia) in order to access them from CloudFront. You can always check what region you’re in by checking the top right of the app bar, next to your name. When you’re all done, hit Yes, Edit. Now we wait, updates to CloudFront distributions can take some time, so take a little break.

Once your changes are deployed, head over to the Route 53 Dashboard and click on your registered domain. Next, click Create Record Set and fill in your URL in the Name field. You’ll then need to change Alias to Yes and copy your CloudFront URL into the Alias Target field.

Hit Create and wait for your changes to go live! The time taken can vary so don’t worry if it’s not instant, go grab a coffee or beer and come back later to check. Don’t forget to add your new domain name to the Authorised Javascript Origins, in your Google Cloud API settings, to ensure Google Login works correctly.

Congrats! You’ve now got a fully functioning, albeit a bit rough looking, Serverless React Web App! Now you have a great foundation to move forward with, allowing you to build, test and deploy your app in nice, quick iterations.

Further Development

There’s quite a few different things you can do to further enhance your app. Here’s a few to help get the ideas bubbling:

These are just a few ideas, but there’s lots more you can do to further develop and build new features.

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.

Hi Paweł Zubkiewicz thanks for reading!

Hi Paweł Zubkiewicz thanks for reading! I’m not sure about DynamoDB being publicly accessible? To access DynamoDB you need AWS Credentials, which have access to the correct resources. In this model, Amplify takes care of Authentication through the API module. All requests made are automatically signed with a AWS v4 signature, so any requests made to DynamoDB are secured through your API.

In answer to your second question, cognito is integrated into Amplify and pretty easy to setup. This is another way you could authenticate access, by creating a cognito group or user with the relevant permissions.

Hope this helps!

Architecting your IoT App using Raspberry Pi and AWS

Having done a lot of work with IoT sensors and Raspberry Pi recently, and considering it’s growing popularity, I thought I’d share how I went about architecting the infrastructure of the application, which can be applied to pretty much any IoT app you may be working on. Whilst the principals will apply to all major cloud hosting providers, I’ll be discussing AWS specific services.

Project Outline

A Smart Watering System for Farms/Plants, driven by data obtained from IoT Sensors and Forecasting APIs.

Hardware Used

The above is obviously still in the prototyping phase, which hopefully explains the mess. The end product will be cased in a watertight enclosure. In addition to the Temperature Sensor, the above picture also contains a Photo Resistor which captures information about light levels, but is currently not being used.

Architecture Diagram

This diagram illustrates all of the components used and how they interact with each other. Let’s break it down a little further and understand what’s happening.

First the Pi IoT sensors publish raw data about the temperature, humidity, soil moisture and hours of sunlight. This is sent to an IoT topic via an MQTT message. This is a super light weight pub/sub messaging protocol designed specifically for devices in locations with limited network bandwidth, which is great for IoT. Next, an AWS IoT rule and action is created, which subscribes to the topic that the sensors are publishing to and sends these messages to a Kinesis data stream and DynamoDB Table.

Using Kinesis is entirely optional and dependant on what you’re building. The benefit of Kinesis is that it allows you the option of creating multiple delivery streams for your data. You could have one stream that delivers to a Lambda Function for record transformation, which writes to DynamoDB, or another stream for a Kinesis’ Analytics Application, even a delivery stream for an ElasticSearch app. It gives you the flexibility of being able to stream the data to multiple destinations for multiple uses. On the other hand, it can over complicate the app and be a bit of overkill in some cases, but it’s worth considering and making a final decision based on your app’s requirements. In this example the Kinesis Delivery Stream writes to an S3 Bucket, as an archive.

Once in DynamoDB, the application looks similar to a typical serverless application. The back-end logic is handled by Lambda and API Gateway and then sent to the client/Raspberry Pi. The React Front End is stored in an S3 bucket and distributed via CloudFront to the client. The client will display the sensor data as well as having the ability to control the water flow manually.

To automate the watering, the data from the sensors will be evaluated alongside data from Forecasting APIs and, based on certain criteria, the app will decide whether the area needs water and, if so, it would calculate the amount required based on all the data available. In the event watering is required a message will be published from Lambda to the Raspberry Pi on another topic, containing the “Open Valve” message as well as the amount of time to keep the valve open.

To add a level of interactivity, a chatbot could be included which would prompt the user the current status and give the user the option to interact back, querying past events. In addition to this, data visualisation tools could be used to create dashboards, historical data could even be loaded into a Machine Learning Model to predict the likelihood of watering and the optimum amount of water. There are lots of applications and ways in which you can expand a small project into something more, to gain experience using new tools and technologies.

Whilst this model explains the infrastructure in the context of this project, it is easily applied to any other IoT app you may have in mind. Connecting a Raspberry Pi to the cloud allows it to do so much more and gives you the opportunity to build new, different apps and explore different technologies.

In another post we’ll look at the specifics of setting this up within the AWS Environment and getting a Raspberry Pi Connected on the IoT Console.

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.


Architecting your IoT App using Raspberry Pi and AWS was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.

Hi Gerald Carter, sorry for the delay in getting back to you.

Hi Gerald Carter, sorry for the delay in getting back to you. Looking at the error you mentioned above, it might be down to the command you used in your pipelines script or the name of your app/environment on AWS.

Using my example above, my elastic beanstalk app was called MyApplication and the environment I used was MyApplication-staging.

On line 17 I mention the name of the Application, MyApplication.

On line 18, I mention the environment I wish to deploy my app to, MyApplicaton-staging.

Both of these need to be setup before pushing. Double check what you called your app/environment in AWS and make sure this matches the build script.

Hope this helps!

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.

library_booksRead more at Medium