DEV Community

Cover image for Aurora Serverless V2 with AWS CDK
Binh Bui for AWS Community Builders

Posted on • Originally published at codewithyou.com

Aurora Serverless V2 with AWS CDK

Introduction

Aurora Serverless is a fully managed, auto-scaling configuration for Amazon Aurora. It automatically starts up, shuts down, and scales capacity up or down based on your application’s needs. You can use Aurora Serverless to run your most demanding production workloads with less administrative effort than with other database options. Aurora Serverless is available in two versions: Aurora Serverless v1 and Aurora Serverless v2.

Aurora Serverless v2 is a new version of Aurora Serverless that provides a new storage engine, Aurora Storage Engine (Aurora SE), and a new query engine, Aurora Query Engine (Aurora QE). Aurora Serverless v2 is a drop-in replacement for Aurora Serverless v1. You can use the same API calls and tools to manage your Aurora Serverless v2 clusters as you use for Aurora Serverless v1.

In this article, we will use the AWS CDK to create an Aurora Serverless v2 cluster with a database and a database user. We will also create a Lambda function that will connect to the database and query the data.

Now AWS CDK has support for Aurora Serverless v2. You can use the AWS CDK to create an Aurora Serverless v2 cluster with a database and a database user.

Let’s get started.

// create a vpc
const vpc = new Vpc(this, 'VPC', {
  cidr: '10.0.0.0/16',
  subnetConfiguration: [{ name: 'egress', subnetType: SubnetType.PUBLIC }], // only one subnet is needed
  natGateways: 0, // disable NAT gateways
})

// create a security group for aurora db
const dbSecurityGroup = new SecurityGroup(this, 'DbSecurityGroup', {
  vpc: vpc, // use the vpc created above
  allowAllOutbound: true, // allow outbound traffic to anywhere
})

// allow inbound traffic from anywhere to the db
dbSecurityGroup.addIngressRule(
  Peer.anyIpv4(),
  Port.tcp(5432), // allow inbound traffic on port 5432 (postgres)
  'allow inbound traffic from anywhere to the db on port 5432'
)

// create a db cluster
// https://github.com/aws/aws-cdk/issues/20197#issuecomment-1117555047
const dbCluster = new rds.DatabaseCluster(this, 'DbCluster', {
  engine: rds.DatabaseClusterEngine.auroraPostgres({
    version: rds.AuroraPostgresEngineVersion.VER_13_6,
  }),
  instances: 1,
  instanceProps: {
    vpc: vpc,
    instanceType: new InstanceType('serverless'),
    autoMinorVersionUpgrade: true,
    publiclyAccessible: true,
    securityGroups: [dbSecurityGroup],
    vpcSubnets: vpc.selectSubnets({
      subnetType: SubnetType.PUBLIC, // use the public subnet created above for the db
    }),
  },
  port: 5432, // use port 5432 instead of 3306
})

// add capacity to the db cluster to enable scaling
Aspects.of(dbCluster).add({
  visit(node) {
    if (node instanceof CfnDBCluster) {
      node.serverlessV2ScalingConfiguration = {
        minCapacity: 0.5, // min capacity is 0.5 vCPU
        maxCapacity: 1, // max capacity is 1 vCPU (default)
      }
    }
  },
})
Enter fullscreen mode Exit fullscreen mode

Let see what we have done here.

  • We created a VPC with a public subnet. We will use this VPC to create the Aurora Serverless v2 cluster.
  • We created a security group for the Aurora Serverless v2 cluster. We will use this security group to allow inbound traffic from anywhere to the Aurora Serverless v2 cluster.
  • We created an Aurora Serverless v2 cluster with a single instance. We used the serverless instance type to create an Aurora Serverless v2 cluster. We also set public accessibility to true to allow the Lambda function to connect to the Aurora Serverless v2 cluster. We also set the port to 5432 to use the Postgres port.
  • We added capacity to the Aurora Serverless v2 cluster to enable scaling. We set the minCapacity to 0.5 and maxCapacity to 1. This will allow the Aurora Serverless v2 cluster to scale up to 1 vCPU and scale down to 0.5 vCPU.

Create a lambda function to connect to the database and query the data

In this section, we will create a lambda function that will connect to the database and query the data. We will use the AWS CDK to create the lambda function.

const fn = new NodejsFunction(this, 'Lambda', {
  entry: './lambda/index.ts',
  runtime: Runtime.NODEJS_16_X,
  handler: 'main',
  bundling: {
    externalModules: ['aws-sdk', 'pg-native'],
    minify: false,
  },
  environment: {
    databaseSecretArn: dbCluster.secret?.secretArn ?? '', // pass the secret arn to the lambda function
  },
})

