data-api-client 1.2.0 → 2.0.0-beta.0

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.
Files changed (3) hide show
  1. package/README.md +136 -110
  2. package/index.js +334 -298
  3. package/package.json +6 -5
package/README.md CHANGED
@@ -4,7 +4,9 @@
4
4
  [![npm](https://img.shields.io/npm/v/data-api-client.svg)](https://www.npmjs.com/package/data-api-client)
5
5
  [![npm](https://img.shields.io/npm/l/data-api-client.svg)](https://www.npmjs.com/package/data-api-client)
6
6
 
7
- The **Data API Client** is a lightweight wrapper that simplifies working with the Amazon Aurora Serverless Data API by abstracting away the notion of field values. This abstraction annotates native JavaScript types supplied as input parameters, as well as converts annotated response data to native JavaScript types. It's basically a [DocumentClient](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html) for the Data API. It also promisifies the `AWS.RDSDataService` client to make working with `async/await` or Promise chains easier AND dramatically simplifies **transactions**.
7
+ ## v2.0 BETA with support for AWS SDK v3
8
+
9
+ The **Data API Client** is a lightweight wrapper that simplifies working with the Amazon Aurora Serverless Data API by abstracting away the notion of field values. This abstraction annotates native JavaScript types supplied as input parameters, as well as converts annotated response data to native JavaScript types. It's basically a [DocumentClient](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html) for the Data API. It also exposes simplified versions of the native AWS SDK v3 `RDSDataClient` methods to make working with `async/await` or Promise chains easier AND dramatically simplifies **transactions**.
8
10
 
9
11
  For more information about the Aurora Serverless Data API, you can review the [official documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html) or read [Aurora Serverless Data API: An (updated) First Look](https://www.jeremydaly.com/aurora-serverless-data-api-a-first-look/) for some more insights on performance.
10
12
 
@@ -33,34 +35,31 @@ let result = await data.query(`SELECT * FROM myTable`)
33
35
  // }
34
36
 
35
37
  // SELECT with named parameters
36
- let resultParams = await data.query(
37
- `SELECT * FROM myTable WHERE id = :id`,
38
- { id: 2 }
39
- )
38
+ let resultParams = await data.query(`SELECT * FROM myTable WHERE id = :id`, {
39
+ id: 2
40
+ })
40
41
  // { records: [ { id: 2, name: 'Mike', age: 52 } ] }
41
42
 
42
43
  // INSERT with named parameters
43
- let insert = await data.query(
44
- `INSERT INTO myTable (name,age,has_curls) VALUES(:name,:age,:curls)`,
45
- { name: 'Greg', age: 18, curls: false }
46
- )
44
+ let insert = await data.query(`INSERT INTO myTable (name,age,has_curls) VALUES(:name,:age,:curls)`, {
45
+ name: 'Greg',
46
+ age: 18,
47
+ curls: false
48
+ })
47
49
 
48
50
  // BATCH INSERT with named parameters
49
- let batchInsert = await data.query(
50
- `INSERT INTO myTable (name,age,has_curls) VALUES(:name,:age,:curls)`,
51
- [
52
- [{ name: 'Marcia', age: 17, curls: false }],
53
- [{ name: 'Peter', age: 15, curls: false }],
54
- [{ name: 'Jan', age: 15, curls: false }],
55
- [{ name: 'Cindy', age: 12, curls: true }],
56
- [{ name: 'Bobby', age: 12, curls: false }]
57
- ]
58
- )
51
+ let batchInsert = await data.query(`INSERT INTO myTable (name,age,has_curls) VALUES(:name,:age,:curls)`, [
52
+ [{ name: 'Marcia', age: 17, curls: false }],
53
+ [{ name: 'Peter', age: 15, curls: false }],
54
+ [{ name: 'Jan', age: 15, curls: false }],
55
+ [{ name: 'Cindy', age: 12, curls: true }],
56
+ [{ name: 'Bobby', age: 12, curls: false }]
57
+ ])
59
58
  // Update with named parameters
60
- let update = await data.query(
61
- `UPDATE myTable SET age = :age WHERE id = :id`,
62
- { age: 13, id: 5 }
63
- )
59
+ let update = await data.query(`UPDATE myTable SET age = :age WHERE id = :id`, {
60
+ age: 13,
61
+ id: 5
62
+ })
64
63
 
65
64
  // Delete with named parameters
66
65
  let remove = await data.query(
@@ -73,24 +72,19 @@ let custom = data.query({
73
72
  sql: `SELECT * FROM myOtherTable WHERE id = :id AND active = :isActive`,
74
73
  continueAfterTimeout: true,
75
74
  database: 'myOtherDatabase',
76
- parameters: [
77
- { id: 123},
78
- { name: 'isActive', value: { booleanValue: true } }
79
- ]
75
+ parameters: [{ id: 123 }, { name: 'isActive', value: { booleanValue: true } }]
80
76
  })
81
77
  ```
82
78
 
83
79
  ## Why do I need this?
84
- The [Data API](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html) requires you to specify data types when passing in parameters. The basic `INSERT` example above would look like this using the native `AWS.RDSDataService` class:
85
80
 
86
- ```javascript
87
- const AWS = require('aws-sdk')
88
- const data = new AWS.RDSDataService()
81
+ The [Data API](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html) requires you to specify data types when passing in parameters. The basic `INSERT` example above would look like this using the native `RDSDataClient` class:
89
82
 
90
- /*** Assuming we're in an async function ***/
83
+ ```javascript
84
+ const { RDSDataClient, ExecuteStatementCommand } = require('@aws-sdk/client-rds-data')
85
+ const client = new RDSDataClient({ region: 'us-east-1' })
91
86
 
92
- // INSERT with named parameters
93
- let insert = await data.executeStatement({
87
+ const params = {
94
88
  secretArn: 'arn:aws:secretsmanager:us-east-1:XXXXXXXXXXXX:secret:mySecret',
95
89
  resourceArn: 'arn:aws:rds:us-east-1:XXXXXXXXXXXX:cluster:my-cluster-name',
96
90
  database: 'myDatabase',
@@ -100,10 +94,20 @@ let insert = await data.executeStatement({
100
94
  { name: 'age', value: { longValue: 10 } },
101
95
  { name: 'curls', value: { booleanValue: false } }
102
96
  ]
103
- ).promise()
97
+ }
98
+
99
+ const command = new ExecuteStatementCommand(params)
100
+
101
+ // async/await.
102
+ try {
103
+ const data = await client.send(command)
104
+ // process data.
105
+ } catch (error) {
106
+ // error handling.
107
+ }
104
108
  ```
105
109
 
106
- Specifying all of those data types in the parameters is a bit clunky. In addition to requiring types for parameters, it also returns each field as an object with its value assigned to a key that represents its data type, like this:
110
+ Specifying all of those data types in the parameters and writing all that SDK code is a bit clunky. In addition to requiring types for parameters, it also returns each field as an object with its value assigned to a key that represents its data type, like this:
107
111
 
108
112
  ```javascript
109
113
  { // id field
@@ -119,9 +123,11 @@ Specifying all of those data types in the parameters is a bit clunky. In additio
119
123
  "booleanValue": false
120
124
  }
121
125
  ```
126
+
122
127
  Not only are there no column names, but you have to pull the value from the data type field. Lots of extra work that the **Data API Client** handles automatically for you. 😀
123
128
 
124
129
  ## Installation and Setup
130
+
125
131
  ```
126
132
  npm i data-api-client
127
133
  ```
@@ -132,26 +138,25 @@ For more information on enabling Data API, see [Enabling Data API](#enabling-dat
132
138
 
133
139
  Below is a table containing all of the possible configuration options for the `data-api-client`. Additional details are provided throughout the documentation.
134
140
 
135
- | Property | Type | Description | Default |
136
- | -------- | ---- | ----------- | ------- |
137
- | resourceArn | `string` | The ARN of your Aurora Serverless Cluster. This value is *required*, but can be overridden when querying. | |
138
- | secretArn | `string` | The ARN of the secret associated with your database credentials. This is *required*, but can be overridden when querying. | |
139
- | database | `string` | *Optional* default database to use with queries. Can be overridden when querying. | |
140
- | engine | `mysql` or `pg` | The type of database engine you're connecting to (MySQL or Postgres). | `mysql` |
141
- | hydrateColumnNames | `boolean` | When `true`, results will be returned as objects with column names as keys. If `false`, results will be returned as an array of values. | `true` |
142
- | ~~keepAlive~~ (deprecated) | `boolean` | See [Connection Reuse](#connection-reuse) below. | |
143
- | ~~sslEnabled~~ (deprecated) | `boolean` | Set this in the `options` | `true` |
144
- | options | `object` | An *optional* configuration object that is passed directly into the RDSDataService constructor. See [here](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/RDSDataService.html#constructor-property) for available options. | `{}` |
145
- | ~~region~~ (deprecated) | `string` | Set this in the `options` | |
146
- | formatOptions | `object` | Formatting options to auto parse dates and coerce native JavaScript date objects to MySQL supported date formats. Valid keys are `deserializeDate` and `treatAsLocalDate`. Both accept boolean values. | Both `false` |
141
+ | Property | Type | Description | Default |
142
+ | ------------------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
143
+ | resourceArn | `string` | The ARN of your Aurora Serverless Cluster. This value is _required_, but can be overridden when querying. | |
144
+ | secretArn | `string` | The ARN of the secret associated with your database credentials. This is _required_, but can be overridden when querying. | |
145
+ | database | `string` | _Optional_ default database to use with queries. Can be overridden when querying. | |
146
+ | engine | `mysql` or `pg` | The type of database engine you're connecting to (MySQL or Postgres). | `mysql` |
147
+ | hydrateColumnNames | `boolean` | When `true`, results will be returned as objects with column names as keys. If `false`, results will be returned as an array of values. | `true` |
148
+ | options | `object` | An _optional_ configuration object that is passed directly into the v3 RDSDataClient constructor. Use this to set `region`, `tls`, etc. See [here](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rds-data/interfaces/rdsdataclientconfig.html) for available options. | `{}` |
149
+ | formatOptions | `object` | Formatting options to auto parse dates and coerce native JavaScript date objects to MySQL supported date formats. Valid keys are `deserializeDate` and `treatAsLocalDate`. Both accept boolean values. | Both `false` |
150
+ | wrapper | `function` | A custom wrapper around `@aws-sdk/client-rds-data` used to enable AWS X-Ray. | |
147
151
 
148
152
  ### Connection Reuse
153
+
149
154
  It is recommended to enable connection reuse as this dramatically decreases the latency of subsequent calls to the AWS API. This can be done by setting an environment variable
150
155
  `AWS_NODEJS_CONNECTION_REUSE_ENABLED=1`. For more information see the [AWS SDK documentation](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html).
151
156
 
152
157
  ## How to use this module
153
158
 
154
- The **Data API Client** wraps the [RDSDataService Class](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/RDSDataService.html), providing you with a number of convenience features to make your workflow easier. The module also exposes **promisified** versions of all the standard `RDSDataService` methods, with your default configuration information already merged in. 😉
159
+ The **Data API Client** wraps the AWS SDK v3 [RDSDataClient Class](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rds-data/index.html), providing you with a number of convenience features to make your workflow easier. The module also exposes simplified versions of all the native `RDSDataClient` methods, with your default configuration information already merged in. 😉
155
160
 
156
161
  To use the Data API Client, require the module and instantiate it with your [Configuration options](#configuration-options). If you are using it with AWS Lambda, require it **OUTSIDE** your main handler function. This will allow you to reuse the initialized module on subsequent invocations.
157
162
 
@@ -165,6 +170,7 @@ const data = require('data-api-client')({
165
170
  ```
166
171
 
167
172
  ### Running a query
173
+
168
174
  Once initialized, running a query is super simple. Use the `query()` method and pass in your SQL statement:
169
175
 
170
176
  ```javascript
@@ -172,8 +178,9 @@ let result = await data.query(`SELECT * FROM myTable`)
172
178
  ```
173
179
 
174
180
  By default, this will return your rows as an array of objects with column names as property names:
181
+
175
182
  ```javascript
176
- [
183
+ ;[
177
184
  { id: 1, name: 'Alice', age: null },
178
185
  { id: 2, name: 'Mike', age: 52 },
179
186
  { id: 3, name: 'Carol', age: 50 }
@@ -183,23 +190,22 @@ By default, this will return your rows as an array of objects with column names
183
190
  To query with parameters, you can use named parameters in your SQL, and then provider an object containing your parameters as the second argument to the `query()` method:
184
191
 
185
192
  ```javascript
186
- let result = await data.query(`
193
+ let result = await data.query(
194
+ `
187
195
  SELECT * FROM myTable WHERE id = :id AND created > :createDate`,
188
196
  { id: 2, createDate: '2019-06-01' }
189
197
  )
190
198
  ```
191
199
 
192
- The Data API Client will automatically convert your parameters into the correct Data API parameter format using native JavaScript types. If you prefer to use the clunky format, or you need more control over the data type, you can just pass in the `RDSDataService` format:
200
+ The Data API Client will automatically convert your parameters into the correct Data API parameter format using native JavaScript types. If you prefer to use the clunky format, or you need more control over the data type, you can just pass in the `RDSDataClient` format:
193
201
 
194
202
  ```javascript
195
- let result = await data.query(
196
- `SELECT * FROM myTable WHERE id = :id AND created > :createDate`,
197
- [ // An array of objects is totally cool, too. We'll merge them for you.
198
- { id: 2 },
199
- // Data API Client just passes this straight on through
200
- { name: 'createDate', value: { blobValue: new Buffer('2019-06-01') } }
201
- ]
202
- )
203
+ let result = await data.query(`SELECT * FROM myTable WHERE id = :id AND created > :createDate`, [
204
+ // An array of objects is totally cool, too. We'll merge them for you.
205
+ { id: 2 },
206
+ // Data API Client just passes this straight on through
207
+ { name: 'createDate', value: { blobValue: new Buffer('2019-06-01') } }
208
+ ])
203
209
  ```
204
210
 
205
211
  If you want even more control, you can pass in an `object` as the first parameter. This will allow you to add additional configuration options and override defaults as well.
@@ -209,35 +215,34 @@ let result = await data.query({
209
215
  sql: `SELECT * FROM myTable WHERE id = :id`,
210
216
  parameters: [ { id: 2 } ], // or just { id: 2 }
211
217
  database: 'someOtherDatabase', // override default database
212
- schema: 'mySchema', // RDSDataService config option
213
- continueAfterTimeout: true, // RDSDataService config option (non-batch only)
214
- includeResultMetadata: true, // RDSDataService config option (non-batch only)
218
+ schema: 'mySchema', // RDSDataClient config option
219
+ continueAfterTimeout: true, // RDSDataClient config option (non-batch only)
220
+ includeResultMetadata: true, // RDSDataClient config option (non-batch only)
215
221
  hydrateColumnNames: false, // Returns each record as an arrays of values
216
- transactionId: 'AQC5SRDIm...ZHXP/WORU=' // RDSDataService config option
222
+ transactionId: 'AQC5SRDIm...ZHXP/WORU=' // RDSDataClient config option
217
223
  }
218
224
  ```
219
225
 
220
- Sometimes you might want to have *dynamic identifiers* in your SQL statements. Unfortunately, the `RDSDataService` doesn't do this, but the **Data API Client** does! We're using the [sqlstring](https://github.com/mysqljs/sqlstring) module under the hood, so as long as [NO_BACKSLASH_ESCAPES](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_backslash_escapes) SQL mode is disabled (which is the default state for Aurora Serverless), you're good to go. Use a double colon (`::`) prefix to create *named identifiers* and you can do cool things like this:
226
+ Sometimes you might want to have _dynamic identifiers_ in your SQL statements. Unfortunately, the `RDSDataClient` doesn't do this, but the **Data API Client** does! We're using the [sqlstring](https://github.com/mysqljs/sqlstring) module under the hood, so as long as [NO_BACKSLASH_ESCAPES](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_backslash_escapes) SQL mode is disabled (which is the default state for Aurora Serverless), you're good to go. Use a double colon (`::`) prefix to create _named identifiers_ and you can do cool things like this:
221
227
 
222
228
  ```javascript
223
- let result = await data.query(
224
- `SELECT ::fields FROM ::table WHERE id > :id`,
225
- {
226
- fields: ['id','name','created'],
227
- table: 'table_' + someScaryUserInput, // someScaryUserInput = 123abc
228
- id: 1
229
- }
230
- )
229
+ let result = await data.query(`SELECT ::fields FROM ::table WHERE id > :id`, {
230
+ fields: ['id', 'name', 'created'],
231
+ table: 'table_' + someScaryUserInput, // someScaryUserInput = 123abc
232
+ id: 1
233
+ })
231
234
  ```
232
235
 
233
236
  Which will produce a query like this:
237
+
234
238
  ```sql
235
239
  SELECT `id`, `name`, `created` FROM `table_123abc` WHERE id > :id LIMIT 10
236
240
  ```
237
241
 
238
- You'll notice that we leave the *named parameters* alone. Anything that Data API and the `RDSDataService` Class currently handles, we defer to them.
242
+ You'll notice that we leave the _named parameters_ alone. Anything that Data API and the `RDSDataClient` Class currently handles, we defer to them.
239
243
 
240
244
  ### Type-Casting
245
+
241
246
  The Aurora Data API can sometimes give you trouble with certain data types, such as uuid, unless you explicitly cast them. While you can certainly do this manually in your SQL string, the Data API Client offers a really easy way to handle this for you.
242
247
 
243
248
  ```javascript
@@ -267,28 +272,28 @@ const result = await data.query(
267
272
  ```
268
273
 
269
274
  ### Batch Queries
270
- The `RDSDataService` Class provides a `batchExecuteStatement` method that allows you to execute a prepared statement multiple times using different parameter sets. This is only allowed for `INSERT`, `UPDATE` and `DELETE` queries, but is much more efficient than issuing multiple `executeStatement` calls. The Data API Client handles the switching for you based on *how* you send in your parameters.
275
+
276
+ The `RDSDataClient` Class provides a `batchExecuteStatement` method that allows you to execute a prepared statement multiple times using different parameter sets. This is only allowed for `INSERT`, `UPDATE` and `DELETE` queries, but is much more efficient than issuing multiple `executeStatement` calls. The Data API Client handles the switching for you based on _how_ you send in your parameters.
271
277
 
272
278
  To issue a batch query, use the `query()` method (either by passing an object or using the two arity form), and provide multiple parameter sets as nested arrays. For example, if you wanted to update multiple records at once, your query might look like this:
273
279
 
274
280
  ```javascript
275
- let result = await data.query(
276
- `UPDATE myTable SET name = :newName WHERE id = :id`,
277
- [
278
- [ { id: 1, newName: 'Alice Franklin' } ],
279
- [ { id: 7, newName: 'Jan Glass' } ]
280
- ]
281
- )
281
+ let result = await data.query(`UPDATE myTable SET name = :newName WHERE id = :id`, [
282
+ [{ id: 1, newName: 'Alice Franklin' }],
283
+ [{ id: 7, newName: 'Jan Glass' }]
284
+ ])
282
285
  ```
283
286
 
284
- You can also use *named identifiers* in batch queries, which will update and escape your SQL statement. **ONLY** parameters from the first parameter set will be used to update the query. Subsequent parameter sets will only update *named parameters* supported by the Data API.
287
+ You can also use _named identifiers_ in batch queries, which will update and escape your SQL statement. **ONLY** parameters from the first parameter set will be used to update the query. Subsequent parameter sets will only update _named parameters_ supported by the Data API.
285
288
 
286
289
  Whenever a batch query is executed, it returns an `updateResults` field. Other than for `INSERT` statements, however, there is no useful feedback provided by this field.
287
290
 
288
291
  ### Retrieving Insert IDs
292
+
289
293
  The Data API returns a `generatedFields` array that contains the value of auto-incrementing primary keys. If this value is returned, the Data API Client will parse this and return it as the `insertId`. This also works for batch queries as well.
290
294
 
291
295
  ## Transaction Support
296
+
292
297
  Transaction support in the Data API Client has been dramatically simplified. Start a new transaction using the `transaction()` method, and then chain queries using the `query()` method. The `query()` method supports all standard query options. Alternatively, you can specify a function as the only argument in a `query()` method call and return the arguments as an array of values. The function receives two arguments, the result of the last query executed, and an array containing all the previous query results. This is useful if you need values from a previous query as part of your transaction.
293
298
 
294
299
  You can specify an optional `rollback()` method in the chain. This will receive the `error` object and the `transactionStatus` object, allowing you to add additional logging or perform some other action. Call the `commit()` method when you are ready to execute the queries.
@@ -304,10 +309,13 @@ let results = await mysql.transaction()
304
309
  With a function to get the `insertId` from the previous query:
305
310
 
306
311
  ```javascript
307
- let results = await mysql.transaction()
312
+ let results = await mysql
313
+ .transaction()
308
314
  .query('INSERT INTO myTable (name) VALUES(:name)', { name: 'Tiger' })
309
- .query((r) => [ 'UPDATE myTable SET age = :age WHERE id = :id', { age: 4, id: r.insertId } ])
310
- .rollback((e,status) => { /* do something with the error */ }) // optional
315
+ .query((r) => ['UPDATE myTable SET age = :age WHERE id = :id', { age: 4, id: r.insertId }])
316
+ .rollback((e, status) => {
317
+ /* do something with the error */
318
+ }) // optional
311
319
  .commit() // execute the queries
312
320
  ```
313
321
 
@@ -317,7 +325,8 @@ By default, the `transaction()` method will use the `resourceArn`, `secretArn` a
317
325
 
318
326
  ### Using native methods directly
319
327
 
320
- The Data API Client exposes *promisified* versions of the five RDSDataService methods. These are:
328
+ The Data API Client exposes simplified versions of the five `RDSDataClient` methods. These are:
329
+
321
330
  - `batchExecuteStatement`
322
331
  - `beginTransaction`
323
332
  - `commitTransaction`
@@ -336,11 +345,28 @@ let result = await data.executeStatement({
336
345
  )
337
346
  ```
338
347
 
348
+ ## Custom SDK Wrapper
349
+
350
+ `data-api-client` allows you to wrap the instance of `RDSDataClient` to enable additional features like AWS X-Ray. You can specify a function using the optional `wrapper` paramenter. The function will receive one parameter which contains an instantiated `RDSDataClient` instance.
351
+
352
+ ```javascript
353
+ // Import X-Ray
354
+ const AWSXRay = require('aws-xray-sdk');
355
+
356
+ // Instantiate data-api-client with the AWSXRay.captureAWSv3Client wrapper
357
+ const data = require('data-api-client')({
358
+ wrapper: AWSXRay.captureAWSv3Client,
359
+ ...
360
+ })
361
+ ```
362
+
339
363
  ## Data API Limitations / Wonkiness
340
- The first GA release of the Data API has *a lot* of promise, unfortunately, there are still quite a few things that make it a bit wonky and may require you to implement some workarounds. I've outlined some of my findings below.
364
+
365
+ The first GA release of the Data API has _a lot_ of promise, unfortunately, there are still quite a few things that make it a bit wonky and may require you to implement some workarounds. I've outlined some of my findings below.
341
366
 
342
367
  ### You can't send in an array of values
343
- The GitHub repo for RDSDataService mentions something about `arrayValues`, but I've been unable to get arrays (including TypedArrays and Buffers) to be used for parameters with `IN` clauses. For example, the following query will **NOT** work:
368
+
369
+ The GitHub repo for RDSDataClient mentions something about `arrayValues`, but I've been unable to get arrays (including TypedArrays and Buffers) to be used for parameters with `IN` clauses. For example, the following query will **NOT** work:
344
370
 
345
371
  ```javascript
346
372
  let result = await data.executeStatement({
@@ -357,9 +383,11 @@ let result = await data.executeStatement({
357
383
  I'm using `blobValue` because it's the only generic value field. You could send it in as a string, but then it only uses the first value. Hopefully they will add an `arrayValues` or something similar to support this in the future.
358
384
 
359
385
  ### ~~Named parameters MUST be sent in order~~
360
- ~~Read that again if you need to. So parameters have to be **BOTH** named and *in order*, otherwise the query **may** fail. I stress **may**, because if you send in two fields of compatible type in the wrong order, the query will work, just with your values flipped. 🤦🏻‍♂️ Watch out for this one.~~ 👈This was fixed!
386
+
387
+ ~~Read that again if you need to. So parameters have to be **BOTH** named and _in order_, otherwise the query **may** fail. I stress **may**, because if you send in two fields of compatible type in the wrong order, the query will work, just with your values flipped. 🤦🏻‍♂️ Watch out for this one.~~ 👈This was fixed!
361
388
 
362
389
  ### You can't parameterize identifiers
390
+
363
391
  If you want to use dynamic column or field names, there is no way to do it automatically with the Data API. The `mysql` package, for example, lets you use `??` to dynamically insert escaped identifiers. Something like the example below is currently not possible.
364
392
 
365
393
  ```javascript
@@ -378,18 +406,19 @@ let result = await data.executeStatement({
378
406
 
379
407
  No worries! The Data API Client gives you the ability to parameterize identifiers and auto escape them. Just use a double colon (`::`) to prefix your named identifiers.
380
408
 
381
-
382
409
  ### Batch statements do not give you updated record counts
410
+
383
411
  This one is a bit frustrating. If you execute a standard `executeStatement`, then it will return a `numberOfRecordsUpdated` field for `UPDATE` and `DELETE` queries. This is handy for knowing if your query succeeded. Unfortunately, a `batchExecuteStatement` does not return this field for you.
384
412
 
385
413
  ## Enabling Data API
386
- In order to use the Data API, you must enable it on your Aurora Serverless Cluster and create a Secret. You also musst grant your execution environment a number of permission as outlined in the following sections.
414
+
415
+ In order to use the Data API, you must enable it on your Aurora Serverless Cluster and create a Secret. You also must grant your execution environment a number of permission as outlined in the following sections.
387
416
 
388
417
  ### Enable Data API on your Aurora Serverless Cluster
389
418
 
390
419
  ![Enable Data API in Network & Security settings of your cluster](https://user-images.githubusercontent.com/2053544/58768968-79ee4300-8570-11e9-9266-1433182e0db2.png)
391
420
 
392
- You need to modify your Aurora Serverless cluster by clicking “ACTIONS” and then “Modify Cluster”. Just check the Data API box in the *Network & Security* section and you’re good to go. Remember that your Aurora Serverless cluster still runs in a VPC, even though you don’t need to run your Lambdas in a VPC to access it via the Data API.
421
+ You need to modify your Aurora Serverless cluster by clicking “ACTIONS” and then “Modify Cluster”. Just check the Data API box in the _Network & Security_ section and you’re good to go. Remember that your Aurora Serverless cluster still runs in a VPC, even though you don’t need to run your Lambdas in a VPC to access it via the Data API.
393
422
 
394
423
  ### Set up a secret in the Secrets Manager
395
424
 
@@ -397,7 +426,6 @@ Next you need to set up a secret in the Secrets Manager. This is actually quite
397
426
 
398
427
  ![Enter database credentials and select database to access](https://user-images.githubusercontent.com/2053544/58768974-912d3080-8570-11e9-8878-636dfb742b00.png)
399
428
 
400
-
401
429
  Next we give it a name, this is important, because this will be part of the arn when we set up permissions later. You can give it a description as well so you don’t forget what this secret is about when you look at it in a few weeks.
402
430
 
403
431
  ![Give your secret a name and add a description](https://user-images.githubusercontent.com/2053544/58768984-a7d38780-8570-11e9-8b21-199db5548c73.png)
@@ -411,24 +439,26 @@ You can then configure your rotation settings, if you want, and then you review
411
439
  In order to use the Data API, your execution environment requires several IAM permissions. Below are the minimum permissions required. **Please Note:** The `Resource: "*"` permission for `rds-data` is recommended by AWS (see [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazonrdsdataapi.html#amazonrdsdataapi-resources-for-iam-policies)) because Amazon RDS Data API does not support specifying a resource ARN. The credentials specified in Secrets Manager can be used to restrict access to specific databases.
412
440
 
413
441
  **YAML:**
442
+
414
443
  ```yaml
415
444
  Statement:
416
- - Effect: "Allow"
445
+ - Effect: 'Allow'
417
446
  Action:
418
- - "rds-data:ExecuteSql"
419
- - "rds-data:ExecuteStatement"
420
- - "rds-data:BatchExecuteStatement"
421
- - "rds-data:BeginTransaction"
422
- - "rds-data:RollbackTransaction"
423
- - "rds-data:CommitTransaction"
424
- Resource: "*"
425
- - Effect: "Allow"
447
+ - 'rds-data:ExecuteSql'
448
+ - 'rds-data:ExecuteStatement'
449
+ - 'rds-data:BatchExecuteStatement'
450
+ - 'rds-data:BeginTransaction'
451
+ - 'rds-data:RollbackTransaction'
452
+ - 'rds-data:CommitTransaction'
453
+ Resource: '*'
454
+ - Effect: 'Allow'
426
455
  Action:
427
- - "secretsmanager:GetSecretValue"
428
- Resource: "arn:aws:secretsmanager:{REGION}:{ACCOUNT-ID}:secret:{PATH-TO-SECRET}/*"
456
+ - 'secretsmanager:GetSecretValue'
457
+ Resource: 'arn:aws:secretsmanager:{REGION}:{ACCOUNT-ID}:secret:{PATH-TO-SECRET}/*'
429
458
  ```
430
459
 
431
460
  **JSON:**
461
+
432
462
  ```javascript
433
463
  "Statement" : [
434
464
  {
@@ -451,10 +481,6 @@ Statement:
451
481
  ]
452
482
  ```
453
483
 
454
- ## Sponsors
455
-
456
- [![New Relic](https://user-images.githubusercontent.com/2053544/96728664-55238700-1382-11eb-93cb-82fe7cb5e043.png)](https://ad.doubleclick.net/ddm/trackclk/N1116303.3950900PODSEARCH.COM/B24770737.285235234;dc_trk_aid=479074825;dc_trk_cid=139488579;dc_lat=;dc_rdid=;tag_for_child_directed_treatment=;tfua=;gdpr=${GDPR};gdpr_consent=${GDPR_CONSENT_755})
457
- <IMG SRC="https://ad.doubleclick.net/ddm/trackimp/N1116303.3950900PODSEARCH.COM/B24770737.285235234;dc_trk_aid=479074825;dc_trk_cid=139488579;ord=[timestamp];dc_lat=;dc_rdid=;tag_for_child_directed_treatment=;tfua=;gdpr=${GDPR};gdpr_consent=${GDPR_CONSENT_755}?" BORDER="0" HEIGHT="1" WIDTH="1" ALT="Advertisement">
458
-
459
484
  ## Contributions
485
+
460
486
  Contributions, ideas and bug reports are welcome and greatly appreciated. Please add [issues](https://github.com/jeremydaly/data-api-client/issues) for suggestions and bug reports or create a pull request. You can also contact me on Twitter: [@jeremy_daly](https://twitter.com/jeremy_daly).