-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds
authToken
support to neon() (#97)
* Adds `authToken` support which can be used to send a JWT in the `Bearer` of the Authentication HTTP header * We don't have WebSockets support for this hence no changes to the Pool class * The sanity "tests" (`src/index.ts`) run, but have not been modified because they need to run against an instance of Neon that supports this (and that's not possible yet) We won't merge this PR until we can add some tests for it, but the code review can happen now. ## How to use this? ```typescript import { neon } from "@neondatabase/serverless"; import { auth, clerkClient } from "@clerk/nextjs/server"; const sql = neon(process.env.DATABASE_URL, { authToken: async () => clerkClient.sessions.getToken(auth(), "Neon"), }); ... ``` --------- Co-authored-by: Pedro Figueiredo <[email protected]>
- Loading branch information
1 parent
6a9a2d2
commit d90ad3b
Showing
14 changed files
with
14,953 additions
and
5,958 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,12 @@ | |
|
||
`@neondatabase/serverless` is [Neon](https://neon.tech)'s PostgreSQL driver for JavaScript and TypeScript. It's: | ||
|
||
* **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 | ||
|
||
Install it with your preferred JavaScript package manager. It's named `@neondatabase/serverless` on npm and `@neon/serverless` on JSR. So, for example: | ||
|
@@ -26,7 +24,6 @@ bunx jsr add @neon/serverless | |
|
||
Using TypeScript? No worries: types are included either way. | ||
|
||
|
||
### Configure it | ||
|
||
Get your connection string from the [Neon console](https://console.neon.tech/) and set it as an environment variable. Something like: | ||
|
@@ -35,7 +32,6 @@ Get your connection string from the [Neon console](https://console.neon.tech/) a | |
DATABASE_URL=postgres://username:[email protected]/neondb | ||
``` | ||
|
||
|
||
### Use it | ||
|
||
For one-shot queries, use the `neon` function. For instance: | ||
|
@@ -50,7 +46,6 @@ const [post] = await sql`SELECT * FROM posts WHERE id = ${postId}`; | |
|
||
Note: interpolating `${postId}` here is [safe from SQL injection](https://neon.tech/blog/sql-template-tags). | ||
|
||
|
||
### Deploy it | ||
|
||
Turn this example into a complete API endpoint deployed on [Vercel Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions) at `https://myapp.vercel.dev/api/post?postId=123` by following two simple steps: | ||
|
@@ -71,7 +66,7 @@ export default async (req: Request, ctx: any) => { | |
if (!post) return new Response('Not found', { status: 404 }); | ||
|
||
// return the post as JSON | ||
return new Response(JSON.stringify(post), { | ||
return new Response(JSON.stringify(post), { | ||
headers: { 'content-type': 'application/json' } | ||
}); | ||
} | ||
|
@@ -93,14 +88,12 @@ npx vercel deploy | |
|
||
The `neon` query function has a few [additional options](CONFIG.md). | ||
|
||
|
||
## 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()` | ||
|
||
Multiple queries can be issued via fetch request within a single, non-interactive transaction by using the `transaction()` function. This is exposed as a property on the query function. | ||
|
@@ -120,32 +113,29 @@ const [posts, tags] = await sql.transaction([ | |
|
||
There are some [additional options](CONFIG.md) when using `transaction()`. | ||
|
||
|
||
### `Pool` and `Client` | ||
|
||
Use the `Pool` or `Client` constructors, instead of the functions described above, when you need: | ||
|
||
* **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/). | ||
|
||
Queries using `Pool` and `Client` are carried by WebSockets. There are **two key things** to know about this: | ||
|
||
1. **In Node.js** and some other environments, there's no built-in WebSocket support. In these cases, supply a WebSocket constructor function. | ||
|
||
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. | ||
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. | ||
|
||
### API | ||
These points are demonstrated in the examples below. | ||
|
||
* **The full API guide** to `Pool` and `Client` can be found in the [node-postgres docs](https://node-postgres.com/). | ||
### API | ||
|
||
* There are a few [additional configuration options](CONFIG.md) that apply to `Pool` and `Client` here. | ||
- **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. | ||
|
||
## Example: Node.js with `Pool.connect()` | ||
|
||
|
@@ -155,24 +145,29 @@ In Node.js, it takes two lines to configure WebSocket support. For example: | |
import { Pool, neonConfig } from '@neondatabase/serverless'; | ||
|
||
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 | ||
// ... | ||
|
||
const client = await pool.connect(); | ||
|
||
try { | ||
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 { | ||
client.release(); | ||
} | ||
|
@@ -185,10 +180,9 @@ Other WebSocket libraries are available. For example, you could replace `ws` in | |
|
||
```typescript | ||
import { WebSocket } from 'undici'; | ||
neonConfig.webSocketConstructor = WebSocket; | ||
neonConfig.webSocketConstructor = WebSocket; | ||
``` | ||
|
||
|
||
## Example: Vercel Edge Function with `Pool.query()` | ||
|
||
We can rewrite the Vercel Edge Function shown above (under the heading 'Deploy it') to use `Pool`, as follows: | ||
|
@@ -210,12 +204,12 @@ export default async (req: Request, ctx: any) => { | |
const [post] = await pool.query('SELECT * FROM posts WHERE id = $1', [postId]); | ||
if (!post) return new Response('Not found', { status: 404 }); | ||
|
||
// 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) | ||
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' } | ||
}); | ||
} | ||
|
@@ -228,7 +222,6 @@ export const config = { | |
|
||
Note: we don't actually use the pooling capabilities of `Pool` in this example. But it's slightly briefer than using `Client` and, because `Pool.query` is designed for one-shot queries, we may in future automatically route these queries over https for lower latency. | ||
|
||
|
||
## Example: Vercel Edge Function with `Client` | ||
|
||
Using `Client` instead, the example looks like this: | ||
|
@@ -251,12 +244,12 @@ export default async (req: Request, ctx: any) => { | |
const [post] = await client.query('SELECT * FROM posts WHERE id = $1', [postId]); | ||
if (!post) return new Response('Not found', { status: 404 }); | ||
|
||
// 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) | ||
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' } | ||
}); | ||
} | ||
|
@@ -271,23 +264,20 @@ export const config = { | |
|
||
These repos show how to use `@neondatabase/serverless` with a variety of environments and tools: | ||
|
||
* [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 | ||
|
||
This package comes configured to connect to a Neon database. But you can also use it to connect to your own Postgres instances if you [run your own WebSocket proxy](DEPLOY.md). | ||
|
||
|
||
## Open-source | ||
|
||
This code is released under the [MIT license](LICENSE). | ||
|
||
|
||
## Feedback and support | ||
|
||
Please visit [Neon Community](https://community.neon.tech/) or [Support](https://neon.tech/docs/introduction/support). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.