Railgun
Features
- Yarn Workspaces
- Devcontainers
- Typescript
- TRPC
- AWS SAM Boilerplate
- API Gateway + Lambda + Lambda Authorizer
- Cloudfront + S3 + Lambda@Edge to host landing page and web app within a single S3 bucket
- Offline processor using Eventbridge + SQS + Lambda
- Web, Mobile & Chrome Extension Boilerplate
- EAS to create custom Expo builds
- Shared UI and logic packages
- CI/CD with Github Actions
- Deploy to Staging and Production environments using separate Cloudformation stacks
- Build your Chrome extension right from Github Actions. It will updgrade the manifest version automatically
- Create EAS Internal and App Store distribution builds for Android/iOS
- Custom domain names for landing page, web app, and API
- Google SSO
- DynamoDB, Gmail, OpenAI clients
Installation
The steps below assume that you have Google Oauth apps created for Web, Android, iOS as well an Expo account
Create your first EAS build (Adjust the env in apps/mobile/eas.json
and owner, name, slug
fields in apps/mobile/app.config.js
to suit your needs)
-
Install and configure Docker for your operating system.
-
Install the Dev Containers extension in VS Code.
-
Fork the repository.
-
Run
cp -n .devcontainer/devcontainer.env.example .devcontainer/devcontainer.env
. You will need to change the credentials in a later step if you want to setup NoSQL Workbench -
Replace
reponame
inpackages/api/sam/lambda/aws-toolkit-tsconfig.json
with your repository name. -
Add appropriate host permissions in
apps/extension/public/manifest.json
to ensure cookie authentication works as expected. e.g."host_permissions": ["https://api.<your-domain-name>.com/", "https://api.stagin.<your-domain-name>.com/", "http://localhost:3001/"]
-
If you would like to inject a user ID into your lambda functions when testing locally, follow these steps:
a. Create
samdev
directory in the rootb. Pull a local version of the SAM CLI into
samdev
c. Remove
.git
file inaws-sam-cli
to not create a git submoduled. In
aws-sam-cli/setup.py
, change line 42 tocmd_name = "sam"
fromcmd_name = "samdev"
e. In
aws-sam-cli/samcli/local/events/api_event.py
, add"authorizer": { "userID": 1234 }
to the
to_dict
function in theRequestContext
class. You can add any custom values you want to be passed to the lambda function. For more information, follow this guide -
Press
Command + Shift + P
and type "Open folder in container" to open the repository in a dev container. The container will take a few minutes to startup. -
Add any sensitive or missing environment variables to
apps/.env
andpackages/api/env.json
for local development.
IMPORTANT NOTE: When developing the React Native app on your own mobile device, you need to replace all localhost
references in apps/.env
with your host ip address. e.g. 192.168.X.X
-
Follow these steps if you would like to use NoSQL Workbench to view your local dynamo db tables:
a. Download NoSQL Workbench.
b. Once installed, click on the "Operation builder" tab
c. Click "Add connection"
d. Click on the "DynamoDB local" tab
e. Choose a connection name and click "Connect"
f. Within the connection, click on the key button to view the credentials NoSQL Workbench assigned this connection
g. Copy the Access key ID and Secret access key to your
.devcontainer/devcontainer.env
fileh. Completely delete and restart the container so that the Dynamo DB local container can be restarted with the correct credentials.
Development
Running the App
Entire Stack
yarn start:web:extension
to run the web app, extension and api in watch mode or yarn start:web:debug
to run the web app, extension and api in debug mode
or
yarn start:web
& yarn start:web:debug
to run the web app and api in watch mode
or
yarn start:extension
& yarn start:extension:debug
to run the extension and api in watch mode
Note: Running the entire stack with mobile is not supported since you need your host machine to run the simulators
API Gateway
cd packages/api
- Open a new terminal and run
yarn start
. This will build watch your lambda functions and start the SAM local api.
Frontend
Web
- Open a new terminal and
cd apps/web
yarn start
Extension
- Open a new terminal and
cd apps/extension
yarn start
- Upload the generated build folder to Extensions Manager
- You can reload the extension by using this handy extension
Mobile
- Open a new terminal outside of the devcontainer and
cd apps/mobile
yarn start
- Open your custom build on a device or simulator. Consult expo documentation for your specific use case if needed.
Debugging Lambda Functions
- Run the api using
yarn start:debug
- Set breakpoints in the lambda function you want to debug
- Call the lambda function you want to debug from your app or a tool like postman
- Go to the Run and Debug panel in VSCode
- Run the "Attach to SAM CLI" debug configuration
- Click the play button the first time the debugger pauses as the debugger pauses on entry for an unknown reason. After clicking play the first time, the debugger will pause on the first breakpoint you set
Adding Dynamo DB Tables
-
In
packages/api/template.yaml
, create a newAWS::DynamoDB::Table
similar to the existing table resources in the template. Follow the AWS Documentation to configure the table -
When naming the table, ensure the name format follows: TableName${Environment}. Suffixing the table name with the environment name allows for a multi cloud formation stack architecture.
-
Add a new configuration to
packages/api/sam/lambda/localDynamoTables
so that the dev container can start up with the appropriate tables. -
For the newly created table to propogate locally you can either:
a. Rebuild the container
b. Manually run
dynamodb:sync
. This will create new tables added based on new configurations added and leave existing tables untouched.c. Manually run
dynamodb:sync:clean
. This will delete all tables and recreate all tables from scratch. This is helpful when trying to update an existing table. -
To make use of the new table when using the DynamoDB client:
import DynamoDBClient from '../utils/dynamoDBClient';
...
# Inside your handler or function
const dynamoDBClient = new DynamoDBClient();
await dynamoDBClient.client.putItem({
TableName: DynamoDBClient.getTableName('<TABLE_NAME>'),
Item: {},
});
Adding Functions
- In
packages/api/template.yaml
, create a newAWS::Serverless::Function
similar to the existing serverless function resources in the template. Follow the AWS Documentation to configure the function. - Add your handler code to
packages/api/sam/lambda/handler/[name].ts
. Below is a trpc handler template:
import { APIGatewayProxyEvent } from 'aws-lambda';
import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
import { initTRPC } from '@trpc/server';
import DynamoDBClient from '../utils/dynamoDBClient';
import { requestHandler } from '../utils/trpcRequestHandler';
const createContext = ({ event }: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>) => ({ event });
export const t = initTRPC.context<typeof createContext>().create();
export const appRouter = t.router({
handlerName: t.procedure.mutation(async (req) => {
const userID = req.ctx.event.requestContext.authorizer?.userID;
const dynamoDBClient = new DynamoDBClient();
await dynamoDBClient.client.putItem({
TableName: DynamoDBClient.getTableName('<TABLE_NAME>'),
Item: {},
});
// OTHER CODE HERE
}),
});
export type <LAMBDA_NAME>Router = typeof appRouter;
export const handler = requestHandler({ router: appRouter, createContext });
-
Ensure you have
yarn build:watch
running to rebuild as the function changes -
To use your function within the
common
package, import the Router type, exported from the lambda, into the common object you want to call the function in. Then usethis.trpc.<handler_name>.<query | mutate>()
Interacting with the API
The common
package in packages/common
is the go to package to make calls to the backend. Create new objects here for your frontend apps to use. With this approach, if the method of getting data from the backend changes for a specific frontend function, you should only need to modify what is in the common package instead of making changes to all frontend apps.
Clients
Included in this framework are some basic client interfaces for you to work with within the SAM application. Currently supported clients include: DynamoDB, Gmail, and OpenAI. These clients exist within packages/api/sam/lambda/utils
Setting up custom domain names
To connect your custom domain with API Gateway and S3, it may be easier to deploy to Staging and Production manually before using the available Github Actions in case there is any debugging required. The command to deploy is below. Add the appropriate environment variables in the --parameter-overrides option. (You can find all possible environment variables to set in packages/api/env.json
)
sam deploy sam deploy --config-env staging --parameter-overrides='EnvironmentParameter=Staging DynamoDBRegionParameter=us-east-1 GoogleOauthClientIDParameter=<YOUR_CLIENT_ID> GoogleOauthClientSecretParameter=<YOUR_CLIENT_SECRET> DomainNameParameter=staging.<YOUR_DOMAIN>.com FullApiDomainNameParameter=https://api.staging.<YOUR_DOMAIN>.com FullDomainNameParameter=https://www.staging.<YOUR_DOMAIN>.com OpenAISecretKeyParameter=<YOUR_SECRET>'
When deploying the SAM app to Staging or Production, you will need to validate the certificate AWS creates by adding the appropriate CNAME's to your DNS provider.
- Go to AWS Certificate Manager and click on the certificate created by the deployment
- Add the CNAME within the certificate to your DNS to verify. Once set wait for verification to complete
- Add a CNAME to direct
https://api.staging.<YOUR_DOMAIN>.com
to API Gateway. E.g. Name =api.staging
& Value =API Gateway URL
- Add a permanent redirect to cloudfront. Forward
<YOUR_DOMAIN>.com
tohttps://www.<YOUR_DOMAIN>.com
Deployment
- Add appropriate Github secrets for use in Github Workflows (
.github/workflows/
). You can add the secrets in the repository settings under "Secrets and variables". - Manually run workflows to deploy or build your apps