// allow the lambda function to access credentials stored in AWS Secrets Manager
// the lambda function will be able to access the credentials for the default database in the db cluster
dbCluster.secret?.grantRead(fn)

const api = new LambdaRestApi(this, 'Api', {
  handler: fn,
})
Enter fullscreen mode Exit fullscreen mode

Let see what we have done here.

  • We created a lambda function with the AWS CDK. We used the NodejsFunction construct to create the lambda function. We used the externalModules option to exclude the aws-sdk and pg-native modules from the lambda function. We also used the minify option to disable minification.
  • We passed the secret arn to the lambda function using the environment option. The lambda function will use the secret arn to access the credentials for the default database in the Aurora Serverless v2 cluster.
  • We allowed the lambda function to access credentials stored in AWS Secrets Manager. The lambda function will be able to access the credentials for the default database in the Aurora Serverless v2 cluster.
  • And finally, we created a REST API using the lambda function. We will use this REST API to test the lambda function.

You can read more about how to Building Lambda functions with TypeScript in AWS CDK here

Below is the code for the lambda function.

import { GetSecretValueCommand, SecretsManagerClient } from '@aws-sdk/client-secrets-manager'
import { APIGatewayEvent, APIGatewayProxyResult } from 'aws-lambda'
import { Client } from 'pg'

export async function main(event: APIGatewayEvent): Promise<APIGatewayProxyResult> {
  // get the secret from secrets manager.
  const client = new SecretsManagerClient({})
  const secret = await client.send(
    new GetSecretValueCommand({
      SecretId: process.env.databaseSecretArn,
    })
  )
  const secretValues = JSON.parse(secret.SecretString ?? '{}')

  console.log('secretValues', secretValues)

  // connect to the database
  const db = new Client({
    host: secretValues.host, // host is the endpoint of the db cluster
    port: secretValues.port, // port is 5432
    user: secretValues.username, // username is the same as the secret name
    password: secretValues.password, // this is the password for the default database in the db cluster
    database: secretValues.dbname ?? 'postgres', // use the default database if no database is specified
  })

  await db.connect()

  // execute a query
  const res = await db.query('SELECT NOW()')

  // disconnect from the database
  await db.end()

  return {
    body: JSON.stringify({
      message: `DB Response: ${res.rows[0].now}`,
    }),
    statusCode: 200,
    isBase64Encoded: false,
    headers: {
      'Content-Type': 'application/json',
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

Let see what we have done here.

  • We created a SecretsManagerClient to get the secret from AWS Secrets Manager.
  • We used the SecretsManagerClient to get the secret from AWS Secrets Manager. We used the SecretId option to specify the secret arn. We used the GetSecretValueCommand to get the secret from AWS Secrets Manager.
  • We parsed the secret string to get the secret values. The secret string contains the credentials for the default database in the Aurora Serverless v2 cluster.
  • We created a Client to connect to the database. We used the host, port, user, password, and database options to connect to the database.
  • We executed a query to get the current time from the database.

That's it. We have created an Aurora Serverless v2 cluster with a single instance. We have also created a lambda function that will connect to the database and query the data. Now, we can deploy the stack to AWS.

Deploy the stack to AWS

To deploy the stack to AWS, run the following command.

cdk deploy
Enter fullscreen mode Exit fullscreen mode

After the stack is deployed, you can verify that the Aurora Serverless v2 cluster is created in the AWS console.

Aurora Serverless v2 cluster

Besides, you can test the lambda function by making a request to the REST API.

curl -X GET https://<api-id>.execute-api.<region>.amazonaws.com/prod
Enter fullscreen mode Exit fullscreen mode

You should get a response similar to the one below.

{
  "message": "DB Response: 2022-11-08T15:52:00.000Z"
}
Enter fullscreen mode Exit fullscreen mode

Clean up

Don't forget to delete the stack after you are done. This will prevent you from incurring any charges.

cdk destroy
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this article, we created an Aurora Serverless v2 cluster with a single instance. We also created a lambda function that will connect to the database and query the data. We used the AWS CDK to create the Aurora Serverless v2 cluster and the lambda function. You can find the code for this article here

That's it for this article. I hope you found it useful. If you have any questions or suggestions, please leave a comment below. You can also reach out to me on Twitter or LinkedIn.

Top comments (0)