shark-on-lambda
shark-on-lambda
provides a lightweight framework to write Ruby on AWS Lambda
whilst using a familiar approach to web services known from e. g. Ruby on Rails.
History
For a long time, "going serverless" on AWS Lambda was a world Ruby developers only could explore using methods such as packing their own Ruby binaries, going the JRuby way, running on the Java VM, or using Ruby On Jets.
When AWS went public with the Ruby 2.5 runtime for AWS Lambda at the end of 2018, that changed for Ruby developers and suddenly, a whole new world opened.
Since it is possible to run any Rack-based application on AWS Lambda, you can even run your own Rails application there, if you want to.
However, if you prefer a more lightweight solution, shark-on-lambda
may be
of interest to you, as it offers you a similar approach to things whilst
maintaining a smaller memory footprint.
Changelog
Have a look at the actual changelog.
Upgrading?
If you are upgrading from a previous version of shark-on-lambda
, these
upgrade guides might be of some help to you.
Installation
Add this line to your application's gems.rb
:
gem 'shark-on-lambda'
And then execute:
$ bundle
Or install it yourself:
$ gem install shark-on-lambda
Handlers
Handlers are the entry points for Lambda function invocation, e. g. when they are triggered by HTTP requests on the API Gateway. They also are responsible for responding with a well-formed response to the caller.
class MyHandler < SharkOnLambda::ApiGatewayHandler
end
By inheriting from SharkOnLambda::ApiGatewayHandler
, your own handler
class is indirectly tied to your controller class by convention: It assumes
the controller name is MyController
and it will dispatch events to that
controller.
If you however bring your own class with a different name, you can configure your handler to use that controller instead:
class MyHandler < SharkOnLambda::BaseHandler
self.controller_class = AnotherController
end
All controller methods are going to be automagically mapped to class methods on the handler class.
class MyController < SharkOnLambda::BaseController
def index
end
def show
end
end
class MyHandler < SharkOnLambda::ApiGatewayHandler
end
MyHandler
will respond to .index
and .show
class methods that accept the
event
and context
objects from the API Gateway. Those are passed to
MyController
and eventually, the controller method that corresponds to the
handler class method is called.
Controllers
Controllers are similar to Rails controllers: You have access to params
,
request
, and response
objects that contain informatoni retrieved from the
AWS Lambda event
object.
"Basic" controllers
You also have access to the render
and redirect_to
.
class MyController < SharkOnLambda::BaseController
def index
# Make the API Gateway respond with a 201 response saying "Hello, World!"
# in the response body.
#
# The default status code for `render` is 200.
render 'Hello, World!', status: 201
end
def show
# Does what you think it does.
#
# The default status code for `redirect_to` is 302.
redirect_to 'https://github.com', status: 307
end
end
before_action
, around_action
, and after_action
filters also are available,
as well as rescue_from
.
JSON API-compliant controllers
If you inherit your controller from SharkOnLambda::JsonapiController
,
render
and redirect_to
will create JSON API-compliant responses.
You however must have a serialiser for the objects you want to render. Otherwise, rendering will fail and you will receive an Internal Server Error instead.
JSON API fields
and include
query string parameters are automagically
being parsed and used for rendering automagically by the JSON API renderer.
JSON API serialisers
We use jsonapi-serializable
(and jsonapi-rb
in general) for our
JSON API compatibility. Therefore, we expect the serialisers to be inherited
from ::JSONAPI::Serializable::Resource
(or ::JSONAPI::Serializable::Error
).
class SomethingSerializer < JSONAPI::Serializable::Resource
type :somethings
attributes :foo, :bar, :baz
end
Serialiser lookup
Each object that is to be serialised requires a serialiser class that knows how to serialise it. We also implemented a convention over configuration approach here to determine which serialiser class to use:
-
If the object is an instance of
YourClass
andYourClassSerializer
is defined,YourClassSerializer
is used as the serialiser class. -
If the object is an instance of
YourClass
andYourClassSerializer
is not defined, check whether 1) applies for any of the ancestors ofYourClass
.
Example
If YourClass
has YourBaseClass
, Object
, and BasicObject
as ancestor
classes, the first existing one of YourClassSerializer
,
YourBaseClassSerializer
, ObjectSerializer
, and BasicObjectSerializer
(in that order) is used. If none of those exist, serialisation will fail with
an Internal Server Error.
Configuration
You can "initialise" SharkOnLambda
using its .initialize!
class method.
SharkOnLambda.initialize!
yields to a block with the config
and secrets
objects where you can access and add to those two objects.
SharkOnLambda.initialize! do |config, secrets|
# Do things here.
end
Calling SharkOnLambda.initialize!
does these things (in order):
- Process the block passed to
.initialize!
. - Load
config/settings.yml
andconfig/settings.local.yml
into theconfig
object. - Load
config/database.yml
andconfig/database.local.yml
intoconfig.database
. - Load
config/secrets.yml
andsecrets/secrets.local.yml
into thesecrets
object. - Load all
config/initializers/*.rb
files.
If SharkOnLambda.config.stage
was set inside the block passed to
.initialize!
, configurations and secrets for that stage will be merged into
the default set (the default
node in the YAML files) of configuration and
secrets, overwriting values where applicable.
Test helpers
By including SharkOnLambda::RSpec::Helpers
in your RSpec test suite, you can
use delete
, get
, patch
, post
, and put
methods, which will return
a Rack::MockResponse
object. You can also access that response object in your
test examples by calling response
, but only after you've called either of the
aforementioned methods. Otherwise, an exception will be raised.
You can include the test helpers like this in your spec/spec_helper.rb
.
RSpec.configure do |config|
config.include SharkOnLambda::RSpec::Helpers
end
JSON API helpers
By including SharkOnLambda::RSpec::JsonapiHelpers
, you gain all the goodies
from SharkOnLambda::RSpec::Helpers
and access to jsonapi_data
and
jsonapi_errors
methods, which contain the data
and errors
keys of the
parsed response body respectively. In addition to that, there is
jsonapi_attributes
, which returns the attributes
key from jsonapi_data
.
Rack compatibility
As SharkOnLambda.application
is a Rack-compatible application, treating it
as such and using existing Rack middleware is straightforward.
Using Rack middleware
The middleware stack can be found at SharkOnLambda.config.middleware
. Adding
middleware to your stack can be either done by calling #use
:
SharkOnLambda.config.middleware.use Your::Middleware
You can also just set up your middleware stack during your
SharkOnLambda.initialize!
call:
SharkOnLambda.initialize! do |config, secrets|
config.middleware.use Your::Middleware
end
Development
Clone this repository and change away. Then, once you are done, please submit a pull request at https://github.com/Skudo/shark-on-lambda/pulls.
However, please make sure the tests (bundle exec rake spec
) and rubocop
(bundle exec rubocop
) pass before submitting a pull request. Pull requests
that do not pass both on the CI system will not be merged. On the same note,
untested code will not be merged, either.