@neondatabase/serverless
@neondatabase/serverless
is Neon's PostgreSQL driver for JavaScript and TypeScript. It's:
- Low-latency, thanks to message pipelining and other optimizations
- Ideal for serverless/edge deployment, using https and WebSockets in place of TCP
- A drop-in replacement for node-postgres, aka
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:
npm install @neondatabase/serverless
or
bunx jsr add @neon/serverless
Using TypeScript? No worries: types are included either way.
Configure it
Get your connection string from the Neon console and set it as an environment variable. Something like:
DATABASE_URL=postgres://username:password@host.neon.tech/neondb
Use it
For one-shot queries, use the neon
function. For instance:
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL);
const [post] = await sql`SELECT * FROM posts WHERE id = ${postId}`;
Note: interpolating ${postId}
here is safe from SQL injection.
Deploy it
Turn this example into a complete API endpoint deployed on Vercel Edge Functions at https://myapp.vercel.dev/api/post?postId=123
by following two simple steps:
- Create a new file
api/post.ts
:
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL);
export default async (req: Request, ctx: any) => {
const postId = parseInt(new URL(req.url).searchParams.get('postId'), 10);
if (isNaN(postId)) return new Response('Bad request', { status: 400 });
const [post] = await sql`SELECT * FROM posts WHERE id = ${postId}`;
if (!post) return new Response('Not found', { status: 404 });
return new Response(JSON.stringify(post), {
headers: { 'content-type': 'application/json' }
});
}
export const config = {
runtime: 'edge',
regions: ['iad1'],
};
- Test and deploy
npm install -g vercel
npx vercel env add DATABASE_URL
npx vercel dev
npx vercel deploy
The neon
query function has a few additional options.
Sessions, transactions, and node-postgres compatibility
A query using the neon
function, as shown above, is carried by an https fetch 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.
For example:
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL);
const showLatestN = 10;
const [posts, tags] = await sql.transaction([
sql`SELECT * FROM posts ORDER BY posted_at DESC LIMIT ${showLatestN}`,
sql`SELECT * FROM tags`,
]);
There are some additional options 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
-
compatibility with node-postgres, which supports query libraries like Kysely or Zapatos.
Queries using Pool
and Client
are carried by WebSockets. There are two key things to know about this:
-
In Node.js and some other environments, there's no built-in WebSocket support. In these cases, supply a WebSocket constructor function.
-
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
Example: Node.js with Pool.connect()
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;
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
pool.on('error', (err) => console.error(err));
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',
]);
await client.query('COMMIT');
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release();
}
await pool.end();
Other WebSocket libraries are available. For example, you could replace ws
in the above example with undici
:
import { WebSocket } from 'undici';
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:
import { Pool } from '@neondatabase/serverless';
export default async (req: Request, ctx: any) => {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const postId = parseInt(new URL(req.url).searchParams.get('postId'), 10);
if (isNaN(postId)) return new Response('Bad request', { status: 400 });
const [post] = await pool.query('SELECT * FROM posts WHERE id = $1', [postId]);
if (!post) return new Response('Not found', { status: 404 });
ctx.waitUntil(pool.end());
return new Response(JSON.stringify(post), {
headers: { 'content-type': 'application/json' }
});
}
export const config = {
runtime: 'edge',
regions: ['iad1'],
};
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:
import { Client } from '@neondatabase/serverless';
export default async (req: Request, ctx: any) => {
const client = new Client(process.env.DATABASE_URL);
await client.connect();
const postId = parseInt(new URL(req.url).searchParams.get('postId'), 10);
if (isNaN(postId)) return new Response('Bad request', { status: 400 });
const [post] = await client.query('SELECT * FROM posts WHERE id = $1', [postId]);
if (!post) return new Response('Not found', { status: 404 });
ctx.waitUntil(client.end());
return new Response(JSON.stringify(post), {
headers: { 'content-type': 'application/json' }
});
}
export const config = {
runtime: 'edge',
regions: ['iad1'],
};
More examples
These repos show how to use @neondatabase/serverless
with a variety of environments and tools:
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.
Open-source
This code is released under the MIT license.
Feedback and support
Please visit Neon Community or Support.