Skip to main content

RDS

The RDS construct is a higher level CDK construct that makes it easy to create an RDS Serverless Cluster. It uses the following defaults:

Migrations

The RDS construct uses Kysely to run and manage schema migrations. The migrations prop should point to the folder where your migration files are.

new RDS(stack, "Database", {
engine: "postgresql10.14",
defaultDatabaseName: "acme",
migrations: "path/to/migration/scripts",
});

On sst deploy, all migrations that have not yet been run will be run as a part of the deploy process. The migrations are executed in alphabetical order by their name.

On sst start, migrations are not automatically run. You can manually run them via the SST Console.

note

New migrations must always have a name that comes alphabetically after the last executed migration.

Migration files should have the following format.

async function up(db) {
// Migration code
}

async function down(db) {
// Migration code
}

module.exports = { up, down };

Read more about writing migrations over on the Kysely docs.

Migrations with PostgreSQL

async function up(db) {
await db.schema
.createTable("person")
.addColumn("id", "serial", (col) => col.primaryKey())
.addColumn("first_name", "varchar", (col) => col.notNull())
.addColumn("last_name", "varchar")
.addColumn("gender", "varchar(50)", (col) => col.notNull())
.execute()
}

async function down(db) {
await db.schema.dropTable("person").execute()
}

module.exports = { up, down };

Migrations with MySQL

async function up(db) {
await db.schema
.createTable("person")
.addColumn("id", "integer", (col) => col.autoIncrement().primaryKey())
.addColumn("first_name", "varchar(255)", (col) => col.notNull())
.addColumn("last_name", "varchar(255)")
.addColumn("gender", "varchar(50)", (col) => col.notNull())
.execute()
}

async function down(db) {
await db.schema.dropTable("person").execute()
}

module.exports = { up, down };

Auto-scaling

RDS automatically scales the cluster size based on CPU utilization, connections, and available memory. An RDS with the MySQL engine can scale from 1 to 256 ACU (Aurora capacity unit). And an RDS with the PostgreSQL engine can scale from 2 to 384 ACU. You can specify the minimum and maximum range for the cluster. The default minimum and maximum capacity are 2 and 16 ACU.

You can also choose to pause your RDS cluster after a given amount of time with no activity. When the cluster is paused, you are charged only for the storage. If database connections are requested when a cluster is paused, the cluster automatically resumes. By default, the cluster auto-pauses after 5 minutes of inactivity.

For dev stages, it makes sense to pick a low capacity and auto-pause time. And disable it for production stages.

const prodConfig = {
autoPause: false,
minCapacity: "ACU_8",
maxCapacity: "ACU_64",
};
const devConfig = {
autoPause: true,
minCapacity: "ACU_2",
maxCapacity: "ACU_2",
};

new RDS(stack, "Database", {
engine: "postgresql10.14",
defaultDatabaseName: "acme",
scaling: app.stage === "prod" ? prodConfig : devConfig,
});

Read more over on the RDS docs.

Examples

Using the minimal config

import { RDS } from "@serverless-stack/resources";

new RDS(stack, "Database", {
engine: "postgresql10.14",
defaultDatabaseName: "my_database",
});

Configuring the RDS cluster

You can configure the internally created CDK ServerlessCluster instance.

import * as cdk from "aws-cdk-lib";

new RDS(stack, "Database", {
engine: "postgresql10.14",
defaultDatabaseName: "acme",
cdk: {
cluster: {
backupRetention: cdk.Duration.days(7),
},
},
});

Advanced examples

Import existing RDS Serverless v1 cluster

import * as rds from "aws-cdk-lib/aws-rds";
import * as secretsManager from "aws-cdk-lib/aws-secretsmanager";

new RDS(stack, "Database", {
engine: "postgresql10.14",
defaultDatabaseName: "acme",
cdk: {
cluster: rds.ServerlessCluster.fromServerlessClusterAttributes(stack, "ICluster", {
clusterIdentifier: "my-existing-cluster",
}),
secret: secretsManager.Secret.fromSecretAttributes(stack, "ISecret", {
secretPartialArn: "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret",
}),
},
});

