Using SST Config Variables with Shopify Remix deployed to AWS Lambda
Introduction
SST provides for two ways to manage environment variables. The first is through .env
files and the second is through the Config
class. SST recommends using the Config
class, you can read about the reasoning for that in the SST documentation.
Under the hood, SST uses AWS Systems Manager Parameter Store to store the values. That means that in order to use Config
through your entire workflow you will need to use SST's local development process "Live Lambda". I wrote an article about developing locally here: Developing Shopify Remix with AWS Lambda.
.env method
Let's look at the using .env
files first since it's the more common way of managing environment variables. SST comes out of the box with dotenv
support so there is a naming convention setup for managing values in different stages. Read more about that in the Config dotenv.
My local stage is named wwright
so my .env
file is named .env.wwright
. I have a variable DB_HOST
that I want to use in my Lambda function. I set the variable in the .env.wwright
file like this:
DB_NAME=shopify_app
DB_HOST=notcorrect
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=password123
In order to use this value in the app I need to pass it into the stack definition.
shopify.local.app.ts
const site = new RemixSite(stack, "site", {
runtime: "nodejs20.x",
cdk: {
server: {
vpc: vpc,
securityGroups: [defaultLambdaSecurityGroup]
}
},
bind: [rds],
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 || "",
}
});
Now using it in the shopify server function.
shopify.server.ts
console.log("DB_HOST", process.env.DB_HOST);
I can verify that the Lambda function has the .env
value by inspect the function in the AWS Lambda UI. Goto the "Template" tab and the values should all be shown.
Config method
The Config
method is a bit more involved but it's the recommended way to manage environment variables.
First, you need to define the variable in the app.stack
file.
...
const DB_HOST = new Config.Secret(stack, "DB_HOST");
const site = new RemixSite(stack, "site", {
runtime: "nodejs20.x",
cdk: {
...
Then you need to bind the variable to the Lambda function. Use bind
to make the object available to the Lambda function instead of passing the value in the environment
object.
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_NAME: process.env.DB_NAME || "",
DB_USERNAME: process.env.DB_USERNAME || "",
DB_PASSWORD: process.env.DB_PASSWORD || "",
DB_PORT: process.env.DB_PORT || "",
},
bind: [DB_HOST],
});
To read the value in the Lambda function you need to import the Config
class. Note that the object comes from sst/node/config
and NOT sst/constructs
. sst/constructs
is for the Config
class that is used to define the variable in the app.stack
NOT to use it in the Lambda function.
shopify.server.ts
import { Config } from "sst/node/config";
console.log("DB_HOST from config", Config.DB_HOST);
I will change the MySQLSessionStorage constuctor while I'm in here so that the shopify.server function will work with the new DB_HOST variable.
shopify.server.ts
...
const shopify = shopifyApp({
apiKey: process.env.SHOPIFY_API_KEY,
apiSecretKey: process.env.SHOPIFY_API_SECRET || "",
apiVersion: LATEST_API_VERSION,
scopes: process.env.SCOPES?.split(","),
appUrl: process.env.SHOPIFY_APP_URL || "",
authPathPrefix: "/auth",
sessionStorage: MySQLSessionStorage.withCredentials(
Config.DB_HOST || "",
process.env.DB_NAME || "",
process.env.DB_USERNAME || "",
process.env.DB_PASSWORD || "",
{
sessionTableName: 'session',
connectionPoolLimit: 10
}
),
...
Lastly the value needs to be set using the CLI.
npx sst secrets set DB_HOST 127.0.0.1
To keep the local environment working we need to make a small update to the command for starting Remix.
Open package.json
and update the dev
command.
package.json
"scripts": {
...
"dev": "npx sst bind --script shopify app dev",
...
}
Wrapping the shopify CLI command with bind
will ensure that the values are read from AWS and made available to the locally running app.
Run the app locally and you should see the value in the console.
npm run dev
Verify that the variable is coming from Config
and not .env
.
Top level awaits
The first time that I tried using the Config
method I got an error from Remix about top level awaits. The full error is:
[ERROR] Top-level await is not available in the configured target environment ("chrome87", "edge88", "es2020", "firefox78", "safari14" + 2 overrides)
In order to fix the solution for me was modifying the vite.config
with the following.
vite.config.ts
Add to defineConfig
export default defineConfig({
...
optimizeDeps: {
esbuildOptions: {
target: "esnext"
}
}
...