Ruby & AWS Lambda, 💖 BFF 💖

As a Serverless enthusiast at Codegram it was about time I published a new blog post since AWS Lambda has introduced official support for Ruby 🎉.  

You can read more at the official release blog post which also has an example Sinatra app to get started. If you want an in-depth review of the example and release, I recommend this nice review from EquiValent.

I was thrilled to learn that I can finally use Ruby with Lambda, but using Sinatra as an example feels wrong. If you want to build an API with it, you would want to have API Gateway do all the routing and request handling. Adding an extra layer to do the same at the application level is unnecessary and seems like an anti-pattern (I understand it’s used as an example of migrating microservices, but it’s still confusing).


After talking about it with my fellow Codegrammers I decided to build a small proof-of-concept GraphQL API with Ruby and Lambda, using Serverless to simplify the deployment and infrastructure, without Sinatra, Rack or any boilerplate.

The objective was to demonstrate that you can build an API and almost forget that you’re actually dealing with web requests, just focus on the GraphQL schema and the business logic.

How does it look like? There are three key elements:

serverless.yml exposes our function through an HTTP POST:

service: serverless-ruby-graphql

provider:
  name: aws
  runtime: ruby2.5
  region: eu-west-1
  stage: dev

functions:
  api:
    handler: app.request
    events:
      - http:
          path: api
          method: post

plugins:
  - serverless-hooks-plugin

custom:
  hooks:
    package:initialize:
      - bundle install --deployment
    deploy:finalize:
      - rm -fr .bundle
      - rm -fr vendor
      - bundle install

app.rb handles the incoming requests and delegates all the work to GraphQL Ruby:

require 'json'
require_relative "app/graphql/schema"

def request(event:, context:)
  puts "Received Request: #{event}"

  body = Schema.execute(event["body"]).to_json

  {
    statusCode: 200,
    body: body
  }
rescue StandardError => e
  puts e.message
  puts e.backtrace.inspect

  {
    statusCode: 400,
    body: JSON.generate("Bad request, please POST a request body!")
  }
end

And finally, app/ a folder with all the GraphQL schemas and models:

https://github.com/oriolgual/serverless-ruby-graphql/tree/master/app

Although this works OK, keep in mind that it is not production-ready code.

Let's build everything with Lambda

Well, maybe not. Building things with AWS Lambda and Ruby is straightforward, but would I build my next GraphQL API with it? Probably not.

I’m not entirely sold on using AWS Lambda with user-facing APIs; response times are too unpredictable and quite high (yes, even if you keep your Lambdas warm). Things get [even]((https://www.robertvojta.com/aws-journey-api-gateway-lambda-vpc-performance/) slower if your Lambda needs to access resources inside a VPC (which is necessary if you want to access a database for example).

I really like Lambda, but I think it shines when used to respond to events (such as S3 uploads or Kinesis streams) where you don’t really care about some extra latency but scalability can be an issue.

I’m eager to try something similar with Knative, Kubeless or OpenWhisk. So far I’ve only used AWS Lambda, and I’d like to compare it to other solutions and get a better idea whether it would be a good option to deploy serverless, user-facing APIs.