Sharing API Gateway with Multiple Services in Serverless Framework

A short how-to on sharing one API resource with multiple services in serverless framework.

Sharing API Gateway with Multiple Services in Serverless Framework

While working on my little side project JSONPerf (check it out: jsonperf.com), I stumbled upon an interesting problem I thought was worth sharing with you.

A quick summary of what JSONPerf is will help us understand the need before discussing the solution.
The project's goal is to benchmark JSON libraries' performance in different programming languages. One of the features it provides is uploading a JSON file and benchmarking the different libraries on the user's specific file.

To implement this feature, I decided to implement the per-language benchmark endpoint using AWS Lambda as it provides me flexibility and I can use a different runtime for each function.

The problem I stumbled upon is the fact that in my serverless framework definitions file, I need to have multiple endpoints ( /python3, /python2, /java etc.) under the same API, each using a different language (runtime).

Unfortunately, this quickly turned out to be impossible because the python requirements plugin currently doesn't support my setup.
It allows you to specify different requirements file per function, but only if the file is called requirements.txt and the code is in different folders (github issue).
In my case, both Python 2 and Python 3 functions share the same code but have different requirements (different libraries to compare).

To solve it, I created a main serverless.yml that declares an API Gateway which is then shared between the other, per-function serverless.yml files.

Main (API) serverless.yml file

service: api 

provider:
name: aws
stage: dev
region: us-east-1

resources:
Resources:
  ApiGw:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: ApiGw

Outputs:
  apiGatewayRestApiId:
    Value:
      Ref: ApiGw
    Export:
      Name: ApiGw-restApiId

  apiGatewayRestApiRootResourceId:
    Value:
      Fn::GetAtt:
        - ApiGw
        - RootResourceId
    Export:
      Name: ApiGw-rootResourceId

Services files, Python 2 example python/serverless_py2.yml

service: python2

plugins:
  - serverless-python-requirements
  - serverless-wsgi

package:
  exclude:
    - 'venv*/**'

custom:
  wsgi:
    app: app.app
    packRequirements: false
  pythonRequirements:
    fileName: requirements_py2.txt
    dockerizePip: non-linux
    slim: true

provider:
  name: aws
  stage: dev
  region: us-east-1
  runtime: python2.7
  apiGateway:
    restApiId:
      'Fn::ImportValue': ApiGw-restApiId
    restApiRootResourceId:
      'Fn::ImportValue': ApiGw-rootResourceId
    websocketApiId:
      'Fn::ImportValue': ApiGw-websocketApiId

functions:
  python2:
    handler: wsgi_handler.handler
    events:
      - http:
          method: post
          path: /python2
          cors: false

This is of course just an example and you can tweak it as much as you want with the flexibility you gain.

Hope this will help someone in finding the little hidden documentation to accomplish that using serverless framework.