Goldstack's Lambda Express module provides a basic Express.js server that is deployed as an AWS lambda function.
The following key properties need to be configured for this module:
https://api.mydomain.com/
the API domain
api.mydomain.com
needs to be configured.
api.mydomain.com
,
the hosted zones api.mydomain.com
or
mydomain.com
would be valid. For more details,
please check
Hosted Zone Configuration
in the Goldstack documentation.
ui.mydomain.com
the CORS header
https://ui.mydomain.com
should be supplied. To
learn more about CORS, see the
Cross-Origin Resource Sharing (CORS)
in the MDN docs.
The first thing we recommend to do with a new module is to stand up the
infrastructure for the module. For this, find the directory for this
module in the packages/
folder and navigate to
this folder in the command line. Then identify the name of the deployment
you have defined in the Goldstack configuration tool. This can be found in
the packages/[moduleName]/goldstack.json
file.
Look for the "deployments"
property and there
for the "name"
of the first deployment. The
name should either be dev
or
prod
.
In order to stand up the infrastructure, run the following command:
yarn infra up [deploymentName]
This will be either yarn infra up dev
or
yarn infra up prod
depending on your choice of
deployment. Note that running this command can take a while.
Note that your API will not work yet. It first needs to be deployed as per instructions below.
Once the infrastructure is successfully set up in AWS using
yarn infra up
, we can deploy the module. For
this, simply run the following command:
yarn deploy [deploymentName]
This will either be yarn deploy dev
or
yarn deploy prod
depending on your choice of
deployment during project configuration.
You should now be able to access your API. The domain under which the API is
deployed is configured in goldstack.json
under
"deployments[*].apiDomain"
. You can access this
API domain with a browser since the default API provided in the template
allows for GET requests to the root.
The source code for the express server is defined in the
src/
folder. The entry point for defining new
routes is in src/server.ts
. The easiest way to
get started extending the API is to modify or add new routes to the server
here.
All infrastructure for this module is defined in Terraform. You can find the
Terraform files for this module in the directory
[moduleDir]/infra/aws
. You can define multiple
deployments for this module, for instance for development, staging and
production environments.
If you configured AWS deployment before downloading your project, the
deployments and their respective configurations are defined in
[moduleDir]/goldstack.json
.
The configuration tool will define one deployment. This will be either
dev
or
prod
depending on your choice during project
configuration. In the example
goldstack.json
below, a deployment with the name
dev
is defined.
{
"$schema": "./schemas/package.schema.json",
"name": "...",
"template": "...",
"templateVersion": "...",
"configuration": {},
"deployments": [
{
"name": "dev",
"awsRegion": "us-west-2",
"awsUser": "awsUser",
"configuration": {
...
}
}
]
}
Infrastructure commands for this module can be run using
yarn
. There are four commands in total:
yarn infra up
: For standing up infrastructure.
yarn infra init
: For
initialising Terraform.
yarn infra plan
: For running
Terraform plan.
yarn infra apply
: For running
Terraform apply.
yarn infra destroy
: For destroying all
infrastructure using
Terraform destroy.
For each command, the deployment they should be applied to must be specified.
yarn infra [command] [deploymentName]
For instance, to stand up the infrastructure for the
dev
deployment, the following command would need
to be issued:
yarn infra up dev
Generally you will only need to run
yarn infra up
. However, if you are familiar with
Terraform and want more fine-grained control over the deployment of your
infrastructure, you can also use the other commands as required.
Goldstack modules make it very easy to customize infrastructure to your
specific needs. The easiest way to do this is to simply edit the
*.tf
files in the
infra/aws
folder. You can make the changes you
need and then run
yarn infra up [deploymentName]
to apply the
changes.
The infra/aws
folder contains a file
variables.tf
that contains the variables
required for your deployment; for instance the domain name for a website.
The values for these variables are defined in the module's
goldstack.json
file in the
"configuration"
property. There is one global
configuration
property that applies for all
deployments and each deployment also has its own
configuration
property. In order to add a new
variable, add the variable to variables.tf
and
then add it to the configuration for your module or to the configurations
for the deployments.
Note that due to JavaScript and Terraform using different conventions for
naming variables, Goldstack applies a basic transformation to variable
names. Camel-case variables names are converted to valid variables names for
Terraform by replacing every instance of a capital letter
C
with _c
in the
variable name. For instance:
myVariableName
in the Goldstack configuration
will translate to the Terraform variable
my_variable_name
as defined in
variables.tf
.
In order to manage your infrastructure, Terraform maintains a state for each
deployment; to calculate required changes when the infrastructure is updated
and also for destroying the infrastructure if it is no longer required.
Goldstack by default will store the terraform state in the
infra/aws
folder as simple files.
This works well for deploying infrastructure from your local development environment but is not a good choice when building a CI/CD pipeline for the infrastructure definition. In that case, it is better to define Remote State. A popular choice many projects adopt here is to store the state in an S3 bucket. Please see the Terraform documentation for further details.
This module can be packaged up and deployed to the deployments specified in
goldstack.json
. Note that deployment will only
work after the infrastructure for the respective deployment has
been stood up. To deploy your module, run the following script:
yarn deploy [deploymentName]
Environment variables are defined in the Terraform source code for this
module. Specifically they are defined in the
infra/aws/lambda.tf
file in the resource
resource "aws_lambda_function" "main"
. By default,
there is one environment variable specified that identifies the Goldstack
deployment used.
environment {
variables = {
GOLDSTACK_DEPLOYMENT = var.name
}
}
Add your environment variables into the
variables
map:
environment {
variables = {
GOLDSTACK_DEPLOYMENT = var.name
YOUR_ENV_VAR = 'your env var value'
}
}
Usually environment variables should have different values depending on which environment the server is deployed to. This can be accomplished using Terraform variables. Change your variable declaration to the following:
YOUR_ENV_VAR = var.my_env
Then go into the file infra/aws/variables.tf
and
add the following definition:
variable "my_env" {
description = "My environment variable"
type = string
}
And finally add this variable to all deployment configurations in
goldstack.json
:
"configuration": {
"lambdaName": "my-lambda",
"apiDomain": "api.mysite.com",
"hostedZoneDomain": "mysite.com",
"cors": "https://mysite.com",
"myEnv": "Value for deployment"
}
Note that the Terraform variable my_env
translates
to myEnv
in the JSON definition (Just remove all
_
and make the first character after
_
uppercase for your variable definitions).
Lastly, to support local development make sure to define the variable
correctly in all scripts
in
package.json
. Specifically, you may want to define
them for "test"
,
"test:ci"
and "watch"
.
"test": "MY_ENV=localvalue jest --passWithNoTests --watch --config=jest.config.js",
"test:ci": "MY_ENV=localvalue jest --passWithNoTests --config=jest.config.js --detectOpenHandles",
"watch": "PORT=8731 MY_ENV=localvalue nodemon --config nodemon.json --exec 'yarn node dist/src/local.js'"
Note that for credentials and other values that should not be committed to
source code, it may be better to store these in AWS Secrets Manager and
retrieve them using the AWS SDK based on the
process.env.GOLDSTACK_DEPLOYMENT
value provided.
It is also possible to provide the value of Terraform variables through
environment variables during build time. For instance, if you have defined the
variable my_env
, simply provide the environment
variable MY_ENV
when calling
yarn infra
.
MY_ENV=value yarn infra up prod
This works very well in combination with secrets for GitHub actions.
- name: Deploy API
run: |
yarn workspace my-api infra up prod
env:
MY_ENV: ${{secrets.MY_ENV}}
AWS_USER_NAME: goldstack-prod
AWS_ACCESS_KEY_ID: ${{secrets.PROD_AWS_ACCESS_KEY_ID}}
AWS_SECRET_ACCESS_KEY: ${{secrets.PROD_AWS_SECRET_ACCESS_KEY}}
AWS_DEFAULT_REGION: us-west-2
This module requires further security hardening when deployed in critical
production applications. Specifically the lambda is given the role
arn:aws:iam::aws:policy/AdministratorAccess"
and
this will grant the lambda access to all resources on the AWS account,
including the ability to create and destroy infrastructure. It is therefore
recommended to grant this lambda only rights to resources it needs access to,
such as read and write permissions for an S3 bucket. This can be modified in
infra/aws/lambda.tf
in the resource
resource "aws_iam_role_policy_attachment" "lambda_admin_role_attach"
.