Stacks on Stacks

The Serverless Ecosystem Blog by Stackery.

Posts on Tutorials & Guides

Building Slack Bots for Fun: A Serverless Release Gong
Anna Spysz

Anna Spysz | November 16, 2018

Building Slack Bots for Fun: A Serverless Release Gong

We have a running joke at Stackery regarding our tiny little gong that’s used to mark the occasion when we get a new customer.

sad tiny gong

So tiny.

And while I’m all about the sales team celebrating their successes (albeit with a far-too-small gong), I felt like the dev team needed its own way to commemorate major product releases and iterations.

Then I saw that Serverless Framework is doing its No Server November challenge, and I thought, what a perfect way to show off our multiple framework support while iterating on our Github Webhooks Tutorial to support Serverless Framework projects!

Starting from Scratch…Almost

Stackery makes it easy to import an existing stack or create new a stack based on an existing template. And, conveniently, I had already build a GitHub webhook listener just the week before as part of the webhook tutorial. However, the rules of the competition specifically state that “to qualify, the entry must use the Serverless Framework and a serverless backend” - and I was curious to see the differences when building out my app using that framework as compared to our default (AWS SAM).

So the first thing I did was create an empty Serverless Framework template I could use to build my app on. This was quite simple - I just created a serverless.yml file in a new directory and added the following:

service: serverless-gong

frameworkVersion: ">=1.4.0 <2.0.0"

provider:
  name: aws
  runtime: nodejs8.10

I initialized a new git repository, and added, committed and pushed the serverless.yml file to it.

Building in Stackery

Now it was time to import my new Serverless Framework boilerplate into Stackery so I could start adding resources. In the Stackery App, I navigated to my Stacks page, and clicked the Create New Stack button in the upper right, filling it out like so:

screenshot

Then, in the Stackery Dashboard, I created an API Gateway resource with a POST route with a /webhook path and a Function resource named handleGong, and connected them with a wire. All of this, including saving and using environment variables for your GitHub secret, is documented in the webhook tutorial, so I won’t go through it again. In the end, I had a setup very similar to that found at the end of that tutorial, with the exception of having a serverless.yml file rather than a template.yml for the configuration, and having everything in one directory (which was fine for a small project like this, but not ideal in the long run).

With the added resources, my serverless configuration now looked like this:

service: serverless-gong
frameworkVersion: '>=1.4.0 <2.0.0'
provider:
  name: aws
  runtime: nodejs8.10
functions:
  handleGong:
    handler: handler.gongHandler
    description:
      Fn::Sub:
        - 'Stackery Stack #{StackeryStackTagName} Environment #{StackeryEnvironmentTagName} Function #{ResourceName}'
        - ResourceName: handleGong
    events:
      - http:
          path: /webhook
          method: POST
    environment:
      GITHUB_WEBHOOK_SECRET:
        Ref: StackeryEnvConfiggithubSecretAsString
      SLACK_WEBHOOK_URL:
        Ref: StackeryEnvConfigslackWebhookURLAsString
resources:
  Parameters:
    StackeryStackTagName:
      Type: String
      Description: Stack Name (injected by Stackery at deployment time)
      Default: serverless-gong
    StackeryEnvironmentTagName:
      Type: String
      Description: Environment Name (injected by Stackery at deployment time)
      Default: dev
    StackeryEnvConfiggithubSecretAsString:
      Type: AWS::SSM::Parameter::Value<String>
      Default: /Stackery/Environments/<StackeryEnvId>/Config/githubSecret
    StackeryEnvConfigslackWebhookURLAsString:
      Type: AWS::SSM::Parameter::Value<String>
      Default: /Stackery/Environments/<StackeryEnvId>/Config/slackWebhookURL
  Metadata:
    StackeryEnvConfigParameters:
      StackeryEnvConfiggithubSecretAsString: githubSecret
      StackeryEnvConfigslackWebhookURLAsString: slackWebhookURL
plugins:
  - serverless-cf-vars

Look at all that yaml I didn't write!

And my Dashboard looked like so:

screenshot

Since I had already written a webhook starter function that at the moment logged to the console, it didn’t feel necessary to reinvent the wheel, so I committed in Stackery, then git pulled my code to see the updates, and created a handler.js file in the same directory as the serverless.yml. In it, I pasted the code from my previous webhook function - this was going to be my starting point:

