Shopify-Remix App in AWS Lambda local development setup
Shopify Remix AWS Lambda Local Development Setup
When deploying a Shopify-Remix app to AWS Lambda, one of the services that is required is a database. In my work environment that usually means MySQL or Postgres. Since we are deploying to AWS the cloud version of MySQL or Postgres is RDS.
The SST stack that I use as a reference creates the RDS intance inside of a VPC. This is a good practice for security reasons. However, it makes local development a bit more difficult. The VPC is a private network and the RDS instance is not accessible from the public internet. This means that you can't connect to the RDS instance from your local machine.
When you run npx sst dev
and npm run dev
the app will start up and you can access it from your browser. However, the app will not be able to connect to the RDS instance. For this reason I have created a local development setup that uses a local MySQL instance.
Prerequisites
This tutorial assumes some setup from Remix template that I used in the previous post.
Specifically, it assumes that Prisma has been replaced with Kyseley.
Create a local MySQL instance
For local development I will use a local MySQL instance instead of the AWS RDS instance. I will use Docker to run the instance.
Create the docker-compose file which will start a MySQL instance.
#####################################
#
#####################################
services:
db:
platform: linux/x86_64
image: mysql:8
volumes:
- mysqldb:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=password123
- MYSQL_DATABASE=shopify_app
ports:
- "127.0.0.1:3306:3306"
volumes:
mysqldb:
Run docker-compose up
to start the MySQL instance.
Update the .env file
Update the .env
file to point the local Remix app to the local MySQL instance.
SHOPIFY_API_KEY=somekey
SHOPIFY_API_SECRET=somesecret
SCOPES=read_products,write_products
DB_NAME=shopify_app
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=password123
Create a local stack
First let's create a "local" stack that we can use for local development. This stack will use a local MySQL instance instead of RDS.
See my previous post for the full setup. I am going to copy the stack that I created in that post and use it as a starting point.
Copy stacks/shopify.app.stack
to shopify.local.app.stack
. Next remove the rds
definition so that the stack only contains: vpc
, security group, and the RemixSite
.
shopify.local.app.stack
import { SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
import { RemixSite, StackContext } from "sst/constructs";
const ShopifyLocalApp = (context: StackContext) => {
const {app, stack} = context;
/**
* Create a VPC with a single NAT gateway
*
* @param {Stack} stack
* @param {App} app
* @returns {Vpc}
*/
const vpc = new Vpc(stack, app.logicalPrefixedName('net'), { natGateways: 1 });
/**
* Create a default security group for lambda functions
*
* @param {Stack} stack
* @returns {SecurityGroup}
*/
const defaultLambdaSecurityGroup = new SecurityGroup(stack, 'DefaultLambda', {
vpc: vpc,
description: 'Default security group for lambda functions',
});
/**
* Set the default function props
*/
app.setDefaultFunctionProps({
vpc: vpc,
securityGroups: [defaultLambdaSecurityGroup],
});
/**
* Create a Remix site
*
* @param {Stack} stack
* @returns {RemixSite}
*/
const site = new RemixSite(stack, "site", {
runtime: "nodejs20.x",
cdk: {
server: {
vpc: vpc,
securityGroups: [defaultLambdaSecurityGroup]
}
},
environment: {
SHOPIFY_APP_URL: process.env.SHOPIFY_APP_URL || "",
SHOPIFY_API_KEY: process.env.SHOPIFY_API_KEY || "",
SHOPIFY_API_SECRET: process.env.SHOPIFY_API_SECRET || "",
SCOPES: process.env.SCOPES || "",
DB_HOST: process.env.DB_HOST || "",
DB_NAME: process.env.DB_NAME || "",
DB_USERNAME: process.env.DB_USERNAME || "",
DB_PASSWORD: process.env.DB_PASSWORD || "",
DB_PORT: process.env.DB_PORT || "",
}
});
stack.addOutputs({
url: site.url,
});
};
export default ShopifyLocalApp;
We need to update the sst.config.ts
file to switch between the local and prod stacks.
sst.config.ts
stacks(app) {
if (app.stage !== "prod") {
app.stack(ShopifyLocalApp);
} else {
app.stack(ShopifyApp);
}
app.setDefaultRemovalPolicy("destroy");
I use two stages, "prod" and "wwright". The "wwright" stage is my personal stage that I use for local development. The "prod" stage is the stage that I use for production.
Run npx sst dev
to bring up the local stack and use the local config.
Start the Remix app
Run npm run dev
to start the Remix app. The app should start up and connect to the local MySQL instance. The data migrations will be run automatically and the Shopify should be able to connect to the app.