DEV Community

Cover image for Using all new Amazon API Gateway HTTP APIs with Serverless Framework
Serverless Guru, LLC
Serverless Guru, LLC

Posted on

Using all new Amazon API Gateway HTTP APIs with Serverless Framework

It is easy to build and maintain large scale APIs with Amazon API Gateway. From REST to WebSockets APIs, HTTP Proxies to workloads with request transformation, authentication and validation.

One common integration for building APIs is to proxy it to an AWS Lambda. Listening to their customers feedback, Amazon went one step further and on last year re:Invent they released a new feature called HTTP APIs for Amazon API Gateway.

HTTP APIs offer the core functionality of Amazon API Gateway and are optimised for building APIs that proxy to AWS Lambda functions or HTTP backends. With cost savings up to 70% compared to REST APIs, significant performance improvements (without the Amazon API Gateway service overhead), out of the box features like throttling, metrics, logging, OIDC, OAuth and built-in support for CORS.

What is the difference between HTTP APIs and REST APIs in Amazon API Gateway?

As mentioned above, HTTP APIs are focused and optimised for APIs proxying request to an AWS Lambda or HTTP backends. Any other offers and features from REST APIs aren't available. I will list some features heres, but they are not exhaustive and can change, since Amazon can ship more (or less) features anytime.

What features ARE NOT AVAILABLE while using HTTP APIs?

  • Custom Authorizers: AWS Lambda and IAM (You can use Amazon Cognito as a JWT issuer only)
  • Integration: HTTP, AWS Services, Private Integration or Mocks (we can use HTTP as a proxy integration only)
  • API Management: Usage Plans, API Keys, custom domains with TLS 1.0 or wildcard custom domains names.
  • Development: Cache, Request transformation, Request/Response validation, Test Invocation
  • Security: Client Certificates, AWS WAF, Resource policies
  • API Type: Edge-optimized, Private (HTTP APIs are Regional only)
  • Monitoring: Access logs to Amazon Kinesis Data Firehose, AWS X-Ray

While the list above can be scary and maybe you been thinking "...damn, that is too much, I'll keep using REST APIs". I dare you to think again, usually, we do not use 90% of the features on this list, but we would be paying for it if we misconfigured our projects.

With HTTP APIs, AWS helps us to falling into the pit of success, with enhanced performance, cheaper with great features and an easier developer experience.

This service is still in Beta, with fast improvements and moves towards General Availability. You can stay up to date following this AWS page.

Building your first HTTP API with Serverless Framework

At Serverless Guru, we love helping you to migrate, build or train your team on serverless best practices.

Last week, our official partner Serverless, released its support for HTTP APIs with Serverless Framework.

We can now leverage the awesome features of Serverless Framework to build products using all new Amazon API Gateway HTTP APIs!

Let's review the following serverless.yml file:

service: example-http-api-service

provider:
    name: aws

functions:
  getItems:
    handler: handler.getItems
    events:
      - httpApi: "GET /items"

Even with all the differences between HTTP APIs and REST APIs, Serverless Framework decided to propose a new event, httpApi to attach functions to HTTP APIs in your serverless.yml file, keeping the syntax familiar and easy to migrate.

Exploring the HTTP verbs, we have more routing options, like:

...
functions:
  ...
  postItem:
    handler: handler.postItem
    events:
      - httpApi: "POST /items"
  getItem:
    handler: handler.getItem
    events:
      - httpApi: "GET /items/{id}"
  putItem:
    handler: handler.putItem
    events:
      - httpApi: "PUT /items/{id}"
  deleteItem:
    handler: handler.deleteItem
    events:
      - httpApi: "DELETE /items/{id}"
...

If we need to customise our httpApi event, we can use the object syntax:

...
  getReceipt:
    handler: handler.getReceipt
    events:
      - httpApi:
          method: GET
          path: /receipt/{receiptId}
          timeout: 5
...

We can define a catch all route using:

...
  catchAll:
    handler: handler.catchAll
    events:
      - httpApi:
          path: "*"
...

Protecting your routes with JWT Authorizers

By default, all endpoints are publicly accessible. To protect our routes, HTTP API supports JSON Web Tokens, commonly used by OAuth 2.0 and Native OpenID workflows, called JWT Authorizers.

We can restrict access to our endpoints by configuring a JWT Authorizer in our provider section:

provider:
  ...
  httpApi:
    authorizers:
      myCustomJwtAuthorizer:
        identitySource: $request.header.Authorization
        issuerUrl: https://...
        audience:
          - xxxx
          - xxxx

To authorise your request, Amazon API Gateway uses a general workflow of 4 steps to validate your token by calling your JWT Authorizer. Make sure your issuer is following these requirements.