const crypto = require('crypto');
function signRequestBody(key, body) {
  return `sha1=${crypto.createHmac('sha1', key).update(body, 'utf-8').digest('hex')}`;
}
// The webhook handler function
exports.gongHandler = async event => {
  // get the GitHub secret from the environment variables
  const token = process.env.GITHUB_WEBHOOK_SECRET;
  const calculatedSig = signRequestBody(token, event.body);
  let errMsg;
  // get the remaining variables from the GitHub event
  const headers = event.headers;
  const sig = headers['X-Hub-Signature'];
  const githubEvent = headers['X-GitHub-Event'];
  const body = JSON.parse(event.body);
  // this determines username for a push event, but lists the repo owner for other events
  const username = body.pusher ? body.pusher.name : body.repository.owner.login;
  const message = body.pusher ? `${username} pushed this awesomeness/atrocity through (delete as necessary)` : `The repo owner is ${username}.`
  // get repo variables
  const { repository } = body;
  const repo = repository.full_name;
  const url = repository.url;

  // check that a GitHub webhook secret variable exists, if not, return an error
  if (typeof token !== 'string') {
    errMsg = 'Must provide a \'GITHUB_WEBHOOK_SECRET\' env variable';
    return {
      statusCode: 401,
      headers: { 'Content-Type': 'text/plain' },
      body: errMsg,
    };
  }
  // check validity of GitHub token
  if (sig !== calculatedSig) {
    errMsg = 'X-Hub-Signature incorrect. Github webhook token doesn\'t match';
    return {
      statusCode: 401,
      headers: { 'Content-Type': 'text/plain' },
      body: errMsg,
    };
  }

  // print some messages to the CloudWatch console
  console.log('---------------------------------');
  console.log(`\nGithub-Event: "${githubEvent}" on this repo: "${repo}" at the url: ${url}.\n ${message}`);
  console.log('Contents of event.body below:');
  console.log(event.body);
  console.log('---------------------------------');

  // return a 200 response if the GitHub tokens match
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      input: event,
    }),
  };

  return response;
};

At this point, I prepared and did the initial deploy of my stack in order to get the Rest API endpoint for the GitHub webhook I needed to set up. Again, the webhook tutorial runs through the deployment and webhook setup process step by step, so I won’t repeat it here.

Using the Rest API /webhook url, I created a webhook in our Stackery CLI repo that was now listening for events, and I confirmed in my CloudWatch logs that it was indeed working.

Bring on the Gong

The next step was to modify the function so it “gonged” our Slack channel when our Stackery CLI repo was updated with a new release. To do that, I had to create a custom Slack app for our channel and set up its incoming webhooks. Luckily, Slack makes that really easy to do, and I just followed the step-by-step instructions in Slack’s webhook API guide to get going.

I set up a #gong-test channel in our Slack for testing so as to not annoy my co-workers with incessant gonging, and copied the URL Slack provided (it should look something like https://hooks.slack.com/services/T00000000/B00000000/12345abcde).

Before editing the Lambda function itself, I needed a way for it to reference that URL as well as my GitHub secret without hard-coding it in my function that would then be committed to my public repo (because that is a Very Bad Way to handle secrets). This is where Stackery Environments come in handy.

I saved my GitHub secret and Slack URL in my environment config like so:

screenshot

Then referenced it in my function:

screenshot

And will add it to my function code in the next step, using process.env.GITHUB_WEBHOOK_SECRET and process.env.SLACK_WEBHOOK_URL as the variables.

Final Ingredient

Since we’re automating our gong, what’s more appropriate than an automated gong? After a somewhat frustrating YouTube search, I found this specimen:

A auto-gong for our automated app? Perfect! Now let’s use our function to send that gong to our Slack channel.

Here’s the code for the final gongHandler function in handler.js:

const crypto = require('crypto');
const Slack = require('slack-node');

