Skip to content

Commit abedd83

Browse files
author
Mitchell Elholm
committed
Add PostgresJS sample with token refresh and non-admin
1 parent aa08d7c commit abedd83

5 files changed

Lines changed: 226 additions & 0 deletions

File tree

javascript/postgres-js/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/node_modules
2+
/build
3+
/dist
4+
/coverage

javascript/postgres-js/README.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# node-postgres with Aurora DSQL
2+
3+
## Overview
4+
5+
This code example demonstrates how to use `node-postgres` with Amazon Aurora DSQL.
6+
The example shows you how to connect to an Aurora DSQL cluster and perform basic database operations.
7+
8+
Aurora DSQL is a distributed SQL database service that provides high availability and scalability for
9+
your PostgreSQL-compatible applications. `node-postgres` is a popular PostgreSQL adapter for Node.js that allows
10+
you to interact with PostgreSQL databases using JavaScript code.
11+
12+
## About the code example
13+
14+
The example demonstrates a flexible connection approach that works for both admin and non-admin users:
15+
16+
* When connecting as an **admin user**, the example uses the `public` schema and generates an admin authentication
17+
token.
18+
* When connecting as a **non-admin user**, the example uses a custom `myschema` schema and generates a standard
19+
authentication token.
20+
21+
The code automatically detects the user type and adjusts its behavior accordingly.
22+
23+
## ⚠️ Important
24+
25+
* Running this code might result in charges to your AWS account.
26+
* We recommend that you grant your code least privilege. At most, grant only the
27+
minimum permissions required to perform the task. For more information, see
28+
[Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
29+
* This code is not tested in every AWS Region. For more information, see
30+
[AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
31+
32+
## Run the example
33+
34+
### Prerequisites
35+
36+
* You must have an AWS account, and have your default credentials and AWS Region
37+
configured as described in the
38+
[Globally configuring AWS SDKs and tools](https://docs.aws.amazon.com/credref/latest/refdocs/creds-config-files.html)
39+
guide.
40+
* Node.js: Ensure you have Node.js 18+ installed.
41+
42+
```bash
43+
node --version
44+
```
45+
46+
It should output something similar to `v18.x` or higher.
47+
48+
* You must have an Aurora DSQL cluster. For information about creating an Aurora DSQL cluster, see the
49+
[Getting started with Aurora DSQL](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/getting-started.html)
50+
guide.
51+
* If connecting as a non-admin user, ensure the user is linked to an IAM role and is granted access to the `myschema`
52+
schema. See the
53+
[Using database roles with IAM roles](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/using-database-and-iam-roles.html)
54+
guide.
55+
56+
### Run the code
57+
58+
The example demonstrates the following operations:
59+
60+
- Opening a connection to an Aurora DSQL cluster
61+
- Creating a table
62+
- Inserting and querying data
63+
64+
The example is designed to work with both admin and non-admin users:
65+
66+
- When run as an admin user, it uses the `public` schema
67+
- When run as a non-admin user, it uses the `myschema` schema
68+
69+
**Note:** running the example will use actual resources in your AWS account and may incur charges.
70+
71+
Set environment variables for your cluster details:
72+
73+
```bash
74+
# e.g. "admin"
75+
export CLUSTER_USER="<your user>"
76+
77+
# e.g. "foo0bar1baz2quux3quuux4.dsql.us-east-1.on.aws"
78+
export CLUSTER_ENDPOINT="<your endpoint>"
79+
80+
# e.g. "us-east-1"
81+
export REGION="<your region>"
82+
```
83+
84+
Run the example:
85+
86+
```bash
87+
npm install
88+
npm test
89+
```
90+
91+
The example contains comments explaining the code and the operations being performed.
92+
93+
## Additional resources
94+
95+
* [Amazon Aurora DSQL Documentation](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/what-is-aurora-dsql.html)
96+
* [node-postgres Documentation](https://node-postgres.com/)
97+
* [AWS SDK for JavaScript Documentation](https://docs.aws.amazon.com/sdk-for-javascript/)
98+
99+
---
100+
101+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
102+
103+
SPDX-License-Identifier: MIT-0
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "dsql_nodejs_howto",
3+
"version": "1.0.0",
4+
"description": "How To",
5+
"main": "index.js",
6+
"type": "module",
7+
"scripts": {
8+
"test": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern='test/smoke.test.js' --runInBand --detectOpenHandles --forceExit"
9+
},
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"@aws-sdk/dsql-signer": "^3.705.0",
14+
"assert": "2.1.0",
15+
"pg": "^8.13.1",
16+
"uuid": "^11.0.2"
17+
},
18+
"devDependencies": {
19+
"jest": "^29.7.0"
20+
}
21+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { DsqlSigner } from "@aws-sdk/dsql-signer";
2+
import pg from "pg";
3+
import assert from "node:assert";
4+
const { Client } = pg;
5+
6+
const ADMIN = "admin";
7+
const NON_ADMIN_SCHEMA = "myschema";
8+
9+
async function getConnection(clusterEndpoint, user, region) {
10+
const signer = new DsqlSigner({
11+
hostname: clusterEndpoint,
12+
region,
13+
});
14+
let token;
15+
// Generate a fresh password token for each connection, to ensure the token is
16+
// not expired when the connection is established
17+
if (user === ADMIN) {
18+
token = await signer.getDbConnectAdminAuthToken();
19+
}
20+
else {
21+
signer.user = user;
22+
token = await signer.getDbConnectAuthToken()
23+
}
24+
// <https://node-postgres.com/apis/client>
25+
// By default `rejectUnauthorized` is true in TLS options
26+
// <https://nodejs.org/api/tls.html#tls_tls_connect_options_callback>
27+
// The config does not offer any specific parameter to set sslmode to verify-full
28+
// Settings are controlled either via connection string or by setting
29+
// rejectUnauthorized to false in ssl options
30+
let client = new Client({
31+
host: clusterEndpoint,
32+
user: user,
33+
password: token,
34+
database: "postgres",
35+
port: 5432,
36+
// <https://node-postgres.com/announcements> for version 8.0
37+
ssl: true
38+
});
39+
40+
// Connect
41+
await client.connect();
42+
console.log("Successfully opened connection");
43+
return client;
44+
}
45+
46+
async function example() {
47+
48+
const clusterEndpoint = process.env.CLUSTER_ENDPOINT;
49+
assert(clusterEndpoint);
50+
const user = process.env.CLUSTER_USER;
51+
assert(user);
52+
const region = process.env.REGION;
53+
assert(region);
54+
55+
let client;
56+
try {
57+
client = await getConnection(clusterEndpoint, user, region);
58+
59+
if (user !== ADMIN) {
60+
await client.query("SET search_path=" + NON_ADMIN_SCHEMA)
61+
}
62+
63+
// Create a new table
64+
await client.query(`CREATE TABLE IF NOT EXISTS owner (
65+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
66+
name VARCHAR(30) NOT NULL,
67+
city VARCHAR(80) NOT NULL,
68+
telephone VARCHAR(20)
69+
)`);
70+
71+
// Insert some data
72+
await client.query("INSERT INTO owner(name, city, telephone) VALUES($1, $2, $3)",
73+
["John Doe", "Anytown", "555-555-1900"]
74+
);
75+
76+
// Check that data is inserted by reading it back
77+
const result = await client.query("SELECT id, city FROM owner where name='John Doe'");
78+
assert.deepEqual(result.rows[0].city, "Anytown")
79+
assert.notEqual(result.rows[0].id, null)
80+
81+
await client.query("DELETE FROM owner where name='John Doe'");
82+
83+
} catch (error) {
84+
console.error(error);
85+
raise
86+
} finally {
87+
client?.end()
88+
}
89+
Promise.resolve()
90+
}
91+
92+
export { example }
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { example } from '../src/index.js';
2+
3+
test('Smoke test', async () => {
4+
await example();
5+
return Promise.resolve();
6+
});

0 commit comments

Comments
 (0)