@ttoss/lambda-postgres-query 0.3.17 → 0.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,28 +1,22 @@
1
1
  # @ttoss/lambda-postgres-query
2
2
 
3
- This package creates an AWS Lambda function that can query an RDS Postgres database inside a private subnet of a VPC.
3
+ Create an AWS Lambda function to securely query a PostgreSQL database in a private VPC subnet without exposing the database to the internet.
4
4
 
5
- The goal of this package is to provide a way to query a Postgres database from a Lambda function without exposing the database to the internet. If your project needs to query a Postgres database and access to the internet, you can follow two approaches:
5
+ ## When to Use
6
6
 
7
- 1. Create a Lambda function inside a VPC and use a NAT Gateway to access the internet. This approach is expensive because you need to pay for the NAT Gateway.
8
-
9
- 2. Decompose your architecture into multiple Lambdas—some inside the VPC and some outside the VPC. The Lambda inside the VPC can query the database, and the Lambda outside the VPC can query the Lambda inside the VPC. On this approach, Lambdas outside the VPC invoke Lambdas inside the VPC using the AWS SDK to query the database. This approach is complex and requires more effort to maintain.
10
-
11
- _Check this StackOverflow question for more information: [Why can't an AWS lambda function inside a public subnet in a VPC connect to the internet?](https://stackoverflow.com/questions/52992085/why-cant-an-aws-lambda-function-inside-a-public-subnet-in-a-vpc-connect-to-the)_
7
+ This package solves the challenge of querying a PostgreSQL database from AWS Lambda functions without internet access. Traditional approaches require expensive NAT Gateways or complex multi-Lambda architectures. This package provides a simpler solution by deploying a dedicated Lambda function within your VPC.
12
8
 
13
9
  ## Installation
14
10
 
15
- To install this package, you need to run the following command:
16
-
17
11
  ```bash
18
12
  pnpm install @ttoss/lambda-postgres-query
19
13
  ```
20
14
 
21
- ## Usage
15
+ ## Setup
22
16
 
23
- ### CloudFormation
17
+ ### CloudFormation Template
24
18
 
25
- Create a `src/cloudformation.ts` file with the following content:
19
+ Create a CloudFormation template to deploy the Lambda function:
26
20
 
27
21
  ```typescript
28
22
  import { createLambdaPostgresQueryTemplate } from '@ttoss/lambda-postgres-query/cloudformation';
@@ -32,26 +26,32 @@ const template = createLambdaPostgresQueryTemplate();
32
26
  export default template;
33
27
  ```
34
28
 
35
- Create a `src/handler.ts` file with the following content:
29
+ ### Lambda Handler
30
+
31
+ Create a handler file that exports the Lambda function:
36
32
 
37
33
  ```typescript
38
34
  export { handler } from '@ttoss/lambda-postgres-query';
39
35
  ```
40
36
 
41
- Provide the following environment variables in the `.env` file:
37
+ ### Environment Variables
38
+
39
+ Configure the following environment variables:
42
40
 
43
41
  ```env
44
- DATABASE_NAME=
45
- DATABASE_USERNAME=
46
- DATABASE_PASSWORD=
47
- DATABASE_HOST=
48
- DATABASE_HOST_READ_ONLY=
49
- DATABASE_PORT=
50
- SECURITY_GROUP_IDS=
51
- SUBNET_IDS=
42
+ DATABASE_NAME=your_database_name
43
+ DATABASE_USERNAME=your_username
44
+ DATABASE_PASSWORD=your_password
45
+ DATABASE_HOST=your_database_host
46
+ DATABASE_HOST_READ_ONLY=your_read_only_host # Optional
47
+ DATABASE_PORT=5432
48
+ SECURITY_GROUP_IDS=sg-xxxxx,sg-yyyyy
49
+ SUBNET_IDS=subnet-xxxxx,subnet-yyyyy
52
50
  ```
53
51
 
54
- Add the `deploy` script to the `package.json` file:
52
+ ### Deployment
53
+
54
+ Add a deploy script to your `package.json`:
55
55
 
56
56
  ```json
57
57
  {
@@ -61,44 +61,94 @@ Add the `deploy` script to the `package.json` file:
61
61
  }
62
62
  ```
63
63
 
64
- [Deploy](https://ttoss.dev/docs/carlin/commands/deploy) them using the following command:
64
+ Deploy using Carlin:
65
65
 
66
66
  ```bash
67
67
  pnpm deploy
68
68
  ```
69
69
 
70
- _**Note:** When deploying using carlin, you need to set `lambdaFormat: 'cjs'` because `pg` package doesn't support ESM format._
70
+ **Note:** Set `lambdaFormat: 'cjs'` in your Carlin configuration, as the `pg` package requires CommonJS.
71
71
 
72
- It'll create the necessary resources to query the Postgres database and display the name of the Lambda function created.
72
+ ## Usage
73
73
 
74
- ### Querying the database
74
+ ### Querying from External Lambdas
75
75
 
76
- To query the database from Lambdas outside the VPC, do the following:
76
+ Query the database from Lambda functions outside the VPC:
77
77
 
78
78
  ```typescript
79
79
  import { query } from '@ttoss/lambda-postgres-query';
80
80
  import type { Handler } from 'aws-lambda';
81
81
 
82
82
  export const handler: Handler = async (event) => {
83
- const text = 'SELECT * FROM table_name';
84
- const result = await query({ text });
83
+ const result = await query('SELECT * FROM users');
85
84
  return result.rows;
86
85
  };
87
86
  ```
88
87
 
89
- ## API
88
+ ### Advanced Query Options
89
+
90
+ ```typescript
91
+ import { query } from '@ttoss/lambda-postgres-query';
90
92
 
91
- ### `createLambdaQueryTemplate`
93
+ // Query with parameters
94
+ const result = await query({
95
+ text: 'SELECT * FROM users WHERE id = $1',
96
+ values: [userId],
97
+ });
98
+
99
+ // Use read-only connection
100
+ const result = await query({
101
+ text: 'SELECT * FROM users',
102
+ readOnly: true, // Defaults to true
103
+ });
104
+
105
+ // Disable automatic camelCase conversion
106
+ const result = await query({
107
+ text: 'SELECT * FROM users',
108
+ camelCaseKeys: false, // Defaults to true
109
+ });
110
+
111
+ // Specify custom Lambda function name
112
+ const result = await query({
113
+ text: 'SELECT * FROM users',
114
+ lambdaPostgresQueryFunction: 'custom-function-name',
115
+ });
116
+ ```
92
117
 
93
- This function creates a CloudFormation template to deploy the Lambda function that queries the Postgres database.
118
+ ## API Reference
94
119
 
95
- ### `query`
120
+ ### `createLambdaPostgresQueryTemplate(options?)`
96
121
 
97
- This function queries the Postgres database using the environment variables provided. It uses the [`pg`](https://node-postgres.com/) package to connect to the database.
122
+ Creates a CloudFormation template for the PostgreSQL query Lambda function.
98
123
 
99
124
  #### Parameters
100
125
 
101
- It accepts all the [`QueryConfig` object from the `pg` package](https://node-postgres.com/apis/client#queryconfig) with the following additional properties:
126
+ - `handler` (string, optional): Lambda handler function name. Default: `'handler.handler'`
127
+ - `memorySize` (number, optional): Lambda memory size in MB. Default: `128`
128
+ - `timeout` (number, optional): Lambda timeout in seconds. Default: `30`
129
+
130
+ #### Returns
131
+
132
+ A CloudFormation template object.
133
+
134
+ ### `query(params)`
135
+
136
+ Queries the PostgreSQL database by invoking the VPC Lambda function.
137
+
138
+ #### Parameters
139
+
140
+ Accepts either a SQL string or an options object extending [`QueryConfig`](https://node-postgres.com/apis/client#queryconfig) with additional properties:
141
+
142
+ - `text` (string): SQL query text
143
+ - `values` (array, optional): Query parameter values
144
+ - `readOnly` (boolean, optional): Use read-only database host if available. Default: `true`
145
+ - `lambdaPostgresQueryFunction` (string, optional): Name of the query Lambda function. Default: `LAMBDA_POSTGRES_QUERY_FUNCTION` environment variable
146
+ - `camelCaseKeys` (boolean, optional): Convert snake_case column names to camelCase. Default: `true`
147
+
148
+ #### Returns
149
+
150
+ A [`QueryResult`](https://node-postgres.com/apis/result) object with transformed rows.
151
+
152
+ ### `handler`
102
153
 
103
- - `readOnly`: A boolean that indicates if the query should be executed on the read-only database, in case you provided the `DATABASE_HOST_READ_ONLY` value. Default is `true`.
104
- - `lambdaPostgresQueryFunction`: The name of the Lambda function that queries the database. Default is the value of the `LAMBDA_POSTGRES_QUERY_FUNCTION` environment variable.
154
+ AWS Lambda handler function for processing database queries within the VPC.
@@ -0,0 +1,18 @@
1
+ import { CloudFormationTemplate } from '@ttoss/cloudformation';
2
+ export { CloudFormationTemplate } from '@ttoss/cloudformation';
3
+ import { Handler } from 'aws-lambda';
4
+ import { QueryParams } from '../index.cjs';
5
+ import 'pg';
6
+
7
+ declare const HANDLER_DEFAULT = "handler.handler";
8
+ declare const MEMORY_SIZE_DEFAULT = 128;
9
+ declare const TIMEOUT_DEFAULT = 30;
10
+ declare const createLambdaPostgresQueryTemplate: ({ handler, memorySize, timeout, }?: {
11
+ handler?: string;
12
+ memorySize?: number;
13
+ timeout?: number;
14
+ }) => CloudFormationTemplate;
15
+
16
+ declare const handler: Handler<QueryParams>;
17
+
18
+ export { HANDLER_DEFAULT, MEMORY_SIZE_DEFAULT, TIMEOUT_DEFAULT, createLambdaPostgresQueryTemplate, handler };
@@ -0,0 +1,18 @@
1
+ import { CloudFormationTemplate } from '@ttoss/cloudformation';
2
+ export { CloudFormationTemplate } from '@ttoss/cloudformation';
3
+ import { Handler } from 'aws-lambda';
4
+ import { QueryParams } from '../index.js';
5
+ import 'pg';
6
+
7
+ declare const HANDLER_DEFAULT = "handler.handler";
8
+ declare const MEMORY_SIZE_DEFAULT = 128;
9
+ declare const TIMEOUT_DEFAULT = 30;
10
+ declare const createLambdaPostgresQueryTemplate: ({ handler, memorySize, timeout, }?: {
11
+ handler?: string;
12
+ memorySize?: number;
13
+ timeout?: number;
14
+ }) => CloudFormationTemplate;
15
+
16
+ declare const handler: Handler<QueryParams>;
17
+
18
+ export { HANDLER_DEFAULT, MEMORY_SIZE_DEFAULT, TIMEOUT_DEFAULT, createLambdaPostgresQueryTemplate, handler };
@@ -0,0 +1,240 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ "use strict";
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __name = (target, value) => __defProp(target, "name", {
9
+ value,
10
+ configurable: true
11
+ });
12
+ var __export = (target, all) => {
13
+ for (var name in all) __defProp(target, name, {
14
+ get: all[name],
15
+ enumerable: true
16
+ });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
21
+ get: () => from[key],
22
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
23
+ });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
28
+ value: true
29
+ }), mod);
30
+
31
+ // src/cloudformation/index.ts
32
+ var cloudformation_exports = {};
33
+ __export(cloudformation_exports, {
34
+ HANDLER_DEFAULT: () => HANDLER_DEFAULT,
35
+ MEMORY_SIZE_DEFAULT: () => MEMORY_SIZE_DEFAULT,
36
+ TIMEOUT_DEFAULT: () => TIMEOUT_DEFAULT,
37
+ createLambdaPostgresQueryTemplate: () => createLambdaPostgresQueryTemplate,
38
+ handler: () => handler
39
+ });
40
+ module.exports = __toCommonJS(cloudformation_exports);
41
+
42
+ // src/cloudformation/createLambdaPostgresQueryTemplate.ts
43
+ var HANDLER_DEFAULT = "handler.handler";
44
+ var MEMORY_SIZE_DEFAULT = 128;
45
+ var TIMEOUT_DEFAULT = 30;
46
+ var createLambdaPostgresQueryTemplate = /* @__PURE__ */__name(({
47
+ handler: handler2 = HANDLER_DEFAULT,
48
+ memorySize = 128,
49
+ timeout = 30
50
+ } = {}) => {
51
+ return {
52
+ AWSTemplateFormatVersion: "2010-09-09",
53
+ Description: "A Lambda function to query PostgreSQL.",
54
+ Parameters: {
55
+ DatabaseHost: {
56
+ Type: "String",
57
+ Description: "Database host."
58
+ },
59
+ DatabaseHostReadOnly: {
60
+ Type: "String",
61
+ Description: "Database host read only."
62
+ },
63
+ DatabaseName: {
64
+ Type: "String",
65
+ Description: "Database name."
66
+ },
67
+ DatabaseUsername: {
68
+ Type: "String",
69
+ Description: "Database username."
70
+ },
71
+ DatabasePassword: {
72
+ Type: "String",
73
+ Description: "Database password."
74
+ },
75
+ DatabasePort: {
76
+ Type: "String",
77
+ Default: "5432",
78
+ Description: "Database port."
79
+ },
80
+ LambdaS3Bucket: {
81
+ Type: "String",
82
+ Description: "The S3 bucket where the Lambda code is stored."
83
+ },
84
+ LambdaS3Key: {
85
+ Type: "String",
86
+ Description: "The S3 key where the Lambda code is stored."
87
+ },
88
+ LambdaS3ObjectVersion: {
89
+ Type: "String",
90
+ Description: "The S3 object version of the Lambda code."
91
+ },
92
+ SecurityGroupIds: {
93
+ Description: "Security Group IDs",
94
+ Type: "List<AWS::EC2::SecurityGroup::Id>"
95
+ },
96
+ SubnetIds: {
97
+ Description: "Subnet IDs",
98
+ Type: "List<AWS::EC2::Subnet::Id>"
99
+ }
100
+ },
101
+ Resources: {
102
+ LambdaQueryExecutionRole: {
103
+ Type: "AWS::IAM::Role",
104
+ Properties: {
105
+ AssumeRolePolicyDocument: {
106
+ Version: "2012-10-17",
107
+ Statement: [{
108
+ Effect: "Allow",
109
+ Principal: {
110
+ Service: "lambda.amazonaws.com"
111
+ },
112
+ Action: "sts:AssumeRole"
113
+ }]
114
+ },
115
+ ManagedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"]
116
+ }
117
+ },
118
+ LambdaQueryFunction: {
119
+ Type: "AWS::Lambda::Function",
120
+ Properties: {
121
+ Code: {
122
+ S3Bucket: {
123
+ Ref: "LambdaS3Bucket"
124
+ },
125
+ S3Key: {
126
+ Ref: "LambdaS3Key"
127
+ },
128
+ S3ObjectVersion: {
129
+ Ref: "LambdaS3ObjectVersion"
130
+ }
131
+ },
132
+ MemorySize: memorySize,
133
+ Timeout: timeout,
134
+ Handler: handler2,
135
+ Role: {
136
+ "Fn::GetAtt": ["LambdaQueryExecutionRole", "Arn"]
137
+ },
138
+ Runtime: "nodejs22.x",
139
+ Environment: {
140
+ Variables: {
141
+ DATABASE_HOST: {
142
+ Ref: "DatabaseHost"
143
+ },
144
+ DATABASE_HOST_READ_ONLY: {
145
+ Ref: "DatabaseHostReadOnly"
146
+ },
147
+ DATABASE_NAME: {
148
+ Ref: "DatabaseName"
149
+ },
150
+ DATABASE_USERNAME: {
151
+ Ref: "DatabaseUsername"
152
+ },
153
+ DATABASE_PASSWORD: {
154
+ Ref: "DatabasePassword"
155
+ },
156
+ DATABASE_PORT: {
157
+ Ref: "DatabasePort"
158
+ }
159
+ }
160
+ },
161
+ VpcConfig: {
162
+ SecurityGroupIds: {
163
+ Ref: "SecurityGroupIds"
164
+ },
165
+ SubnetIds: {
166
+ Ref: "SubnetIds"
167
+ }
168
+ }
169
+ }
170
+ },
171
+ LambdaQueryFunctionLogs: {
172
+ Type: "AWS::Logs::LogGroup",
173
+ DependsOn: "LambdaQueryFunction",
174
+ Properties: {
175
+ LogGroupName: {
176
+ "Fn::Join": ["", ["/aws/lambda/", {
177
+ Ref: "LambdaQueryFunction"
178
+ }]]
179
+ },
180
+ RetentionInDays: 7
181
+ }
182
+ }
183
+ },
184
+ Outputs: {
185
+ LambdaPostgresQueryFunction: {
186
+ Description: "Lambda function to query PostgreSQL.",
187
+ Value: {
188
+ Ref: "LambdaQueryFunction"
189
+ }
190
+ },
191
+ LambdaPostgresQueryFunctionArn: {
192
+ Description: "Lambda function to query PostgreSQL ARN.",
193
+ Value: {
194
+ "Fn::GetAtt": ["LambdaQueryFunction", "Arn"]
195
+ }
196
+ }
197
+ }
198
+ };
199
+ }, "createLambdaPostgresQueryTemplate");
200
+
201
+ // src/cloudformation/lambdaQueryHandler.ts
202
+ var import_pg = require("pg");
203
+ var database = process.env.DATABASE_NAME;
204
+ var username = process.env.DATABASE_USERNAME;
205
+ var password = process.env.DATABASE_PASSWORD;
206
+ var host = process.env.DATABASE_HOST;
207
+ var hostReadOnly = process.env.DATABASE_HOST_READ_ONLY;
208
+ var port = process.env.DATABASE_PORT;
209
+ var handler = /* @__PURE__ */__name(async event => {
210
+ try {
211
+ const client = new import_pg.Client({
212
+ database,
213
+ user: username,
214
+ password,
215
+ host: event.readOnly && hostReadOnly ? hostReadOnly : host,
216
+ port: Number(port)
217
+ });
218
+ await client.connect();
219
+ try {
220
+ const res = await client.query(event);
221
+ return res;
222
+ } finally {
223
+ await client.end();
224
+ }
225
+ } catch (error) {
226
+ console.error("Error running query", {
227
+ error,
228
+ event
229
+ });
230
+ throw error;
231
+ }
232
+ }, "handler");
233
+ // Annotate the CommonJS export names for ESM import in node:
234
+ 0 && (module.exports = {
235
+ HANDLER_DEFAULT,
236
+ MEMORY_SIZE_DEFAULT,
237
+ TIMEOUT_DEFAULT,
238
+ createLambdaPostgresQueryTemplate,
239
+ handler
240
+ });
@@ -0,0 +1,7 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ var __defProp = Object.defineProperty;
3
+ var __name = (target, value) => __defProp(target, "name", {
4
+ value,
5
+ configurable: true
6
+ });
7
+ export { __name };
@@ -0,0 +1,195 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ import { __name } from "../chunk-V4MHYKRI.js";
3
+
4
+ // src/cloudformation/createLambdaPostgresQueryTemplate.ts
5
+ var HANDLER_DEFAULT = "handler.handler";
6
+ var MEMORY_SIZE_DEFAULT = 128;
7
+ var TIMEOUT_DEFAULT = 30;
8
+ var createLambdaPostgresQueryTemplate = /* @__PURE__ */__name(({
9
+ handler: handler2 = HANDLER_DEFAULT,
10
+ memorySize = 128,
11
+ timeout = 30
12
+ } = {}) => {
13
+ return {
14
+ AWSTemplateFormatVersion: "2010-09-09",
15
+ Description: "A Lambda function to query PostgreSQL.",
16
+ Parameters: {
17
+ DatabaseHost: {
18
+ Type: "String",
19
+ Description: "Database host."
20
+ },
21
+ DatabaseHostReadOnly: {
22
+ Type: "String",
23
+ Description: "Database host read only."
24
+ },
25
+ DatabaseName: {
26
+ Type: "String",
27
+ Description: "Database name."
28
+ },
29
+ DatabaseUsername: {
30
+ Type: "String",
31
+ Description: "Database username."
32
+ },
33
+ DatabasePassword: {
34
+ Type: "String",
35
+ Description: "Database password."
36
+ },
37
+ DatabasePort: {
38
+ Type: "String",
39
+ Default: "5432",
40
+ Description: "Database port."
41
+ },
42
+ LambdaS3Bucket: {
43
+ Type: "String",
44
+ Description: "The S3 bucket where the Lambda code is stored."
45
+ },
46
+ LambdaS3Key: {
47
+ Type: "String",
48
+ Description: "The S3 key where the Lambda code is stored."
49
+ },
50
+ LambdaS3ObjectVersion: {
51
+ Type: "String",
52
+ Description: "The S3 object version of the Lambda code."
53
+ },
54
+ SecurityGroupIds: {
55
+ Description: "Security Group IDs",
56
+ Type: "List<AWS::EC2::SecurityGroup::Id>"
57
+ },
58
+ SubnetIds: {
59
+ Description: "Subnet IDs",
60
+ Type: "List<AWS::EC2::Subnet::Id>"
61
+ }
62
+ },
63
+ Resources: {
64
+ LambdaQueryExecutionRole: {
65
+ Type: "AWS::IAM::Role",
66
+ Properties: {
67
+ AssumeRolePolicyDocument: {
68
+ Version: "2012-10-17",
69
+ Statement: [{
70
+ Effect: "Allow",
71
+ Principal: {
72
+ Service: "lambda.amazonaws.com"
73
+ },
74
+ Action: "sts:AssumeRole"
75
+ }]
76
+ },
77
+ ManagedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"]
78
+ }
79
+ },
80
+ LambdaQueryFunction: {
81
+ Type: "AWS::Lambda::Function",
82
+ Properties: {
83
+ Code: {
84
+ S3Bucket: {
85
+ Ref: "LambdaS3Bucket"
86
+ },
87
+ S3Key: {
88
+ Ref: "LambdaS3Key"
89
+ },
90
+ S3ObjectVersion: {
91
+ Ref: "LambdaS3ObjectVersion"
92
+ }
93
+ },
94
+ MemorySize: memorySize,
95
+ Timeout: timeout,
96
+ Handler: handler2,
97
+ Role: {
98
+ "Fn::GetAtt": ["LambdaQueryExecutionRole", "Arn"]
99
+ },
100
+ Runtime: "nodejs22.x",
101
+ Environment: {
102
+ Variables: {
103
+ DATABASE_HOST: {
104
+ Ref: "DatabaseHost"
105
+ },
106
+ DATABASE_HOST_READ_ONLY: {
107
+ Ref: "DatabaseHostReadOnly"
108
+ },
109
+ DATABASE_NAME: {
110
+ Ref: "DatabaseName"
111
+ },
112
+ DATABASE_USERNAME: {
113
+ Ref: "DatabaseUsername"
114
+ },
115
+ DATABASE_PASSWORD: {
116
+ Ref: "DatabasePassword"
117
+ },
118
+ DATABASE_PORT: {
119
+ Ref: "DatabasePort"
120
+ }
121
+ }
122
+ },
123
+ VpcConfig: {
124
+ SecurityGroupIds: {
125
+ Ref: "SecurityGroupIds"
126
+ },
127
+ SubnetIds: {
128
+ Ref: "SubnetIds"
129
+ }
130
+ }
131
+ }
132
+ },
133
+ LambdaQueryFunctionLogs: {
134
+ Type: "AWS::Logs::LogGroup",
135
+ DependsOn: "LambdaQueryFunction",
136
+ Properties: {
137
+ LogGroupName: {
138
+ "Fn::Join": ["", ["/aws/lambda/", {
139
+ Ref: "LambdaQueryFunction"
140
+ }]]
141
+ },
142
+ RetentionInDays: 7
143
+ }
144
+ }
145
+ },
146
+ Outputs: {
147
+ LambdaPostgresQueryFunction: {
148
+ Description: "Lambda function to query PostgreSQL.",
149
+ Value: {
150
+ Ref: "LambdaQueryFunction"
151
+ }
152
+ },
153
+ LambdaPostgresQueryFunctionArn: {
154
+ Description: "Lambda function to query PostgreSQL ARN.",
155
+ Value: {
156
+ "Fn::GetAtt": ["LambdaQueryFunction", "Arn"]
157
+ }
158
+ }
159
+ }
160
+ };
161
+ }, "createLambdaPostgresQueryTemplate");
162
+
163
+ // src/cloudformation/lambdaQueryHandler.ts
164
+ import { Client } from "pg";
165
+ var database = process.env.DATABASE_NAME;
166
+ var username = process.env.DATABASE_USERNAME;
167
+ var password = process.env.DATABASE_PASSWORD;
168
+ var host = process.env.DATABASE_HOST;
169
+ var hostReadOnly = process.env.DATABASE_HOST_READ_ONLY;
170
+ var port = process.env.DATABASE_PORT;
171
+ var handler = /* @__PURE__ */__name(async event => {
172
+ try {
173
+ const client = new Client({
174
+ database,
175
+ user: username,
176
+ password,
177
+ host: event.readOnly && hostReadOnly ? hostReadOnly : host,
178
+ port: Number(port)
179
+ });
180
+ await client.connect();
181
+ try {
182
+ const res = await client.query(event);
183
+ return res;
184
+ } finally {
185
+ await client.end();
186
+ }
187
+ } catch (error) {
188
+ console.error("Error running query", {
189
+ error,
190
+ event
191
+ });
192
+ throw error;
193
+ }
194
+ }, "handler");
195
+ export { HANDLER_DEFAULT, MEMORY_SIZE_DEFAULT, TIMEOUT_DEFAULT, createLambdaPostgresQueryTemplate, handler };
@@ -0,0 +1,61 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ import { __name } from "./chunk-V4MHYKRI.js";
3
+
4
+ // src/query.ts
5
+ import { InvokeCommand, LambdaClient } from "@aws-sdk/client-lambda";
6
+ import camelcaseKeys from "camelcase-keys";
7
+ var lambdaClient = new LambdaClient();
8
+ var textDecoder = new TextDecoder("utf-8");
9
+ var query = /* @__PURE__ */__name(async params => {
10
+ try {
11
+ const {
12
+ readOnly = true,
13
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
14
+ lambdaPostgresQueryFunction = process.env.LAMBDA_POSTGRES_QUERY_FUNCTION,
15
+ camelCaseKeys = true,
16
+ ...pgParams
17
+ } = typeof params === "string" ? {
18
+ text: params
19
+ } : params;
20
+ const input = {
21
+ FunctionName: lambdaPostgresQueryFunction,
22
+ Payload: JSON.stringify({
23
+ readOnly,
24
+ ...pgParams
25
+ })
26
+ };
27
+ const {
28
+ Payload
29
+ } = await lambdaClient.send(new InvokeCommand(input));
30
+ if (!Payload) {
31
+ console.error("No payload returned from lambda query", {
32
+ input
33
+ });
34
+ throw new Error("No payload returned from lambda query");
35
+ }
36
+ const data = textDecoder.decode(Payload);
37
+ const result = JSON.parse(data);
38
+ if ("errorType" in result) {
39
+ throw new Error(result.errorMessage);
40
+ }
41
+ return {
42
+ ...result,
43
+ rows: result.rows.map(row => {
44
+ if (!camelCaseKeys) {
45
+ return row;
46
+ }
47
+ return {
48
+ ...row,
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ ...camelcaseKeys(row, {
51
+ deep: true
52
+ })
53
+ };
54
+ })
55
+ };
56
+ } catch (error) {
57
+ console.error("Error invoking lambda-postgres-query: ", error);
58
+ throw error;
59
+ }
60
+ }, "query");
61
+ export { query };
@@ -0,0 +1,22 @@
1
+ import * as pg from 'pg';
2
+ import { QueryConfig, QueryResultRow } from 'pg';
3
+
4
+ type QueryParams = {
5
+ readOnly?: boolean;
6
+ lambdaPostgresQueryFunction?: string;
7
+ camelCaseKeys?: boolean;
8
+ } & QueryConfig;
9
+ type LambdaError = {
10
+ errorType: 'Error';
11
+ errorMessage: string;
12
+ trace: string[];
13
+ };
14
+ declare const query: <Rows extends QueryResultRow = any>(params: QueryParams | string) => Promise<{
15
+ rows: any[];
16
+ command: string;
17
+ rowCount: number | null;
18
+ oid: number;
19
+ fields: pg.FieldDef[];
20
+ }>;
21
+
22
+ export { type LambdaError, type QueryParams, query };
@@ -0,0 +1,22 @@
1
+ import * as pg from 'pg';
2
+ import { QueryConfig, QueryResultRow } from 'pg';
3
+
4
+ type QueryParams = {
5
+ readOnly?: boolean;
6
+ lambdaPostgresQueryFunction?: string;
7
+ camelCaseKeys?: boolean;
8
+ } & QueryConfig;
9
+ type LambdaError = {
10
+ errorType: 'Error';
11
+ errorMessage: string;
12
+ trace: string[];
13
+ };
14
+ declare const query: <Rows extends QueryResultRow = any>(params: QueryParams | string) => Promise<{
15
+ rows: any[];
16
+ command: string;
17
+ rowCount: number | null;
18
+ oid: number;
19
+ fields: pg.FieldDef[];
20
+ }>;
21
+
22
+ export { type LambdaError, type QueryParams, query };
package/dist/index.js ADDED
@@ -0,0 +1,109 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ "use strict";
3
+
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __name = (target, value) => __defProp(target, "name", {
11
+ value,
12
+ configurable: true
13
+ });
14
+ var __export = (target, all) => {
15
+ for (var name in all) __defProp(target, name, {
16
+ get: all[name],
17
+ enumerable: true
18
+ });
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from === "object" || typeof from === "function") {
22
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
23
+ get: () => from[key],
24
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
25
+ });
26
+ }
27
+ return to;
28
+ };
29
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
30
+ // If the importer is in node compatibility mode or this is not an ESM
31
+ // file that has been converted to a CommonJS file using a Babel-
32
+ // compatible transform (i.e. "__esModule" has not been set), then set
33
+ // "default" to the CommonJS "module.exports" for node compatibility.
34
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
35
+ value: mod,
36
+ enumerable: true
37
+ }) : target, mod));
38
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
39
+ value: true
40
+ }), mod);
41
+
42
+ // src/index.ts
43
+ var index_exports = {};
44
+ __export(index_exports, {
45
+ query: () => query
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+
49
+ // src/query.ts
50
+ var import_client_lambda = require("@aws-sdk/client-lambda");
51
+ var import_camelcase_keys = __toESM(require("camelcase-keys"), 1);
52
+ var lambdaClient = new import_client_lambda.LambdaClient();
53
+ var textDecoder = new TextDecoder("utf-8");
54
+ var query = /* @__PURE__ */__name(async params => {
55
+ try {
56
+ const {
57
+ readOnly = true,
58
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
59
+ lambdaPostgresQueryFunction = process.env.LAMBDA_POSTGRES_QUERY_FUNCTION,
60
+ camelCaseKeys = true,
61
+ ...pgParams
62
+ } = typeof params === "string" ? {
63
+ text: params
64
+ } : params;
65
+ const input = {
66
+ FunctionName: lambdaPostgresQueryFunction,
67
+ Payload: JSON.stringify({
68
+ readOnly,
69
+ ...pgParams
70
+ })
71
+ };
72
+ const {
73
+ Payload
74
+ } = await lambdaClient.send(new import_client_lambda.InvokeCommand(input));
75
+ if (!Payload) {
76
+ console.error("No payload returned from lambda query", {
77
+ input
78
+ });
79
+ throw new Error("No payload returned from lambda query");
80
+ }
81
+ const data = textDecoder.decode(Payload);
82
+ const result = JSON.parse(data);
83
+ if ("errorType" in result) {
84
+ throw new Error(result.errorMessage);
85
+ }
86
+ return {
87
+ ...result,
88
+ rows: result.rows.map(row => {
89
+ if (!camelCaseKeys) {
90
+ return row;
91
+ }
92
+ return {
93
+ ...row,
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ ...(0, import_camelcase_keys.default)(row, {
96
+ deep: true
97
+ })
98
+ };
99
+ })
100
+ };
101
+ } catch (error) {
102
+ console.error("Error invoking lambda-postgres-query: ", error);
103
+ throw error;
104
+ }
105
+ }, "query");
106
+ // Annotate the CommonJS export names for ESM import in node:
107
+ 0 && (module.exports = {
108
+ query
109
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/lambda-postgres-query",
3
- "version": "0.3.17",
3
+ "version": "0.3.18",
4
4
  "description": "Create a Lambda function that queries a PostgreSQL database.",
5
5
  "license": "MIT",
6
6
  "author": "ttoss",
@@ -40,8 +40,8 @@
40
40
  "aws-sdk-client-mock": "^4.1.0",
41
41
  "jest": "^30.2.0",
42
42
  "tsup": "^8.5.1",
43
- "@ttoss/test-utils": "^4.0.2",
44
- "@ttoss/config": "^1.35.12"
43
+ "@ttoss/config": "^1.35.12",
44
+ "@ttoss/test-utils": "^4.0.2"
45
45
  },
46
46
  "keywords": [
47
47
  "aws",