// validate your payload from GitHub
function signRequestBody(key, body) {
  return `sha1=${crypto.createHmac('sha1', key).update(body, 'utf-8').digest('hex')}`;
}
// webhook handler function
exports.gongHandler = async event => {
  // get the GitHub secret from the environment variables
  const token = process.env.GITHUB_WEBHOOK_SECRET;
  const calculatedSig = signRequestBody(token, event.body);
  let errMsg;
  // get the remaining variables from the GitHub event
  const headers = event.headers;
  const sig = headers['X-Hub-Signature'];
  const githubEvent = headers['X-GitHub-Event'];
  const body = JSON.parse(event.body);
  // get repo variables
  const { repository, release } = body;
  const repo = repository.full_name;
  const url = repository.url;
  // set variables for a release event
  let releaseVersion, releaseUrl, author = null;
  if (githubEvent === 'release') {
    releaseVersion = release.tag_name;
    releaseUrl = release.html_url;
    author = release.author.login;
  }

  // check that a GitHub webhook secret variable exists, if not, return an error
  if (typeof token !== 'string') {
    errMsg = 'Must provide a \'GITHUB_WEBHOOK_SECRET\' env variable';
    return {
      statusCode: 401,
      headers: { 'Content-Type': 'text/plain' },
      body: errMsg,
    };
  }
  // check validity of GitHub token
  if (sig !== calculatedSig) {
    errMsg = 'X-Hub-Signature incorrect. Github webhook token doesn\'t match';
    return {
      statusCode: 401,
      headers: { 'Content-Type': 'text/plain' },
      body: errMsg,
    };
  }

  // if the event is a 'release' event, gong the Slack channel!
  const webhookUri = process.env.SLACK_WEBHOOK_URL;

  const slack = new Slack();
  slack.setWebhook(webhookUri);

  // send slack message
  if (githubEvent === 'release') {
    slack.webhook({
      channel: "#gong-test", // your desired channel here
      username: "gongbot",
      icon_emoji: ":gong:", // because Slack is for emojis
      text: `It's time to celebrate! ${author} pushed release version ${releaseVersion}. See it here: ${releaseUrl}!\n:gong:  https://youtu.be/8nBOF5sJrSE?t=11` // your message
    }, function(err, response) {
      console.log(response);
      if (err) {
        console.log('Something went wrong');
        console.log(err);
      }
    });
  }

  // (optional) print some messages to the CloudWatch console (for testing)
  console.log('---------------------------------');
  console.log(`\nGithub-Event: "${githubEvent}" on this repo: "${repo}" at the url: ${url}.`);
  console.log(event.body);
  console.log('---------------------------------');

  // return a 200 response if the GitHub tokens match
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      input: event,
    }),
  };

  return response;
};

Finally, I needed to add a package.json file so that I could use dependencies. When creating a function using an AWS SAM template, Stackery would do this for your automatically, but in this case I had to create the file and add the following myself:

{
  "private": true,
  "dependencies": {
    "aws-sdk": "~2",
    "slack-node": "0.1.8"
  }
}

I added, committed and pushed the new code, re-deployed my Serverless Framework stack, then added another GitHub webhook to a test repo. I created a GitHub release in my test repo, and waited in anticipation.

Milliseconds later, I hear the familiar click-click-click of Slack…

screenshot

Pretty awesome, if I do say so myself. 🔔

A few notes:

  • I used the slack-node NPM package to make life easier. I could have used the requests library or the built-in HTTPS library (and you can if you want to avoid using external dependencies).
  • The GitHub API is very helpful for figuring out what kind of response to expect from your webhook. That’s how I determined the values to set for releaseVersion, releaseUrl, author.
  • When you console.log() in your serverless function, the results can be seen in the AWS CloudWatch logs. Stackery provides a convenient direct link for each function.

screenshot

  • This serverless application should fit within your AWS free tier, but keep an eye on your logs and billing just in case.

If you’d like to make your own serverless gong, all of the configuration code is available in my Serverless Gong GitHub repository. Just create a new stack your Stackery account (you can sign up for a free trial if you don’t have one yet), choose Create New Repo as the Repo Source, and select Specify Remote Source to paste in the link to my repo as a template.

Add your GitHub and Slack environment parameters, deploy your stack, and sit back and wait for your Slack to gong!

Webhooks Made Easy with Stackery
Anna Spysz

Anna Spysz | November 08, 2018

Webhooks Made Easy with Stackery

