Skip to content

Commit

Permalink
Part II (sst#235)
Browse files Browse the repository at this point in the history
* Adding new chapters for extension

* Adding drafts for more new chapters

* - Add billing form
- Connect billing form

* - Automating react deployments
- Create a build script
- Set up on Netlify
- Custom domains on Netlify
- Frontend workflow

* Adding screenshots for new chapters

* Add screenshots monitoring deployments

* Adding screenshots for Netlify chapters

* Tweaking about page. Editing new chapters.

* Editing new chapters

* - Styling ToC
- Creating two col layout for posts with ToC sections

* Styling new lander. Adding recent updates to data and loading from it.

* Adding links to forums and styling lander

* Tweaking context

* Updating to new code branches

* Tweaking print css

* Updating screenshot for issues page

* Switching to new forums link

* Tweaking chapter links
  • Loading branch information
jayair committed May 10, 2018
1 parent 5a6afb4 commit ce2c3ee
Show file tree
Hide file tree
Showing 236 changed files with 4,333 additions and 481 deletions.
2 changes: 1 addition & 1 deletion 404.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
layout: home
layout: page
title: Page not found
---

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@

[Serverless Stack](https://serverless-stack.com) is an open source guide for building and deploying full-stack apps using Serverless and React on AWS.

We are going to create a [note taking app](https://demo.serverless-stack.com) from scratch using React.js, AWS Lambda, API Gateway, DynamoDB, and Cognito.
We are going to create a [note taking app](https://demo2.serverless-stack.com) from scratch using React.js, AWS Lambda, API Gateway, DynamoDB, and Cognito.

![Demo App](assets/completed-app-desktop.png)

It is a single-page React app powered by a serverless CRUD API. We also cover how add user authentication and handle file uploads.

The entire guide is hosted on GitHub and we use [GitHub Issues][GHIssues] for our comments. With the help of the community we keep the guide accurate and up to date.
The entire guide is hosted on GitHub and we use [Discourse][Discourse] for our comments. With the help of the community we keep the guide accurate and up to date.

#### Source for the Demo App

Expand All @@ -37,8 +37,9 @@ The entire guide is hosted on GitHub and we use [GitHub Issues][GHIssues] for ou

#### Getting Help

- If you are running into issues with a specific chapter, post in the comments for that [chapter][GHIssues].
- Open a [new issue](../../issues/new) if you've found a bug or have some suggestions.
- If you are running into issues with a specific chapter, post in the comments for that [chapter][Discourse].
- Open a [new issue](../../issues/new) if you've found a bug
- Or if you have a suggestion create a [new topic][Discourse] in our forums
- Send us an [email][Email] if you have any other questions or feedback.
- If you've found a typo, edit the chapter and submit a [pull request][PR].

Expand All @@ -49,9 +50,9 @@ The entire guide is hosted on GitHub and we use [GitHub Issues][GHIssues] for ou

#### Maintainers

Serverless Stack is authored and maintained by Frank Wang ([@fanjiewang](https://twitter.com/fanjiewang)) & Jay V ([@jayair](https://twitter.com/jayair)). [**Subscribe to our newsletter**](http://eepurl.com/cEaBlf) for updates on Serverless Stack. Send us an [email][Email] if you have any questions.
Serverless Stack is authored and maintained by Frank Wang ([@fanjiewang](https://twitter.com/fanjiewang)) & Jay V ([@jayair](https://twitter.com/jayair)). [**Subscribe to our newsletter**](https://emailoctopus.com/lists/1c11b9a8-1500-11e8-a3c9-06b79b628af2/forms/subscribe) for updates on Serverless Stack. Send us an [email][Email] if you have any questions.


[GHIssues]: ../../issues?q=is%3Aissue+is%3Aopen+label%3ADiscussion+sort%3Aupdated-desc
[Discourse]: https://discourse.serverless-stack.com
[PR]: ../../compare
[Email]: mailto:contact@anoma.ly
107 changes: 107 additions & 0 deletions _chapters/add-a-billing-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
layout: post
title: Add a Billing API
date: 2018-03-07 00:00:00
description: We are going to create a Lambda function for our serverless billing API. It will take the Stripe token that is passed in from our app and use the Stripe JS SDK to process the payment.
context: true
comments_id: add-a-billing-api/170
---

Now let's get started with creating our billing API. It is going to take a Stripe token and the number of notes the user wants to store.

### Add a Billing Lambda

<img class="code-marker" src="/assets/s.png" />Start by installing the Stripe NPM package. Run the following in the root of our project.

``` bash
$ npm install --save stripe
```

<img class="code-marker" src="/assets/s.png" />Next, add the following to `billing.js`.

``` js
import stripePackage from "stripe";
import { calculateCost } from "./libs/billing-lib";
import { success, failure } from "./libs/response-lib";

export async function main(event, context, callback) {
const { storage, source } = JSON.parse(event.body);
const amount = calculateCost(storage);
const description = "Scratch charge";

// Load our secret key from the environment variables
const stripe = stripePackage(process.env.stripeSecretKey);

try {
await stripe.charges.create({
source,
amount,
description,
currency: "usd"
});
callback(null, success({ status: true }));
} catch (e) {
callback(null, failure({ message: e.message }));
}
}
```

Most of this is fairly straightforward but let's go over it quickly:

- We get the `storage` and `source` from the request body. The `storage` variable is the number of notes the user would like to store in his account. And `source` is the Stripe token for the card that we are going to charge.

- We are using a `calculateCost(storage)` function (that we are going to add soon) to figure out how much to charge a user based on the number of notes that are going to be stored.

- We create a new Stripe object using our Stripe Secret key. We are going to get this as an environment variable. We do not want to put our secret keys in our code and commit that to Git. This is a security issue.

- Finally, we use the `stripe.charges.create` method to charge the user and respond to the request if everything went through successfully.

### Add the Business Logic

Now let's implement our `calculateCost` method. This is primarily our *business logic*.

<img class="code-marker" src="/assets/s.png" />Create a `libs/billing-lib.js` and add the following.

``` js
export function calculateCost(storage) {
const rate = storage <= 10
? 4
: storage <= 100
? 2
: 1;

return rate * storage * 100;
}
```

This is basically saying that if a user wants to store 10 or fewer notes, we'll charge them $4 per note. For 100 or fewer, we'll charge $2 and anything more than a 100 is $1 per note. Clearly, our serverless infrastructure might be cheap but our service isn't!

### Configure the API Endpoint

Let's add a reference to our new API and Lambda function.

<img class="code-marker" src="/assets/s.png" />Add the following above the `resources:` block in the `serverless.yml`.

``` yml
billing:
handler: billing.main
events:
- http:
path: billing
method: post
cors: true
authorizer: aws_iam
```
Make sure this is indented correctly.
### Commit Our Changes
<img class="code-marker" src="/assets/s.png" />Let's quickly commit these to Git.
``` bash
$ git add .
$ git commit -m "Adding a billing API"
```

Now before we can test our API we need to load our Stripe secret key in our environment.
4 changes: 2 additions & 2 deletions _chapters/add-a-create-note-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ layout: post
title: Add a Create Note API
date: 2016-12-30 00:00:00
description: To allow users to create notes in our note taking app, we are going to add a create note POST API. To do this we are going to add a new Lambda function to our Serverless Framework project. The Lambda function will save the note to our DynamoDB table and return the newly created note. We also need to ensure to set the Access-Control headers to enable CORS for our serverless backend API.
context: backend
context: true
code: backend
comments_id: 23
comments_id: add-a-create-note-api/125
---

Let's get started on our backend by first adding an API to create a note. This API will take the note object as the input and store it in the database with a new id. The note object will contain the `content` field (the content of the note) and an `attachment` field (the URL to the uploaded file).
Expand Down
4 changes: 2 additions & 2 deletions _chapters/add-a-delete-note-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ layout: post
title: Add a Delete Note API
date: 2017-01-03 00:00:00
description: To allow users to delete their notes in our note taking app, we are going to add a DELETE note API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will delete a user’s note in the DynamoDB table.
context: backend
context: true
code: backend
comments_id: 27
comments_id: add-a-delete-note-api/153
---

Finally, we are going to create an API that allows a user to delete a given note.
Expand Down
4 changes: 2 additions & 2 deletions _chapters/add-a-get-note-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ layout: post
title: Add a Get Note API
date: 2016-12-31 00:00:00
description: To allow users to retrieve a note in our note taking app, we are going to add a GET note API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will retrieve the note from our DynamoDB table.
context: backend
context: true
code: backend
comments_id: 24
comments_id: add-a-get-note-api/132
---

Now that we created a note and saved it to our database. Let's add an API to retrieve a note given its id.
Expand Down
4 changes: 2 additions & 2 deletions _chapters/add-a-list-all-the-notes-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ layout: post
title: Add a List All the Notes API
date: 2017-01-01 00:00:00
description: To allow users to retrieve their notes in our note taking app, we are going to add a list note GET API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will retrieve all the user’s notes from the DynamoDB table.
context: backend
context: true
code: backend
comments_id: 25
comments_id: add-a-list-all-the-notes-api/147
---

Now we are going to add an API that returns a list of all the notes a user has.
Expand Down
4 changes: 2 additions & 2 deletions _chapters/add-an-update-note-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ layout: post
title: Add an Update Note API
date: 2017-01-02 00:00:00
description: To allow users to update their notes in our note taking app, we are going to add an update note PUT API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will update a user’s note in the DynamoDB table.
context: backend
context: true
code: backend
comments_id: 26
comments_id: add-an-update-note-api/144
---

Now let's create an API that allows a user to update a note with a new note object given its id.
Expand Down
4 changes: 2 additions & 2 deletions _chapters/add-app-favicons.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ layout: post
title: Add App Favicons
date: 2017-01-07 00:00:00
description: To generate app icons and favicons for our React.js app we will use the Realfavicongenerator.net service. This will replace the default favicon that Create React App comes with.
context: frontend
comments_id: 30
context: true
comments_id: add-app-favicons/155
---

Create React App generates a simple favicon for our app and places it in `public/favicon.ico`. However, getting the favicon to work on all browsers and mobile platforms requires a little more work. There are quite a few different requirements and dimensions. And this gives us a good opportunity to learn how to include files in the `public/` directory of our app.
Expand Down
36 changes: 36 additions & 0 deletions _chapters/add-stripe-keys-to-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
layout: post
title: Add Stripe Keys to Config
date: 2018-03-22 00:00:00
description:
comments_id: add-stripe-keys-to-config/185
---

Back in the [Setup a Stripe account]({% link _chapters/setup-a-stripe-account.md %}) chapter, we had two keys in the Stripe console. The **Secret key** that we used in the backend and the **Publishable key**. The **Publishable key** is meant to be used in the frontend.

We did not complete our Stripe account setup back then, so we don't have the live version of this key. For now we'll just assume that we have two versions of the same key.

<img class="code-marker" src="/assets/s.png" />Add the following line in the `dev` block of `src/config.js`.

```
STRIPE_KEY: "YOUR_STRIPE_DEV_PUBLIC_KEY",
```

<img class="code-marker" src="/assets/s.png" />And this in the `prod` block of `src/config.js`.

```
STRIPE_KEY: "YOUR_STRIPE_PROD_PUBLIC_KEY",
```

Make sure to replace, `YOUR_STRIPE_DEV_PUBLIC_KEY` and `YOUR_STRIPE_PROD_PUBLIC_KEY` with the **Publishable key** from the [Setup a Stripe account]({% link _chapters/setup-a-stripe-account.md %}) chapter. For now they'll be the same. Just make sure to use the live version in the `prod` block when you configure your Stripe account completely.

### Commit the Changes

<img class="code-marker" src="/assets/s.png" />Let's quickly commit these to Git.

``` bash
$ git add .
$ git commit -m "Adding Stripe keys to config"
```

Next, we'll build our billing form.
4 changes: 2 additions & 2 deletions _chapters/add-support-for-es6-es7-javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ title: Add Support for ES6/ES7 JavaScript
date: 2016-12-29 12:00:00
redirect_from: /chapters/add-support-for-es6-javascript.html
description: AWS Lambda supports Node.js v8.10 and so to use ES import/exports in our Serverless Framework project we need to use Babel and Webpack 4 to transpile our code. We can do this by using the serverless-webpack plugin to our project. We will use the serverless-nodejs-starter to set this up for us.
context: backend
context: true
code: backend
comments_id: 22
comments_id: add-support-for-es6-es7-javascript/128
---

AWS Lambda recently added support for Node.js v8.10. The supported syntax is a little different compared the frontend React app that we'll be working on a little later. It makes sense to use similar ES features. Specifically, we'll be relying on ES import/exports in our handler functions. To do this we will be transpiling our code using [Babel](https://babeljs.io) and [Webpack 4](https://webpack.github.io). Serverless Framework supports plugins to do this automatically. We are going to use the [serverless-webpack](https://github.com/serverless-heaven/serverless-webpack) plugin.
Expand Down
4 changes: 2 additions & 2 deletions _chapters/add-the-create-note-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ layout: post
title: Add the Create Note Page
date: 2017-01-22 00:00:00
description: We would like users to be able to create a note in our React.js app and upload a file as an attachment. To do so we are first going to create a form using the FormGroup and FormControl React-Bootstrap components.
context: frontend
comments_id: 47
context: true
comments_id: add-the-create-note-page/107
---

Now that we can signup users and also log them in. Let's get started with the most important part of our note taking app; the creation of a note.
Expand Down
4 changes: 2 additions & 2 deletions _chapters/add-the-session-to-the-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ title: Add the Session to the State
date: 2017-01-15 00:00:00
redirect_from: /chapters/add-the-user-token-to-the-state.html
description: We need to add the user session to the state of our App component in our React.js app. By lifting the state up we can pass the session to all the child containers.
context: frontend
comments_id: 39
context: true
comments_id: add-the-session-to-the-state/136
---

To complete the login process we would need to update the app state with the session to reflect that the user has logged in.
Expand Down
4 changes: 2 additions & 2 deletions _chapters/adding-links-in-the-navbar.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ layout: post
title: Adding Links in the Navbar
date: 2017-01-11 12:00:00
description: To add links to the Navbar of our React.js app we’ll be using the NavItem React-Bootstrap component. And to allow users to navigate using these links we are going to use React-Router's Route component and call the history.push method.
context: frontend
comments_id: 35
context: true
comments_id: adding-links-in-the-navbar/141
---

Now that we have our first route set up, let's add a couple of links to the navbar of our app. These will direct users to login or signup for our app when they first visit it.
Expand Down
6 changes: 3 additions & 3 deletions _chapters/api-gateway-and-lambda-logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
layout: post
title: API Gateway and Lambda Logs
description: To view logs for your serverless APIs on AWS, CloudWatch needs to be enabled for API Gateway and Lambda. CloudWatch logs are ordered by Log Groups and Log Stream. Lambda CloudWatch logs can also be viewed using the Serverless CLI with the “serverless logs” command.
date: 2017-03-17 00:00:00
context: all
comments_id: 146
date: 2018-04-03 00:00:00
context: true
comments_id: api-gateway-and-lambda-logs/31
---

Logging is an essential part of building backends and it is no different for a serverless API. It gives us visibility into how we are processing and responding to incoming requests.
Expand Down
20 changes: 20 additions & 0 deletions _chapters/automating-react-deployments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
layout: post
title: Automating React Deployments
date: 2018-03-25 00:00:00
description: We want to automatically deploy our Create React App when we push any changes to our Git repository. To do this, we will need to set our project up on Netlify.
context: true
comments_id: automating-react-deployments/188
---

If you've followed along with the first part of this guide, you'll have noticed that we deployed our Create React App to S3 and used CloudFront as a CDN in front of it. Then we used Route 53 to configure our domain with it. We also had to configure the www version of our domain and this needed another S3 and CloudFront distribution. This process can be a bit cumbersome.

In the next few chapters we are going to be using a service called [Netlify](https://www.netlify.com) to automate our deployments. It's a little like what we did for our serverless API backend. We'll configure it so that it'll deploy our React app when we push our changes to Git. However, there are a couple of subtle differences between the way we configure our backend and frontend deployments.

1. Netlify hosts the React app on their infrastructure. In the case of our serverless API backend, it was hosted on our AWS account.

2. Any changes that are pushed to our `master` branch will update the production version of our React app. This means that we'll need to use a slightly different workflow than our backend. We'll use a separate branch where we will do most of our development and only push to master once we are ready to update production.

Just as in the case with our backend, we could use [Travis CI](https://travis-ci.org) or [Circle CI](https://circleci.com) for this but it can take a bit more configuration and we'll cover that in a different chapter.

So let's get started with setting up your project on Netlify.
24 changes: 24 additions & 0 deletions _chapters/automating-serverless-deployments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
layout: post
title: Automating Serverless Deployments
date: 2018-03-11 00:00:00
description: We would like to automatically deploy our Serverless Framework project when we commit any changes to our Git repository. To do this we are going to use a service called Seed (https://seed.run) to automate our serverless deployments. It will configure a CI/CD pipeline and setup our environments.
context: true
comments_id: automating-serverless-deployments/174
---

So to recap, this is what we have so far:

- A serverless project that has all it's infrastructure completely configured in code
- A way to handle secrets locally
- And finally, a way to run unit tests to test our business logic

All of this is neatly committed in a Git repo.

Next we are going to use our Git repo to automate our deployments. This essentially means that we can deploy our entire project by simply pushing our changes to Git. This can be incredibly useful since you won't need to create any special scripts or configurations to deploy your code. You can also have multiple people on your team deploy with ease.

Along with automating deployments, we are also going to look at working with multiple environments. We want to create clear separation between our production environment and our dev environment. We are going to create a workflow where we continually deploy to our dev (or any non-prod) environment. But we will be using a manual promotion step when we promote to production. We'll also look at configuring custom domains for APIs.

For automating our serverless backend, we are going to be using a service called [Seed](https://seed.run). Full disclosure, we also built Seed. You can replace most of this section with a service like [Travis CI](https://travis-ci.org) or [Circle CI](https://circleci.com). It is a bit more cumbersome and needs some scripting but we might cover this in the future.

Let's get started with setting up your project on Seed.
4 changes: 2 additions & 2 deletions _chapters/call-the-create-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ layout: post
title: Call the Create API
date: 2017-01-23 00:00:00
description: To let our users create a note in our React.js app, we need to connect our form to our serverless API backend. We are going to use AWS Amplify's API module for this.
context: frontend
comments_id: 48
context: true
comments_id: call-the-create-api/124
---

Now that we have our basic create note form working, let's connect it to our API. We'll do the upload to S3 a little bit later. Our APIs are secured using AWS IAM and Cognito User Pool is our authentication provider. Thankfully, Amplify takes care of this for us by using the logged in user's session.
Expand Down
Loading

0 comments on commit ce2c3ee

Please sign in to comment.