This project shows an example of how you might use PostGraphile on Lambda. It has the following aims:
http
-based libraries (such as Connect,
Express, Koa)Postgraphile-lambda-example does NOT intend to watch the schema for changes; this means that you must build and release a new version every time you change your database. (You only need to update the cache file though.)
Postgraphile-lambda-example does NOT intend to make this fully compatible with
the postgraphile
CLI - this will be a subset best suited to Lambda usage.
Postgraphile-lambda-example does NOT intend to support subscriptions.
We use the following tools:
node_modules
any more!)writeCache
/ readCache
- we'll introspect the database
during the build and write the results to a cache file to be included in the
bundle; then when the Lambda service starts up it can read from the cache
rather than introspecting the database again.First clone this repository locally, and install dependencies:
yarn
Next, set up a .env
file matching your environment:
cp .env.template .env
And modify the src/postgraphileOptions.js
and serverless.yml
files to your taste.
When you're deploying Postgraphile using Lambda, you can run your DB instance on AWS as well in order to make use of AWS's integrated security using VPCs. In that case you can restrict the public accessibility of your DB instance from the internet. If you don't host your DB on AWS you can ignore this section.
When using RDS for example, our use case requires two ways of access:
Achieving this can be a bit confusing if you're new to VPCs. When you create your RDS instance, set the following "Network & Security" settings:
PostgresQL
restricted to your current IP address as source. This will satisfy our second requirement. After you created your instance, click on "Modify" and return to your security group settings. Now add the default
security group as the second one. This security group allows other entities within the VPC (e.g. our Lambda function) to access this entity (the RDS instance) which is needed for our first requirement. You might have expected that entities are able to access other entities within the same VPC by default, but you have to add this security group explicitly.If you want to learn more, here's some more info on VPCs in the context of RDS and on security groups.
Now, we just have to make sure that our Lambda is created within our VPC as well. For that just use the AWS_VPC environment variables from the .env.template
and add the following to the specifications of your graphql
function in your serverless.yml
file:
vpc:
securityGroupIds:
"Fn::Split":
- ","
- ${env:AWS_VPC_SECURITY_GROUP_IDS}
subnetIds:
"Fn::Split":
- ","
- ${env:AWS_VPC_SUBNET_IDS}
You can find all these values in the RDS console under "Connectivity & Security". (As the securityGroupId it's enough to use the default
one: this basically makes the Lambda function part of the VPC. There should be 3 subnetIds.)
(You will also need to add "iam:AttachRolePolicy"
to the permissions of the Serverless IAM role policy you will later create.)
Hint: Don't forget that your access to RDS from your local computer is based on your current network, so when you want to e.g. re-generate the Postgraphile schema, but are now connected to a different network, you'll have to return to the rds-launch-wizard
security group settings, edit the inbound rule and select "My IP" at source to automatically update your IP.
This repository runs bash scripts during deployment written on Mac which you can find in the scripts
folder. These scripts should run just fine on Mac and Linux, but you might run into problems on Windows. As a workaround you can just run Linux within Windows and run the deployment scripts there. If you're on Windows 10 you can install a command line Linux distro from the Microsoft Store - there is a guide further below. If you're using another version of Windows, you could run Linux in a VM (or possibly a Docker container).
yarn global add serverless
"s3:GetBucketLocation"
)Now you can deploy to AWS using serverless.js by running:
yarn deploy
sudo apt update
to get access to the latest packages.aws-cli
to provide serverless with AWS credentials to create the stack on AWS on your behalf. aws-cli
relies on python3 which is also used by the Linux system, but in a different version. In order to avoid version conflicts/incompatibilites, we install the aws-cli
in a virtual environment which comes with its own python installation.)pip install --upgrade awscli
(from within the venv). You can make sure it installed correctly by running aws --version
."s3:GetBucketLocation"
.)aws configure
(more details).yarn global add serverless
and zip with sudo apt-get install zip
.yarn deploy
.Just copy the URL that Serverless returns in the command line under endpoints
after successful deployment and paste it into your GraphQL client of choice - you can now talk to your Lambda PostGraphile API 😅
If you prefer not to use the serverless.js framework, you can also deploy your lambda function manually.
Note 1: Change your process.env.AWS_STAGE_NAME to "/default" to match the default stage name for manually deployed API Gateways.
Note 2: CORS is enabled by default. Remove cors() middleware in /src/index.js
if you would prefer disabled cors.
yarn build
to create lambda.zip
file that you can upload to Amazon Lambda.lambda.zip
file you generated above; then click "Save"DATABASE_SCHEMAS
and DATABASE_URL
settings; then click "Save"/
route under Resources
and from the "Actions" dropdown, select "Create method" add create an ANY
methodIf you want GraphiQL support (STRONGLY DISCOURAGED! Use an external GraphQL
client such as GraphiQL.app, Altair or GraphQL Playground instead!), then you
need to go back to stage 9, and choose 'Create Resource', tick "Configure as a
proxy resource", press "Create Resource" and then configure it with the name of
your lambda function, you should also change the settings in
src/postgraphileOptions.js
(see comment in that file).
The system operates based on a number of phases. Each phase depends on the previous non-optional phase; so if an earlier phase rebuilds then all later phases must also rebuild.
scripts/build
Uses webpack to produce a single JS file containing all that is necessary,
using src/index.js
as the entry point.
Compiles src/**
to dist/
Start here when: you change your code, add/remove plugins, or upgrade dependencies.
scripts/generate-cache
Uses a similar approach to postgraphile --write-cache
to write a cache file
containing introspection details of your database.
Generates dist/postgraphile.cache
Start here when: database schema changes.
scripts/bundle
Produce a zip file combining the two artifacts above - dist/index.js
and dist/postgraphile.cache
.
Generates lambda.zip
from dist/
folder
scripts/test
Launch the bundle in the sam local
test environment, and run a series of requests.
Manual checking of the results is required.
Left as an exercise to the reader.
pip install aws-sam-cli
Install dependencies
yarn
Copy .env.template to .env and customize as you like:
cp .env.template .env
If you're using the default .env.template
file then you'll need to populate the
postgraphile_forum_example
database:
./scripts/import-example-database
Make sure that the query in test/query.graphql
and the options in src/postgraphileOptions.js
are both valid for your database. If you're using a remote PostgreSQL server (or one within a docker instance), you may need to update the host.docker.internal
reference in test/make-template-yml.sh
(line 15).
Run the tests:
yarn test
Note the first run might take a while whilst the system installs the relevant docker images.
In the test output you should see a number of 0 error(s)
statements, and some successful GraphQL HTTP request payloads
Do the same as for the test, but instead of running yarn test
at the end, instead run:
yarn sam
This will set up a local GraphQL endpoint at http://127.0.0.1:3000/graphql
You can then use a GraphQL client such as Altair or GraphQL Playground to issue requests.
If you're using the sample database then you can generate a JWT via:
mutation {
authenticate(input: { email: "spowell0@noaa.gov", password: "iFbWWlc" }) {
jwtToken
}
}
Then set the JWT header:
{
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZm9ydW1fZXhhbXBsZV9wZXJzb24iLCJwZXJzb25faWQiOjEsImlhdCI6MTUzODEyOTEyMSwiZXhwIjoxNTM4MjE1NTIxLCJhdWQiOiJwb3N0Z3JhcGhpbGUiLCJpc3MiOiJwb3N0Z3JhcGhpbGUifQ.NFZ10gvIB29VL1p3Wh-Cc74JSigOOhgtqaMCP9ZA2W0"
}
Then you can issue an authenticated query:
{
currentPerson {
nodeId
id
fullName
}
}
Note that SAM unpacks the zip and reboots node for every single request, so you're going to suffer some startup latency with this.
yarn deploy
, sometimes they can be resolved by just running the command another time. At the time of writing (20/Mar/2019), there is also a temporal error with Serverless v1.39: "Can't find graphql.zip file". If this happens downgrade to Serverless 1.38 by running yarn global add serverless@1.38
.serverless remove
, you may run into errors. If that happens, you can go to Cloudformation in the AWS console and delete your stack there.A PostGraphile lambda example using Zeit now.sh to manage the deployment: https://github.com/ggascoigne/now-postgraphile
Improvements to PostGraphile's support for Lambda were sponsored by Connecting Good