Webhooks are about as close as you can get to the perfect serverless use case. They are event-driven and generally perform just one (stateless) function. So of course we wanted to show how easy it is to implement webhooks using Stackery.

Our newest tutorial, the Serverless Webhooks Tutorial, teaches you to create a GitHub webhook and connect it to a Lambda function through an API Gateway. Or, to put it simple terms: when your GitHub repository does a thing, your function does another thing. What that second thing does is completely up to you.

Here are some possible use cases of a GitHub webhook:

  • Connect your webhook to the Slack API and have Slack ping your team members when someone has opened a PR
  • Have your function deploy another stack when its master branch is updated
  • Expanding on that, you can even have your function deploy to multiple environments depending on which branch has been updated
  • Write an Alexa Skill that plays a certain song when your repository has been starred - the possibilities are endless!

The best part is, GitHub allows you to be very specific in what events you subscribe to, and you can further narrow down events in your function logic.

So for example, do you want to be notified by text message every time Jim pushes a change to the master branch of your repository, because Jim has been known to push buggy code? You can set that up using webhooks and Stackery, and never have master go down again (sorry, Jim).

Check out the tutorial to see what else you can build!

Building a Single-Page App With Stackery & React
Jun Fritz

Jun Fritz | October 30, 2018

Building a Single-Page App With Stackery & React

After completing this tutorial, you’ll have a serverless SPA built using Stackery and React. Stackery will be used to configure, deploy, and host our application which will be built using the React library.

The newest tutorial on our documentation site guides you through the process of building a Serverless Single-Page App using Stackery and React.

You’ll be using Stackery to set up the cloud resources needed to deploy, host, and distribute your single-page application. You’ll configure a Lambda function, an S3 Bucket, and a CloudFront CDN in this tutorial with the goal of keeping this application within AWS Free Tier limits.

By the end of this tutorial, you’ll have a fully-scalable backend and an organized React front-end to add to, and grow your application. Watch part one of the tutorial below to see what we’re building, or follow along with the plain-text version here.

Stay tuned for more serverless tutorials from Stackery!

Building Serverless Applications with AWS Amplify
Danielle Heberling

Danielle Heberling | October 24, 2018

Building Serverless Applications with AWS Amplify

So you want to use AWS Cognito to authenticate users and have your user pool, identity pool, and app client all set up in the AWS console. …the next question is how can you connect this with your React based frontend? While there are a few ways to go about doing this, this post is going to give you a brief overview on how to do this via a library called AWS-Amplify.

AWS-Amplify is an open source project managed by AWS described as “a declarative JavaScript library for application development using cloud services.” I liked this particular library, because it has a client first approach and abstracts away some of the setup required in the JavaScript SDK.

My favorite features of Amplify are: Authentication (via Cognito), API (via API Gateway), and Storage (via S3), but this library has a lot more to offer than just those features. This post will focus on how to authenticate users from a React based frontend…more specifically user signup that has an email address verification step.

The Setup

First you’ll need to setup a config file to reference your already created AWS resources (in this case the user pool, identity pool, and client id) in your /src folder. The file will look something like this :

src/config.js

export default {
   cognito: {
    REGION: ‘YOUR_COGNITO_REGION’,
    USER_POOL_ID: ‘YOUR_USER_POOL_ID’,
    APP_CLIENT_ID: ‘YOUR_APP_CLIENT_ID’,
    IDENTITY_POOL_ID: ‘YOUR_IDENTITY_POOL_ID’
  }
};

Then in your index.js file where you setup your react app, you’ll need to configure aws Amplify. It’ll look similar to this:

src/index.js

import React from ‘react’;
import ReactDOM from ‘react-dom’;
import Amplify from ‘aws-amplify’;

import config from ‘./config’;
import App from ‘./App’;

Amplify.configure({
  Auth: {
    mandatorySignIn: true,
    region: config.cognito.REGION,
    userPoolId: config.cognito.USER_POOL_ID,
    identityPoolId: config.cognito.IDENTITY_POOL_ID,
    userPoolWebClientId: config.cognito.APP_CLIENT_ID
  }
});

ReactDOM.render(
  <Router>
    <App />
  </Router>,
  document.getElementById(‘root’)
);