Note that migrations are support for imported cluster. In order for migrations to work, make sure engine and defaultDatabaseName match the configuration of the imported cluster. You also need to import the secret credentials used by the cluster from the Secrets Manager.

Using existing VPC

The RDS construct automatically creates a VPC to deploy the cluster. This VPC contains only PRIVATE and ISOLATED subnets, without NAT Gateways.

note

Since we are using the Data API, you don't need to deploy your Lambda functions into the RDS's VPC.

Yo can override the internally created VPC instance.

import * as ec2 from "aws-cdk-lib/aws-ec2";

new RDS(stack, "Database", {
engine: "postgresql10.14",
defaultDatabaseName: "acme",
cdk: {
cluster: {
vpc: ec2.Vpc.fromLookup(this, "VPC", {
vpcId: "vpc-xxxxxxxxxx",
}),
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE,
},
},
},
});

Constructor

new RDS(scope, id, props)

Parameters

RDSProps

defaultDatabaseName

Type : string

Name of a database which is automatically created inside the cluster.

engine

Type : "mysql5.6" | "mysql5.7" | "postgresql10.14"

Database engine of the cluster. Cannot be changed once set.

migrations?

Type : string

Path to the directory that contains the migration scripts. The RDS construct uses Kysely to run and manage schema migrations. The migrations prop should point to the folder where your migration files are.

new RDS(stack, "Database", {
engine: "postgresql10.14",
defaultDatabaseName: "acme",
migrations: "path/to/migration/scripts",
});

scaling.autoPause?

Type : number | boolean

Default : true

The time before the cluster is paused. Pass in true to pause after 5 minutes of inactive. And pass in false to disable pausing.

Or pass in the number of minutes to wait before the cluster is paused.

new RDS(stack, "Database", {
scaling: {
autoPause: props.app.stage !== "prod"
}
})

scaling.maxCapacity?

Type : "ACU_1" | "ACU_2" | "ACU_4" | "ACU_8" | "ACU_16" | "ACU_32" | "ACU_64" | "ACU_128" | "ACU_192" | "ACU_256" | "ACU_384"

Default : "ACU_16"

The maximum capacity for the cluster.

scaling.minCapacity?

Type : "ACU_1" | "ACU_2" | "ACU_4" | "ACU_8" | "ACU_16" | "ACU_32" | "ACU_64" | "ACU_128" | "ACU_192" | "ACU_256" | "ACU_384"

Default : "ACU_2"

The minimum capacity for the cluster.

types?

Type : string

Path to place generated typescript types after running migrations

new RDS(stack, "Database", {
engine: "postgresql10.14",
defaultDatabaseName: "acme",
migrations: "path/to/migration/scripts",
types: "backend/core/sql/types.ts",
});

cdk.cluster?

Type : IServerlessCluster | RDSCdkServerlessClusterProps

Configure the internallly created RDS cluster.

new RDS(stack, "Database", {
cdk: {
cluster: {
clusterIdentifier: "my-cluster",
}
},
});

Alternatively, you can import an existing RDS Serverless v1 Cluster in your AWS account.

new RDS(stack, "Database", {
cdk: {
cluster: rds.ServerlessCluster.fromServerlessClusterAttributes(stack, "ICluster", {
clusterIdentifier: "my-cluster",
}),
secret: secretsManager.Secret.fromSecretAttributes(stack, "ISecret", {
secretPartialArn: "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret",
}),
},
});

cdk.secret?

Type : ISecret

Required when importing existing RDS Serverless v1 Cluster.

Properties

An instance of RDS has the following properties.

clusterArn

Type : string

The ARN of the internally created RDS Serverless Cluster.

clusterEndpoint

Type : Endpoint

The ARN of the internally created RDS Serverless Cluster.

clusterIdentifier

Type : string

The ARN of the internally created RDS Serverless Cluster.

defaultDatabaseName

Type : string

The default database name of the RDS Serverless Cluster.

migratorFunction?

Type : Function

The ARN of the internally created CDK ServerlessCluster instance.

secretArn

Type : string

The ARN of the internally created Secrets Manager Secret.

cdk.cluster

Type : ServerlessCluster

The ARN of the internally created CDK ServerlessCluster instance.

RDSCdkServerlessClusterProps

vpc?

Type : IVpc