@neondatabase/serverless
Advanced tools
Comparing version 0.9.5 to 0.10.0
@@ -0,1 +1,7 @@ | ||
## 0.10.0 (2024-10-07) | ||
Capture stack traces in `NeonDbError`, if `Error.captureStackTrace` is available. | ||
Allow authentication through `JWT` by adding a `authToken` property to the `neon` HTTP connection options. | ||
## 0.9.3 (2024-05-09) | ||
@@ -2,0 +8,0 @@ |
@@ -29,6 +29,5 @@ # Options and configuration | ||
### `arrayMode: boolean` | ||
When `arrayMode` is true, rows are returned as an array of arrays instead of an array of objects: | ||
When `arrayMode` is true, rows are returned as an array of arrays instead of an array of objects: | ||
@@ -47,7 +46,8 @@ ```typescript | ||
const sql = neon(process.env.DATABASE_URL); | ||
const rows = await sql('SELECT * FROM posts WHERE id = $1', [postId], { arrayMode: true }); | ||
const rows = await sql('SELECT * FROM posts WHERE id = $1', [postId], { | ||
arrayMode: true, | ||
}); | ||
// -> [[12, "My post", ...]] | ||
``` | ||
### `fullResults: boolean` | ||
@@ -80,7 +80,8 @@ | ||
const sql = neon(process.env.DATABASE_URL); | ||
const results = await sql('SELECT * FROM posts WHERE id = $1', [postId], { fullResults: true }); | ||
const results = await sql('SELECT * FROM posts WHERE id = $1', [postId], { | ||
fullResults: true, | ||
}); | ||
// -> { ... same as above ... } | ||
``` | ||
### `fetchOptions: Record<string, any>` | ||
@@ -94,3 +95,5 @@ | ||
import { neon } from '@neondatabase/serverless'; | ||
const sql = neon(process.env.DATABASE_URL, { fetchOptions: { priority: 'high' } }); | ||
const sql = neon(process.env.DATABASE_URL, { | ||
fetchOptions: { priority: 'high' }, | ||
}); | ||
const rows = await sql`SELECT * FROM posts WHERE id = ${postId}`; | ||
@@ -106,10 +109,8 @@ ``` | ||
const timeout = setTimeout(() => abortController.abort('timed out'), 10000); | ||
const rows = await sql( | ||
'SELECT * FROM posts WHERE id = $1', [postId], | ||
{ fetchOptions: { signal: abortController.signal } } | ||
); // throws an error if no result received within 10s | ||
const rows = await sql('SELECT * FROM posts WHERE id = $1', [postId], { | ||
fetchOptions: { signal: abortController.signal }, | ||
}); // throws an error if no result received within 10s | ||
clearTimeout(timeout); | ||
``` | ||
## `transaction(...)` function | ||
@@ -128,9 +129,12 @@ | ||
const [posts, tags] = await sql.transaction([ | ||
sql`SELECT * FROM posts ORDER BY posted_at DESC LIMIT ${showLatestN}`, | ||
sql`SELECT * FROM tags`, | ||
], { | ||
isolationLevel: 'RepeatableRead', | ||
readOnly: true, | ||
}); | ||
const [posts, tags] = await sql.transaction( | ||
[ | ||
sql`SELECT * FROM posts ORDER BY posted_at DESC LIMIT ${showLatestN}`, | ||
sql`SELECT * FROM tags`, | ||
], | ||
{ | ||
isolationLevel: 'RepeatableRead', | ||
readOnly: true, | ||
}, | ||
); | ||
``` | ||
@@ -141,7 +145,5 @@ | ||
```javascript | ||
const [authors, tags] = await neon(process.env.DATABASE_URL) | ||
.transaction(txn => [ | ||
txn`SELECT * FROM authors`, | ||
txn`SELECT * FROM tags`, | ||
]); | ||
const [authors, tags] = await neon(process.env.DATABASE_URL).transaction( | ||
(txn) => [txn`SELECT * FROM authors`, txn`SELECT * FROM tags`], | ||
); | ||
``` | ||
@@ -153,3 +155,2 @@ | ||
### `isolationMode` | ||
@@ -159,3 +160,2 @@ | ||
### `readOnly` | ||
@@ -165,3 +165,2 @@ | ||
### `deferrable` | ||
@@ -171,3 +170,2 @@ | ||
## `neonConfig` configuration | ||
@@ -177,3 +175,3 @@ | ||
1. Import `neonConfig` from the package and set global default options on that. | ||
1. Import `neonConfig` from the package and set global default options on that. | ||
2. Set options on individual `Client` instances using their `neonConfig` property. | ||
@@ -197,3 +195,2 @@ | ||
#### `webSocketConstructor: typeof WebSocket | undefined` | ||
@@ -208,6 +205,5 @@ | ||
import ws from 'ws'; | ||
neonConfig.webSocketConstructor = ws; | ||
neonConfig.webSocketConstructor = ws; | ||
``` | ||
### Advanced configuration | ||
@@ -217,3 +213,2 @@ | ||
#### `poolQueryViaFetch: boolean` | ||
@@ -227,6 +222,5 @@ | ||
#### `fetchEndpoint: string | ((host: string, port: number | string) => string)` | ||
Set `fetchEndpoint` to set the server endpoint to be sent queries via http fetch. | ||
Set `fetchEndpoint` to set the server endpoint to be sent queries via http fetch. | ||
@@ -241,3 +235,2 @@ This may be useful for local development (e.g. to set a port that's not the default 443). | ||
#### `fetchFunction: any` | ||
@@ -251,3 +244,2 @@ | ||
#### `wsProxy: string | (host: string, port: number | string) => string` | ||
@@ -259,3 +251,4 @@ | ||
// either: | ||
neonConfig.wsProxy = (host, port) => `my-wsproxy.example.com/v1?address=${host}:${port}` | ||
neonConfig.wsProxy = (host, port) => | ||
`my-wsproxy.example.com/v1?address=${host}:${port}`; | ||
// or (with identical effect): | ||
@@ -267,6 +260,5 @@ neonConfig.wsProxy = 'my-wsproxy.example.com/v1'; | ||
#### `pipelineConnect: "password" | false` | ||
To speed up connection times, the driver will pipeline the first three messages to the database (startup, authentication and first query) if `pipelineConnect` is set to `"password"`. Note that this will only work if you've configured cleartext password authentication for the relevant user and database. | ||
To speed up connection times, the driver will pipeline the first three messages to the database (startup, authentication and first query) if `pipelineConnect` is set to `"password"`. Note that this will only work if you've configured cleartext password authentication for the relevant user and database. | ||
@@ -277,3 +269,2 @@ If your connection doesn't support password authentication, set `pipelineConnect` to `false` instead. | ||
#### `coalesceWrites: boolean` | ||
@@ -285,3 +276,2 @@ | ||
#### `forceDisablePgSSL: boolean` | ||
@@ -293,6 +283,5 @@ | ||
#### `useSecureWebSocket: boolean` | ||
This option switches between secure (the default) and insecure WebSockets. | ||
This option switches between secure (the default) and insecure WebSockets. | ||
@@ -305,3 +294,2 @@ To use experimental pure-JS encryption, set `useSecureWebSocket = false` and `forceDisablePgSSL = false`, and append `?sslmode=verify-full` to your database connection string. | ||
#### `subtls: any` | ||
@@ -319,3 +307,2 @@ | ||
#### `rootCerts: string /* PEM format */` | ||
@@ -329,3 +316,2 @@ | ||
#### `pipelineTLS: boolean` | ||
@@ -332,0 +318,0 @@ |
@@ -13,7 +13,6 @@ # Deploying a WebSocket proxy in front of your own Postgres instance | ||
2. Use experimental pure-JS Postgres connection encryption via [subtls](https://github.com/jawj/subtls). There's no need for nginx in this scenario, and the Postgres connection is encrypted end-to-end. You get this form of encryption if you set both `neonConfig.useSecureWebSocket` and `neonConfig.forceDisablePgSSL` to `false`, and append `?sslmode=verify-full` (or similar) to your connection string. TLS version 1.3 must be supported by the Postgres back-end. **Please note that subtls is experimental software and this configuration is not recommended for production use.** | ||
2. Use experimental pure-JS Postgres connection encryption via [subtls](https://github.com/jawj/subtls). There's no need for nginx in this scenario, and the Postgres connection is encrypted end-to-end. You get this form of encryption if you set both `neonConfig.useSecureWebSocket` and `neonConfig.forceDisablePgSSL` to `false`, and append `?sslmode=verify-full` (or similar) to your connection string. TLS version 1.3 must be supported by the Postgres back-end. **Please note that subtls is experimental software and this configuration is not recommended for production use.** | ||
Second, you'll need to set some [configuration options](CONFIG.md) on this package: at a minimum the `wsProxy` option and (if using experimental encryption) `subtls` and `rootCerts`. | ||
## Example shell commands | ||
@@ -92,3 +91,3 @@ | ||
echo " | ||
echo " | ||
server { | ||
@@ -105,3 +104,3 @@ listen 80; | ||
} | ||
" > /etc/nginx/sites-available/wsproxy | ||
" > /etc/nginx/sites-available/wsproxy | ||
@@ -136,2 +135,2 @@ ln -s /etc/nginx/sites-available/wsproxy /etc/nginx/sites-enabled/wsproxy | ||
service nginx restart | ||
``` | ||
``` |
// @neondatabase/serverless driver types, mimicking pg | ||
export { DatabaseError } from "pg-protocol"; | ||
export { | ||
ClientConfig, | ||
ConnectionConfig, | ||
Defaults, | ||
PoolConfig, | ||
QueryConfig, | ||
CustomTypesConfig, | ||
Submittable, | ||
QueryArrayConfig, | ||
FieldDef, | ||
QueryResultBase, | ||
QueryResultRow, | ||
QueryResult, | ||
QueryArrayResult, | ||
Notification, | ||
ResultBuilder, | ||
QueryParse, | ||
BindConfig, | ||
ExecuteConfig, | ||
MessageConfig, | ||
Connection, | ||
Query, | ||
Events, | ||
types, | ||
defaults, | ||
native, | ||
BindConfig, ClientConfig, Connection, ConnectionConfig, CustomTypesConfig, Defaults, defaults, Events, ExecuteConfig, FieldDef, MessageConfig, native, Notification, PoolConfig, Query, QueryArrayConfig, QueryArrayResult, QueryConfig, QueryParse, QueryResult, QueryResultBase, | ||
QueryResultRow, ResultBuilder, Submittable, types | ||
} from "pg"; | ||
export { DatabaseError } from "pg-protocol"; | ||
interface FetchEndpointOptions { | ||
jwtAuth?: boolean; | ||
} | ||
export interface NeonConfigGlobalOnly { | ||
@@ -46,3 +27,3 @@ /** | ||
*/ | ||
fetchEndpoint: string | ((host: string, port: number | string) => string); | ||
fetchEndpoint: string | ((host: string, port: number | string, options?: FetchEndpointOptions) => string); | ||
@@ -182,6 +163,6 @@ /** | ||
import { | ||
Client as PgClient, | ||
ClientBase as PgClientBase, | ||
Client as PgClient, | ||
Pool as PgPool, | ||
PoolClient as PgPoolClient, | ||
Pool as PgPool, | ||
} from "pg"; | ||
@@ -257,2 +238,9 @@ | ||
fetchOptions?: Record<string, any>; | ||
/** | ||
* JWT auth token to be passed as the Bearer token in the Authorization header | ||
* | ||
* Default: `undefined` | ||
*/ | ||
authToken?: string | (() => Promise<string> | string); | ||
} | ||
@@ -259,0 +247,0 @@ |
{ | ||
"name": "@neondatabase/serverless", | ||
"version": "0.9.5", | ||
"version": "0.10.0", | ||
"author": "Neon", | ||
@@ -5,0 +5,0 @@ "description": "node-postgres for serverless environments from neon.tech", |
@@ -5,10 +5,8 @@ # @neondatabase/serverless | ||
* **Low-latency**, thanks to [message pipelining](https://neon.tech/blog/quicker-serverless-postgres) and other optimizations | ||
* **Ideal for serverless/edge** deployment, using https and WebSockets in place of TCP | ||
* **A drop-in replacement** for [node-postgres](https://node-postgres.com/), aka [`pg`](https://www.npmjs.com/package/pg) (on which it's based) | ||
- **Low-latency**, thanks to [message pipelining](https://neon.tech/blog/quicker-serverless-postgres) and other optimizations | ||
- **Ideal for serverless/edge** deployment, using https and WebSockets in place of TCP | ||
- **A drop-in replacement** for [node-postgres](https://node-postgres.com/), aka [`pg`](https://www.npmjs.com/package/pg) (on which it's based) | ||
## Get started | ||
### Install it | ||
@@ -30,3 +28,2 @@ | ||
### Configure it | ||
@@ -40,3 +37,2 @@ | ||
### Use it | ||
@@ -56,3 +52,2 @@ | ||
### Deploy it | ||
@@ -78,3 +73,3 @@ | ||
// return the post as JSON | ||
return new Response(JSON.stringify(post), { | ||
return new Response(JSON.stringify(post), { | ||
headers: { 'content-type': 'application/json' } | ||
@@ -101,10 +96,8 @@ }); | ||
## Sessions, transactions, and node-postgres compatibility | ||
A query using the `neon` function, as shown above, is carried by an https [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) request. | ||
A query using the `neon` function, as shown above, is carried by an https [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) request. | ||
This should work — and work fast — from any modern JavaScript environment. But you can only send one query at a time this way: sessions and transactions are not supported. | ||
### `transaction()` | ||
@@ -129,3 +122,2 @@ | ||
### `Pool` and `Client` | ||
@@ -135,5 +127,5 @@ | ||
* **session or interactive transaction support**, and/or | ||
- **session or interactive transaction support**, and/or | ||
* **compatibility with node-postgres**, which supports query libraries like [Kysely](https://kysely.dev/) or [Zapatos](https://jawj.github.io/zapatos/). | ||
- **compatibility with node-postgres**, which supports query libraries like [Kysely](https://kysely.dev/) or [Zapatos](https://jawj.github.io/zapatos/). | ||
@@ -144,16 +136,14 @@ Queries using `Pool` and `Client` are carried by WebSockets. There are **two key things** to know about this: | ||
2. **In serverless environments** such as Vercel Edge Functions or Cloudflare Workers, WebSocket connections can't outlive a single request. | ||
That means `Pool` or `Client` objects must be connected, used and closed **within a single request handler**. Don't create them outside a request handler; don't create them in one handler and try to reuse them in another; and to avoid exhausting available connections, don't forget to close them. | ||
2. **In serverless environments** such as Vercel Edge Functions or Cloudflare Workers, WebSocket connections can't outlive a single request. | ||
That means `Pool` or `Client` objects must be connected, used and closed **within a single request handler**. Don't create them outside a request handler; don't create them in one handler and try to reuse them in another; and to avoid exhausting available connections, don't forget to close them. | ||
These points are demonstrated in the examples below. | ||
### API | ||
### API | ||
- **The full API guide** to `Pool` and `Client` can be found in the [node-postgres docs](https://node-postgres.com/). | ||
* **The full API guide** to `Pool` and `Client` can be found in the [node-postgres docs](https://node-postgres.com/). | ||
- There are a few [additional configuration options](CONFIG.md) that apply to `Pool` and `Client` here. | ||
* There are a few [additional configuration options](CONFIG.md) that apply to `Pool` and `Client` here. | ||
## Example: Node.js with `Pool.connect()` | ||
@@ -167,6 +157,6 @@ | ||
import ws from 'ws'; | ||
neonConfig.webSocketConstructor = ws; // <-- this is the key bit | ||
neonConfig.webSocketConstructor = ws; // <-- this is the key bit | ||
const pool = new Pool({ connectionString: process.env.DATABASE_URL }); | ||
pool.on('error', err => console.error(err)); // deal with e.g. re-connect | ||
pool.on('error', (err) => console.error(err)); // deal with e.g. re-connect | ||
// ... | ||
@@ -178,10 +168,15 @@ | ||
await client.query('BEGIN'); | ||
const { rows: [{ id: postId }] } = await client.query('INSERT INTO posts (title) VALUES ($1) RETURNING id', ['Welcome']); | ||
await client.query('INSERT INTO photos (post_id, url) VALUES ($1, $2)', [postId, 's3.bucket/photo/url']); | ||
const { | ||
rows: [{ id: postId }], | ||
} = await client.query('INSERT INTO posts (title) VALUES ($1) RETURNING id', [ | ||
'Welcome', | ||
]); | ||
await client.query('INSERT INTO photos (post_id, url) VALUES ($1, $2)', [ | ||
postId, | ||
's3.bucket/photo/url', | ||
]); | ||
await client.query('COMMIT'); | ||
} catch (err) { | ||
await client.query('ROLLBACK'); | ||
throw err; | ||
} finally { | ||
@@ -199,6 +194,5 @@ client.release(); | ||
import { WebSocket } from 'undici'; | ||
neonConfig.webSocketConstructor = WebSocket; | ||
neonConfig.webSocketConstructor = WebSocket; | ||
``` | ||
## Example: Vercel Edge Function with `Pool.query()` | ||
@@ -225,3 +219,3 @@ | ||
// end the `Pool` inside the same request handler | ||
// end the `Pool` inside the same request handler | ||
// (unlike `await`, `ctx.waitUntil` won't hold up the response) | ||
@@ -231,3 +225,3 @@ ctx.waitUntil(pool.end()); | ||
// return the post as JSON | ||
return new Response(JSON.stringify(post), { | ||
return new Response(JSON.stringify(post), { | ||
headers: { 'content-type': 'application/json' } | ||
@@ -245,3 +239,2 @@ }); | ||
## Example: Vercel Edge Function with `Client` | ||
@@ -269,3 +262,3 @@ | ||
// end the `Client` inside the same request handler | ||
// end the `Client` inside the same request handler | ||
// (unlike `await`, `ctx.waitUntil` won't hold up the response) | ||
@@ -275,3 +268,3 @@ ctx.waitUntil(client.end()); | ||
// return the post as JSON | ||
return new Response(JSON.stringify(post), { | ||
return new Response(JSON.stringify(post), { | ||
headers: { 'content-type': 'application/json' } | ||
@@ -291,9 +284,8 @@ }); | ||
* [Raw SQL + Vercel Edge Functions](https://github.com/neondatabase/neon-vercel-rawsql) | ||
* [Raw SQL via https + Vercel Edge Functions](https://github.com/neondatabase/neon-vercel-http) | ||
* [Raw SQL + Cloudflare Workers](https://github.com/neondatabase/serverless-cfworker-demo) | ||
* [Kysely + Vercel Edge Functions](https://github.com/neondatabase/neon-vercel-kysely) | ||
* [Zapatos + Vercel Edge Functions](https://github.com/neondatabase/neon-vercel-zapatos) | ||
- [Raw SQL + Vercel Edge Functions](https://github.com/neondatabase/neon-vercel-rawsql) | ||
- [Raw SQL via https + Vercel Edge Functions](https://github.com/neondatabase/neon-vercel-http) | ||
- [Raw SQL + Cloudflare Workers](https://github.com/neondatabase/serverless-cfworker-demo) | ||
- [Kysely + Vercel Edge Functions](https://github.com/neondatabase/neon-vercel-kysely) | ||
- [Zapatos + Vercel Edge Functions](https://github.com/neondatabase/neon-vercel-zapatos) | ||
## Bring your own Postgres database | ||
@@ -303,3 +295,2 @@ | ||
## Open-source | ||
@@ -309,5 +300,4 @@ | ||
## Feedback and support | ||
Please visit [Neon Community](https://community.neon.tech/) or [Support](https://neon.tech/docs/introduction/support). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
346924
3664
284
5