Type Safe API Custom Integrations: ECS with NLB #186
cogwirrel
started this conversation in
Show and tell
Replies: 1 comment 1 reply
-
Thanks for this! Any support for the ECS + ALB on your roadmap? |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Introduction
The
@aws-prototyping-sdk/type-safe-api
package lets you define an API using a declarative model written in either Smithy or OpenAPI, and handles generating infrastructure, clients, types and documentation. See the documentation for more details.You can use the built in
Integrations.lambda
integration type to implement your API operations, but what if you want to run your API operations somewhere else? For this, you can use custom integrations.You can define custom integrations by extending the
Integration
class. You will need to implement therender
method to produce the API Gateway Integration object which will be added to the OpenAPI definition for the given operation, as the x-amazon-apigateway-integration OpenAPI extension property.In this discussion post, we’ll demonstrate how custom integrations can be used to implement your API using Elastic Container Service (ECS) with a Network Load Balancer (NLB). We'll use Express, but still make use of the generated handler wrappers so that we can take advantage of type-safety when implementing our operations.
Project Setup
In the usual way, create a monorepo which will be the base of your project:
mkdir smithy-ecs-workshop cd smithy-ecs-workshop npx projen new --from @aws-prototyping-sdk/nx-monorepo
Add a Dependency on Type Safe API
Update your
.projenrc.ts
to add a dependency on@aws-prototyping-sdk/type-safe-api
:Then synthesize your project by running:
Synthesizing your project will generate the projects you have defined in your
.projenrc.ts
file. Changes to project structure and configuration are defined as code. For more details, take a look at the Projen Documentation.Set up the API, Server and Infrastructure Projects
In your
.projenrc.ts
, we’ll add three more projects:TypeSafeApiProject
- this will be used for defining our API and provides generated infrastructure, clients, types and documentationTypeScriptProject
- a basic typescript in which we'll implement our Express serverAwsCdkTypeScriptApp
- a simple CDK app for deploying your API quickly. Note that in a production application you'd likely opt for a CI/CD pipeline to manage your deployments (egPDKPipelineTsProject
) instead.We can now synthesize and build our "empty" suite of projects:
ECS Infrastructure
Inside
packages/infra/src
, let’s define a new fileload-balanced-ecs-service.ts
which will deploy our express service on ECS. This includes a VPC, the service within the VPC private subnet, and a VPC Link for API Gateway to connect to our service within the VPC:Notice that the docker image points to the server package’s
docker-image
folder. We’ll define this later on!Custom Integration
Next, in
packages/infra/src/nlb-integration.ts
, let's define a custom integration which allows us to point API operations to the NLB via the VPC Link:Note here that we’re adding the resource path as a header. This can then be used to map requests to a particular operation by the handler router, which we'll define later on. We also need to tell API Gateway what path parameters to inject into the request that’s forwarded on to our NLB.
Create the Api Infrastructure
Next, we'll use the generated
Api
construct to define our API Gateway infrastructure, and use ourNlbIntegration
to point operations at ourLoadBalancedEcsService
.Edit
packages/infra/src/main.ts
:Implement the Express Server
Next we'll implement our express server which will run on ECS.
Mapping Requests and Responses
First, we'll write some mapping code to allow us to make use of our generated, type safe handler router, even though we're not running on lambda. This will mean the operations we implement will benefit from all the generated types.
To use the handler router, we need to map Express requests to
APIGatewayProxyEvent
s, andAPIGatewayProxyResult
s back to Express responses. To do this, let's definemapRequest
andmapResponse
methods inpackages/server/src/mapper.ts
as follows:Note that this may not be a fully comprehensive example - there may be more properties of the request that you need to map, and the path parameter extraction logic is quite basic and may not cover all your use cases!
Say Hello Operation Implementation
We can implement our operation in
packages/server/src/say-hello.ts
using the generated lambda handler wrapper:Implement the Server Entrypoint
We can put the mappers and the generated handler router together in the server entry point in
packages/server/src/index.ts
. This creates the handler router, registering handlers for each operation. It defines a proxy route (/*
) which uses the router to match requests with the appropriate handler.Since you likely deleted the default sample code (ie
class Hello ...
) inindex.ts
, make sure you delete the sampletest/hello.test.ts
file which referenced it!Bundle our server
Next, we’ll add a step to our server’s package task in the
.projenrc.ts
file to bundle our server implementation and its dependencies into a singleserver.js
file. This will make it easier to package into a docker container.Docker File
Let’s create a docker file under
packages/server/docker-image/Dockerfile
:This docker file copies our server implementation, and starts the server up as its launch command.
Synthesize, Build and Deploy!
Make sure you're running Docker since the deployment will build a docker container.
Since we updated the
.projenrc.ts
we’ll need to synthesize again. After that we can build all the packages again.After you have set up AWS credentials for your target AWS account (eg. run
aws configure
), you can deploy the CDK application:Once the deployment has completed, you'll see your API URL printed as a CloudFormation output.
Since we used IAM (Sigv4) authentication for our API, we'll need to sign requests to our API. You can use awscurl as an easy way to call it from your command line:
Wrapping Up
We’ve implemented our API using Express running in ECS behind an NLB, by making use of a custom integration which connects API Gateway to the NLB via a VPC Link.
We’ve gone one step further and have made use of the generated handler wrappers to provide us with type-safety when implementing our API operations, even though we’re not making use of AWS Lambda.
Beta Was this translation helpful? Give feedback.
All reactions