The mandatorySignIn property is optional, but is a good idea if you are using other AWS resources via Amplify and want to enforce user authentication before accessing those resources.

Also note that for now having a separate config file might seem a bit overkill, but once you add in multiple resources (i.e. Storage, API, Pub Sub etc.) you’ll want that extra config file to keep things easy to manage.

Implementation Overview

The signup flow will look like this:

  1. The user submits what they’ll use for login credentials (in this case email and password) via a signup form and a second form to type in a confirmation code will appear.
  2. Behind the scenes the Amplify library will sign the user up in Cognito.
  3. Cognito will send a confirmation code email to the user’s signup email address to verify that the email address is real.
  4. The user will check their email > get the code > type the code into the confirmation form.
  5. On submit, Amplify will send the information to Cognito which then confirms the signup. On successful confirmation, Amplify will sign the user into the application.

Implementation Part 1

First in your signup form component, you’ll need to import Auth from the Amplify library like this:

import { Auth } from ‘aws-amplify’;

As you create your form, I’d suggest using local component state to store the form data. It’ll look like your typical form with the difference being using the Amplify methods in your handleSubmit function whenever the user submits the form. The handleSubmit function will look like this:

 handleSubmit = async event => {
    event.preventDefault();

    try {
      const newUser = await Auth.signUp({
        username: this.state.email,
        password: this.state.password
      });
      this.setState({
        newUser
      });
    } catch (event) {
      if (event.code === ‘UsernameExistsException’) {
        const tryAgain = await Auth.resendSignUp(this.state.email);
        this.setState({
          newUser: tryAgain
        });
      } else {
        alert(event.message);
      }
    }
  }

On success, Amplify returns a user object after the signUp method is called, so I’ve decided to store this object in my component local state so the component knows which form to render (the signup or the confirmation).

Before we continue let’s go over a quick edge case. So if our user refreshes the page when on the confirmation form and then tries to sign up again with the same email address, they’ll receive an error that the user already exists and will need to signup with a different email address. The catch block demonstrates one way of handling that possibility by resending the signup code to the user if that email is already present in Cognito. This will allow the user to continue using the same email address should they refresh the page or leave the site before entering the confirmation code.

Implementation Part 2

So now the user is looking at the confirmation form and has their confirmation code to type in. We’ll need to render the confirmation form. Similar to the signup form it’ll look like a typical form with the exception being the function that is called whenever the user submits the confirmation form. The handleSubmit function for the confirmation form will look similar to this when using Amplify:

 handleConfirmationSubmit = async event => {
    event.preventDefault();

    try {
      await Auth.confirmSignUp(this.state.email, this.state.confirmationCode);
      await Auth.signIn(this.state.email, this.state.password);

      this.props.isAuthenticated(true);
      this.props.history.push("/");
    } catch (event) {
      alert(event.message);
    }
  }

So it is taking in the form data, using Amplify to confirm the user’s email address via the conformation code and signing in the user if successful. You can then verify if a user is signed in via props at the route level if you’d like. In this case, I arbitrarily named it isAuthenticated and redirected the user to the root path.

The complete docs for using the Auth feature of Amplify can be found here. We’ve only scratched the surface in this post, so go forth and explore the all of the different features that Amplify has to offer. I’ve found it has a very nice declarative syntax and is very readable for folks who are new to a codebase. For building further on your React-based serverless applications, I highly recommend Stackery for managing all of your serverless infrastructure backed up by seamless, git-based version control.

Stackery’s Quickstart Just Got Quicker—and More Useful
Anna Spysz

Anna Spysz | October 15, 2018

Stackery’s Quickstart Just Got Quicker—and More Useful

If you’ve been over to our documentation site lately, you may have noticed some changes. We’ve got a new look and some new tutorials, but the latest upgrade is our new Quickstart tutorial.

While the first version of our Quickstart just got you up and running with Stackery, version 2.0 also has you deploying a static HTML portfolio page to an API endpoint:

Oooh, fancy!

Once you’ve followed the tutorial and deployed your static site, you can customize the HTML with your own information and links to your projects. You can then follow our serverless contact form tutorial to give the contact form on your site functionality as well.

Want a preview? This YouTube video walks you through the entire Quickstart tutorial:

