Guides & Tutorials

Saving with Serverless

Side projects are especially awesome when they help you solve a real life problem. During a recent problem-solving expedition I encountered a road block that I’d bet prevents a lot of us developers from finishing our side projects: just because a solution is possible, doesn’t mean it’s affordable. If this is sounding familiar, you probably know that infrastructure can be pretty cost prohibitive, both in time and money. Here’s a look at how I used the serverless framework to ship my side project without any additional cost.

The Problem

When I first to moved to the San Francisco bay area I ran into an interesting issue: people here actually go to baseball games (I’m from Tampa, Florida, where people only attend baseball games when the home team plays a better team). On game days, my commute between San Francisco and Oakland was nuts. Things were even crazier when the Oakland Athletics and the San Francisco Giants played against each other in the Battle of the Bay series. During those weeks, public transportation newbs rode the train system all day acting confused while taking up as much space as possible.

I realized I could make my commute easier by answering the question, ‘When is baseball?’

So, when is baseball?

I built an app called Hustlin that knows when there is a home game and sends notifications at the start and anticipated end times. I generated the project really quickly using Ruby on Rails and deployed to production using Heroku.

hustlin-preview-image

This was amazing until I realized I had to pay $7 a month to keep the thing up and running during the 7-month long season. I give the app some love every new baseball season, so I eventually rebuilt it to use the JAMstack, separating the API from the markup. The frontend was easily hosted on Netlify for free as a React application, but I wanted to find something just as free to host my API. The solutions I came across were going to either cost more in money or time to set up and maintain. The API was costing too much just to optimize my commute.

jamstack vs severless cost

I hosted David Wells from Serverless team on an episode of JAMstack Radio and discovered everything I did could be done with Serverless and hosted for free on AWS. Plus, AWS’s Lambda gives you 1 million invocations of functions for free.

If you are not familiar with the server-less, or Functions-as-a-service(FaaS), they are functions that execute on-demand and in a matter milliseconds. Their use can vary from small automated tasks to replacing large process in a devops pipeline with very few limitations.

The switch to Serverless

AWS is one of the providers the Serverless Framework works with out of the box and the CLI made it easy to try out. My simple JSON for home baseball games fits nicely in an AWS-provided DynamoDB table. To get started I used the CLI to deploy the node templates.

# Create a new Serverless Service/Project
$ serverless create --template aws-nodejs --path serverless-hustl
# Change into the newly created directory
$ cd serverless-hustl

After creating the boilerplate I created a seed function to move my existing JSON to a DynamoDB table. This was significantly less code than my previous version of Hustlin, it was 102 lines of code to be exact.

// function that seed the DynamoDB table
module.exports.seed = (event, context, callback) => {
  baseballs.forEach((data) => {
    const {name, start_time, end_time, started, standard_start_time} = data;
    const item = {
      id: `${data.id}`,
      name,
      start_time,
      end_time,
      started: `${started}`,
      standard_start_time
    };

    dynamodb.put({TableName: 'slshustl', Item: item}, (err) => {
      if (err) {
        callback(err);
      }
      const response = {
        statusCode: 201,
        headers,
      }

      callback(null, response);
    });
  })
}

I created a second function, called today, that returns all the games happening that today with start time and location information.

// function that returns all games happening today

module.exports.today = (event, context, callback) => {
  const params = {
    TableName: 'slshustl',
  }

  dynamodb.scan(params, (err, data) => {
    if (err) {
      callback(err);
    }

    const todaysGames = data.Items.filter(isToday)
    const response = {
      statusCode: 200,
      headers,
      body: JSON.stringify({
        count: todaysGames.length,
        data: todaysGames
      })
    };

    callback(null, response);
  });

  function isToday(game) {
    const today = new Date();
    const gameTime = new Date(game.start_time);
    return (today.toDateString() == gameTime.toDateString());
  }
}

After creating my seed and today functions I exposed them as endpoints in the serverless.yml. The original version cost me $7/month for the convenience of simple deployments, that same simplicity is why I never attempted to host my project elsewhere until now. The Serverless framework handles all the complication of setting up API Gateway, Updating DynamoDB, and deploying my Lambda functions.

// serverless.yml
service: slshustl

provider:
  name: aws
  runtime: nodejs6.10
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "dynamodb:*"
      Resource: "arn:aws:dynamodb:*:*:table/slshustl"

functions:
  seed:
    handler: handler.seed
    description: seed dynanomodb table with baseball games
    events:
      - http:
          path: seed
          method: post
  today:
    handler: handler.today
    description: return just baseball games today
    events:
      - http:
          path: today
          method: get
          cors: true

resources:
  Resources:
    DynamoDbTable:
      Type: 'AWS::DynamoDB::Table'
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: slshustl

If you are interested in taking a closer look at the code check out bdougie/serverless-hustl.

json hustlin api

I have completely switched to DynamoDB to store my baseball game JSON data. I also used cron jobs to send notifications. I leveraged the aws-node-scheduled-cron example repo to trigger my notifications, which is live at bdougie/scheduled-hustlin-notifications. After reading through the Serverless documentation as well some heavy copy and pasting, I was able mirror what I was getting from my expensive Postgres database with simple JSON in a DynamoDB table.

Profit

This switch has saved me 100% of the $84 a year I was paying previously. Now that I am saving on time and money, I can start working on making this project provide real time notifications during baseball games and really see if that 1 million invocations can be achieved. If you have interest in this project, please keep an eye open for notifications at hustlin.netlify.com for the 2018 season.

Keep reading

Recent posts

How do the best dev and marketing teams work together?