Like all infrastructure stacks, there is some soft of backend, and frontend. In this case we have a front end implemented with API Gateway and a simple backend with 2 tables and a SQS queue to trigger a function.
In order to use this stack you simply run the make buildAll
and make deployAll
command from within the ./backend
directory.
In order to gain fine control over each endpoint of your API this allows you to separate your project into distinct directories to lighten global dependencies.
This will define an example Serverless infrastructure stack containing:
- an API Gateway The API has 6 endpoints. One to create a user, one to get a user information, one to get a list of users, one to get a list of orders for a user, one to get order information for that user, and one to create an order.
- two DynamoDB tables One is the user table and one is the order table.
- a SQS queue An SQS queue that looks out for orders and moves them to fulfillment.
- one backend lambda function, and three api lambda functions The backend function will look for messages in the Order queue, then move them to fulfillment. The API functions are split into 3 endpoints where you can implement different packages scope.
/user
GET - List of users.
/user
POST - Create a user.
/user/{userid}
GET - User information.
/user/{userid}/orders
GET - Get order information for user.
/order
POST - Create an order.
/order/{orderid}
GET - Get order information.
Take note in the ./backend/serverless.yml
we are exporting two variables from the stack. This is for reuse in our child API endpoint stacks:
#export from ./backend/serverless.yml
- Outputs:
ApiGWRestApiId:
Value:
Ref: ApiGatewayRestApi
Export:
Name: ${self:custom.${self:provider.stage}.Stack}-restApiId-${self:provider.stage}
ApiGWRootResourceId:
Value:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
Export:
Name: ${self:custom.${self:provider.stage}.Stack}-rootResourceId-${self:provider.stage}
#import from ./api/src/user/serverless.yml
provider:
...
apiGateway:
restApiId:
'Fn::ImportValue': ${self:custom.${self:provider.stage}.Stack}-restApiId-${self:provider.stage}
restApiRootResourceId:
'Fn::ImportValue': ${self:custom.${self:provider.stage}.Stack}-rootResourceId-${self:provider.stage}
Special Consideration: When nesting resources within other resources, for example we have the API endpoint of /user/{userid}/orders
. This endpoint is served separately from our /user
endpoint, lets say you are using AWS Cognito for authentication, you can keep these dependencies separate from dependencies that access business function, like /user/{userid}/orders
accesses the Orders table alone.
How is this accomplished?
- We first export the shared resources from the parent resource
/user
.
#export from ./api/src/user/serverless.yml
resources:
Outputs:
ApiRootUser:
Value:
Ref: ApiGatewayResourceUser
Export:
Name: ${self:custom.${self:provider.stage}.Stack}-ApiRootUser-${self:provider.stage}
ApiRootUserUseridVar:
Value:
Ref: ApiGatewayResourceUserUseridVar
Export:
Name: ${self:custom.${self:provider.stage}.Stack}-ApiRootUserUseridVar-${self:provider.stage}
- We then import this shared resources as a
restApiResources
in the child resource/user/{userid}/orders
#import from ./api/src/user/order/serverless.yml
provider:
...
apiGateway:
restApiId:
'Fn::ImportValue': ${self:custom.${self:provider.stage}.Stack}-restApiId-${self:provider.stage}
restApiResources:
/user/{userid}:
'Fn::ImportValue': ${self:custom.${self:provider.stage}.Stack}-ApiRootUserUseridVar-${self:provider.stage}
First it will run yarn install
in the ./backend
directory, then it will look at each directory in the ./backend/src
directory and run yarn install
for each, then it will run make buildAll
from the ./api
directory. This will look at each directory in the ./api/src
directory and run yarn install
for each.
First it will run serverless deploy --stage dev
in the ./backend
directory and then it will run make deployAll --STAGE='dev'
from the ./api
directory. This will look at each directory in the ./api/src
directory and run serverless deploy --stage dev
for each.
This will make a new endpoint in the ./api/src/billing
directory. It will preload it with the serverless packages for the basic framework as well as set a baseline serverless.yml template.
First it will run serverless remove --stage dev
in the ./backend
directory and then it will run make removeAll --STAGE='dev'
from the ./api
directory. This will look at each directory in the ./api/src
directory and run serverless remove --stage dev
for each.
First it will remove .serverless/
and node_modules/**
in the ./backend
directory and then it will run make removeAll --STAGE='dev'
from the ./api
directory. This will look at each directory in the ./api/src
directory and remove .serverless/
and node_modules/**
for each.
This will run serverless deploy --stage dev
for the ./src/_root
endpoint.
This will run yarn install
for the backend code. If you run this command without a SERVICE
variable it will build the serverless.yml
dependencies.
This will remove the serverless project, deleting the backend infrastructure.
This will remove the .serverless
and node_modules/**
directory for the backend. If you run this command without a SERVICE
variable it will clean the serverless.yml
dependencies.
This will run serverless deploy --stage dev
for the ./src/user
endpoint.
This will run yarn install
for the backend code.
This will remove the serverless project, deleting the backend infrastructure.
This will remove the .serverless
and node_modules/**
directory for the backend.
- In more complicated examples you would be able to use AWS Cognito in the
/user
endpoint to set up authentication. This endpoint would be scoped for user functions around Cognito and will likely have similar imports. - You could also import Stripe in a
/billing
endpoint to facilitate collection of payment information. - Within the
/orders
endpoint, you can set up your DynamoDB queries for managing your order collection. - You could set up CI/CD simply by adding a CodePipeline resource, and utilizing CodeBuild to pull down this repository, and run the make files.
- Use the
serverless-domain-manager
plug-in to enable domain functionality. Most of this structure is laid out, you just need to provide a validApiHostedZone
,ApiSite
, andApiCert
. This can be created in the AWS Console for Route53 and ACM and provided here as variables.