And be sure to visit our docs site regularly, as we have several new tutorials in the works. Stay tuned for a React application with a serverless backend - coming soon!

Deploy GraphQL APIs with Stackery
Sam Goldstein

Sam Goldstein | October 03, 2018

Deploy GraphQL APIs with Stackery

It’s been a busy month in Stackery engineering. Here’s a quick recap of what’s new in the product this week.

You can now use Stackery to configure and provision AWS AppSync GraphQL APIs, which is a serverless pay-per-invocation service similar to API Gateway, but for GraphQL! GraphQL resolvers can be connected to backend data sources like DynamoDB tables, Lambda functions, or HTTP proxies. You can read more about the using Stackery with GraphQL in the Stackery docs.

Trigger Lambda Function on Deploy

Does your deployment processes involve multiple commands that need to be run in a certain order? Stackery now provides the ability to mark any function as “Trigger on First Deploy” or “Trigger on Every Deploy”, which provides a clean mechanism to handle database migration, ship single page apps, and handle custom deploy logic across all your environments. To make this work Stackery sets up a CloudFormation Custom Resource in your project’s SAM template which is used to invoke the function when the stack is deployed. Read more in the Stackery Function Docs.

Reference Existing Cloud Resources

Teams are often deploying serverless stacks into existing cloud infrastructures. What happens when your Function needs to subscribe to an existing DynamoDB stream or be placed in an existing VPC? Stackery provides the ability to replace resources in a stack with a pointer to an already provisioned resource. This can be specified per environment which enables you to provision mock resources in dev/test environments but reference central infrastructure in production. Check out the “Use Existing” flag on resources like DynamoDb Tables or Virtual Networks

GitHub and GitLab bulk project import

No one wants to set up a bunch of AWS Serverless Application Model (SAM) projects with Stackery one by one so we built a 1 click importer which locates all your projects with a valid SAM template file (template.yaml) and sets them up to deploy and edit with Stackery. It works for both GitHub and GitLab and you can find it on the Stackery Dashboard homepage at app.stackery.io.

How to Write 200 Lines of YAML in 1 Minute
Anna Spysz

Anna Spysz | September 11, 2018

How to Write 200 Lines of YAML in 1 Minute

Last month, our CTO Chase wrote about why you should stop YAML engineering. I completely agree with his thesis, though for slightly different reasons. As a new developer, I’ve grasped that it’s crucial to learn and do just what you need and nothing more - at least when you’re just getting started in your career.

Now, I’m all about learning for learning’s sake - I have two now-useless liberal arts degrees that prove that. However, when it comes to being a new developer, it’s very easy to get overwhelmed by all of the languages and frameworks out there, and get caught in paralysis as you jump from tutorial to tutorial and end up not learning anything very well. I’ve certainly been there - and then I decided to just get good at the tools I’m actually using for work, and learn everything else as I need it.

Which is what brings us to YAML - short for “YAML Ain’t Markup Language”. I started out as a Python developer. When I needed to, I learned JavaScript. When my JavaScript needed some support, I learned a couple of front-end frameworks, and as much Node.js as I needed to write and understand what my serverless functions were doing. As I got deeper into serverless architecture, it seemed like learning YAML was the next step - but if it didn’t have to be, why learn it? If I can produce 200+ lines of working YAML without actually writing a single line of it, in much less time than it would take me to write it myself (not counting the hours it would take to learn a new markup language), then that seems like the obvious solution.

So if a tool allows me to develop serverless apps without having to learn YAML, I’m all for that. Luckily, that’s exactly what Stackery does, as you can see in the video below:

Creating Your First AWS RDS Database with Stackery
Toby Fee

Toby Fee | September 10, 2018

Creating Your First AWS RDS Database with Stackery

Once you’ve built your first Lambda, you’ll need a datastore.

AWS does have an official instruction guide to help with this, but the official AWS instruction guide is extensive. You need to set up at least four resources before you can deploy your first RDS table, and by the time their 2,000-word guide is done you still haven’t even selected your DB format.

Thankfully there’s a much easier way: The deployment service Stackery, can get your RDS up and running and hooked up to Lambdas in just a few minutes.

Getting Started with RDS