In case you wanna use Amazon Cognito we could use the following syntax:

provider:
  ...
  httpApi:
    authorizers:
      customCognitoAuthorizer:
        identitySource: $request.header.Authorization
        issuerUrl: https://cognito-idp.${region}.amazonaws.com/${cognitoPoolId}
        audience:
          - cognitoClientIDWeb
          - cognitoClientIDMobile

After declaring a JWT Authorizer, we can define which endpoints we want restrict access by configuring an authorizer key:

functions:
  ...
  getProfile:
    handler: handler.getProfile
    events:
      - httpApi:
          method: GET
          path: /me
          authorizer: customCognitoAuthorizer

If we need to extend the authorisation to include scopes to our endpoint, we can change it to:

functions:
    ...
  getProfile:
    handler: handler.getProfile
    events:
      - httpApi:
          method: GET
          path: /me
          authorizer:
            name: customCognitoAuthorizer
            scopes:
              - user.id
              - user.email

Fine grain CORS control

With built-in support for CORS in HTTP API, Serverless Framework made it simple for its users by defining a cors: true property by default:

provider:
  httpApi:
    cors: true

This default behaviour implies and ensure the following headers:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Headers: Content-Type, X-Amz-Date, Authorization, X-Api-Key, X-Amz-Security-Token, X-Amz-User-Agent
  • Access-Control-Allow-Methods: OPTIONS + all methods you defined in your route (GET, POST etc)

In case you need a better control over it, you can manipulate the provider.httpApi.cors property and define:

provider:
  ...
  httpApi:
    ...
    cors:
      allowedOrigins:
        - https://my-allow-domain.com
        - https://allow-this-one.com
      allowedHeaders:
        - Authorization
        - Content-Type
      allowedMethods:
        - GET
        - POST
      allowCredentials: true
      exposedResponseHeaders:
        - Special-Response-Header
      maxAge: 6000 # in seconds

Turning on Access Logging

Similar to REST APIs, we can turn on access logs in HTTP API by using provider.logs.httpApi like:

provider:
  ...
  logs:
    ...
    httpApi: true

If you have a particular log format, you can use the .format property:

provider:
  ...
  logs:
    ...
    httpApi:
      format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength" }'

The custom format above is used by default when you use provider.logs.httpApi: true, but can be used as a reference in case you wanna change it.

All HTTP API options reference

To illustrate the possibilities with Serverless Framework, we can define the following options when using HTTP APIs:

provider:
  httpApi:
    id: ApiID # ID of externally created HTTP API to attach resources
    timeout: 5
    cors: true
    authorizers:
      myJwtAuthorizer:
        identitySource: $request.header.Authorization
        issuerUrl: https://cognito-idp.${region}.amazonaws.com/${cognitoPoolId}
        audience:
          - xxxx
          - xxxx
  logs:
    httpApi: true

functions:
  myFunction:
    handler: myFunction.handler
    events:
      - httpApi:
          method: GET
          path: /url-path/{param}
          timeout: 5
          authorizer:
            name: myJwtAuthorizer
            scopes:
              - user.id
              - user.email

As we can see, the syntax is familiar and easy to use. Making the usage of HTTP APIs friendly and approachable for all projects already using Serverless Framework.

Conclusion

HTTP APIs are designed for low-latency, cost-effective AWS Lambda proxy and HTTP proxy APIs. HTTP APIs support OIDC and OAuth 2.0 authorization, and come with built-in support for CORS.

When should I used it?

With something quite new, it is always a hot topic! HTTP APIs main features orbiting around:

  • JWT Authorizers for JWT AuthN / AuthZ
  • Proxy based API, using Lambda or HTTP backend
  • Built-in CORS support

If you are already using or can support JWT token, most of your requests are being proxied to an AWS Lambda and you require CORS for your clients. You should use or migrate to HTTP API. With an infrastructure optimized and enhanced for these features, you will see great cost reduction with better performance.

With this new flavour in Amazon API Gateway, the enhanced performance and lower costs can be a huge win when building JWT based APIs.

The service is still limited to a few regions, but if you deploying workloads on them already, get ready to pair it up with Serverless Framework and leverage all the advantages today!

Written by Serverless Guru Eduardo Rabelo
@oieduardorabelo
Alt Text

Top comments (1)

Collapse
 
juancpgo profile image
Juan • Edited

Great post! But how do you make the JWT Authorizer work with tokens issued by Cognito?? Cognito's Access Tokens have a "scope" claim, but don't have an "aud" claim (it uses "client_id" instead), and their ID Tokens have an "aud" claim but don't have a "scope" claim.