AWS’s Relational Database Service (RDS) is a good choice if you’re familiar with SQL databases already or just prefer the reliable classic of tables over NoSQL.

At first glance, RDS looks like it’s basically just database hosting from Amazon, but in reality, it’s very different from their traditional server options hosted on EC2. When looking the tooling closely, it’s obvious RDS is as different from a traditional DB as a Lambda is different from a web server.

A few key differences:

  1. No OS management — While you can select the exact DB type and version (e.g., PostgreSQL, MySQL, MariaDB, Oracle, Microsoft SQL Server, Aurora) the operating system level is fully virtualized
  2. AWS’s usual restrictive access policies — Centrally, this is a positive thing: Amazon forces you to be in tight control of what services can access each other. As a result most beginners often end up stumped when, after trying to stand up their RDS, they want to connect to it from their laptop.
  3. Indescribably smooth uptime management — With the concept of an Availability Zone (AZ) you can create multiple instances that Amazon can automatically use for failover

(Seen here: two availability zones in action)

  1. And finally, backups and scaling are simpler — Because if virtualization doesn’t make scaling and backups easier, what the heck is it even for, right?

Deploy an RDS Table

Start by creating a new Stack in the Stackery UI and deleting the default nodes. We’ll add one node for our database and a second for a lambda to access DB data. You’ll need to set a DB name. Note that this is not the database name for DB requests, just the name it’ll be referred to in the Stackery UI. Set a root password here as well. Be sure to record the password somewhere for later use in this tutorial.

For the moment, our Lambda function will be fine with the default settings, but we’ll edit it later to retrieve records from the database.

This tutorial doesn’t cover populating the database in detail, since the most likely scenario is that you’ll have test data you’d like to populate. Let’s set up our Lambda to load records from the account DB.

To get access to edit your Lambda’s code: commit this stack in the Stackery UI (no need to deploy yet), then click the commit ID to go to the stack code in Github.

Next, clone the repository. Then you can edit it in whatever editor you prefer.

Let’s populate the Lambda with some code to access records from the DB:

const knex = require('knex');
cont dbInfo = {
  development: {
    client: 'mysql',
    connection: {
      host: 'localhost',
      user: 'root',
      password: 'mySecretPW',
      database: 'accounts'
    }
  },

  production: {
    client: 'mysql',
    connection: {
      host,
      user: 'root',
      password: process.env.DB_PASSWORD,
      database: 'accounts'
    }
  }
};

const connectionName = process.env.CONNECTION || 'development';
const connection = dbInfo[connectionName];

/**
 * Fetch list of accounts from database and respond with an array of account
 * names.
 */
module.exports = async message => {
  const client = knex(connection);

  try {
    const records = await client('accounts').select('name');
    return records.map(record => record.name);
  } finally {
    client.destroy();
  }
};

A few things to note about this code:

  • As is the default for new Lambda Resources in Stackery, this is written in Node 8, with support for finally{}.
  • This code relies on inline connection info, but you’ll more likely want to share that info between multiple functions — See how to share code modules between Javascript functions in a separate guide.
  • If you’ve written code to access a database from a web app, you might be surprised to discover we’re creating and destroying a client for every request. Since we’re deploying this code to a serverless environment, it’s critical that we don’t max out our connections on smaller RDS instances. It’s quite easy for multiple requests to our Lambda to result in a point of 65 simultaneous connections, which is the limit on a db.t2.micro instance — We’ve previously discussed database connection usage in a serverless context on our blog.

Push the new Lambda code up to the GitHub repository, and refresh the Stackery UI to show your updates.

The last step is to give the Lambda function the database password. It expects to find it as a config variable for this Lambda — which you can set from the Stackery UI — but this isn’t an ideal place to put the password, since it will be visible in both the Stackery UI and the GitHub repo for this stack.

Instead, set two environment variables CONNECTION to production and DB_PASSWORD to ${config.dbPassword}, so the password will be populated from the environment config.

You can access ‘environments’ from the menu at the top right. Finally, set any environment variables as standard Javascript Object Notation (JSON).

Now your stack should be ready to commit, prepare, and deploy!

Get the Serverless Development Toolkit for Teams

Sign up now for a 60-day free trial. Contact one of our product experts to get started building amazing serverless applications today.

To Top