Neon serverless driver
Connect to Neon from serverless environments over HTTP or WebSockets
The Neon serverless driver is a low-latency Postgres driver for JavaScript and TypeScript that allows you to query data from serverless and edge environments over HTTP or WebSockets in place of TCP. The driver's low-latency capability is due to message pipelining and other optimizations.
When to query over HTTP vs WebSockets:
- HTTP: Querying over an HTTP fetch request is faster for single, non-interactive transactions, also referred to as "one-shot queries". Issuing multiple queries via a single, non-interactive transaction is also supported. See Use the driver over HTTP.
- WebSockets: If you require session or interactive transaction support or compatibility with node-postgres (the popular npm
pg
package), use WebSockets. See Use the driver over WebSockets.
note
The Neon serverless driver is currently in beta and subject to change in the future.
Install the Neon serverless driver
You can install the driver with your preferred JavaScript package manager. For example:
The driver includes TypeScript types (the equivalent of @types/pg
). No additional installation is required.
note
The Neon serverless driver is also available as a JavaScript Registry (JSR) package: https://jsr.io/@neon/serverless. The JavaScript Registry (JSR) is a package registry for JavaScript and TypeScript. JSR works with many runtimes (Node.js, Deno, browsers, and more) and is backward compatible with npm
.
Configure your Neon database connection
You can obtain a connection string for your database from the Connection Details widget on the Neon Dashboard. Your Neon connection string will look something like this:
The examples that follow assume that your database connection string is assigned to a DATABASE_URL
variable in your application's environment file.
Use the driver over HTTP
The Neon serverless driver uses the neon function for queries over HTTP.
You can use raw SQL queries or tools such as Drizzle-ORM, kysely, Zapatos, and others for type safety.
note
The maximum request size and response size for queries over HTTP is 10 MB.
neon function configuration options
The neon(...)
function returns a query function that can be used both as a tagged-template function and as an ordinary function:
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL);
// as a tagged-template function
const rowsA = await sql`SELECT * FROM posts WHERE id = ${postId}`;
// as an ordinary function (exactly equivalent)
const rowsB = await sql('SELECT * FROM posts WHERE id = $1', [postId]);
By default, the query function returned by neon(...)
returns only the rows resulting from the provided SQL query, and it returns them as an array of objects where the keys are column names. For example:
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL);
const rows = await sql`SELECT * FROM posts WHERE id = ${postId}`;
// -> [{ id: 12, title: "My post", ... }]
However, you can customize the return format of the query function using the configuration options fullResults
and arrayMode
. These options are available both on the neon(...)
function and on the query function it returns (but only when the query function is called as an ordinary function, not as a tagged-template function).
-
arrayMode: boolean
,false
by defaultThe default
arrayMode
value isfalse
. When it is true, rows are returned as an array of arrays instead of an array of objects:import { neon } from '@neondatabase/serverless'; const sql = neon(process.env.DATABASE_URL, { arrayMode: true }); const rows = await sql`SELECT * FROM posts WHERE id = ${postId}`; // -> [[12, "My post", ...]]
Or, with the same effect:
import { neon } from '@neondatabase/serverless'; const sql = neon(process.env.DATABASE_URL); const rows = await sql('SELECT * FROM posts WHERE id = $1', [postId], { arrayMode: true }); // -> [[12, "My post", ...]]
-
fullResults: boolean
The default
fullResults
value isfalse
. When it istrue
, additional metadata is returned alongside the result rows, which are then found in therows
property of the return value. The metadata matches what would be returned bynode-postgres
:import { neon } from '@neondatabase/serverless'; const sql = neon(process.env.DATABASE_URL, { fullResults: true }); const results = await sql`SELECT * FROM posts WHERE id = ${postId}`; /* -> { rows: [{ id: 12, title: "My post", ... }], fields: [ { name: "id", dataTypeID: 23, ... }, { name: "title", dataTypeID: 25, ... }, ... ], rowCount: 1, rowAsArray: false, command: "SELECT" } */
Or, with the same effect:
import { neon } from '@neondatabase/serverless'; const sql = neon(process.env.DATABASE_URL); const results = await sql('SELECT * FROM posts WHERE id = $1', [postId], { fullResults: true }); // -> { ... same as above ... }
-
fetchOptions: Record<string, any>
The
fetchOptions
option can also be passed to eitherneon(...)
or thequery
function. This option takes an object that is merged with the options to thefetch
call.For example, to increase the priority of every database
fetch
request:import { neon } from '@neondatabase/serverless'; const sql = neon(process.env.DATABASE_URL, { fetchOptions: { priority: 'high' } }); const rows = await sql`SELECT * FROM posts WHERE id = ${postId}`;
Or to implement a
fetch
timeout:import { neon } from '@neondatabase/serverless'; const sql = neon(process.env.DATABASE_URL); const abortController = new AbortController(); 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 clearTimeout(timeout);
For additional details, see Options and configuration.
Issue multiple queries with the transaction() function
The transaction(queriesOrFn, options)
function is exposed as a property on the query function. It allows multiple queries to be executed within a single, non-interactive transaction.
The first argument to transaction(), queriesOrFn
, is either an array of queries or a non-async function that receives a query function as its argument and returns an array of queries.
The array-of-queries case looks like this:
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`],
{
isolationLevel: 'RepeatableRead',
readOnly: true,
}
);
Or as an example of the function case:
const [authors, tags] = await neon(process.env.DATABASE_URL).transaction((txn) => [
txn`SELECT * FROM authors`,
txn`SELECT * FROM tags`,
]);
The optional second argument to transaction()
, options
, has the same keys as the options to the ordinary query function -- arrayMode
, fullResults
and fetchOptions
— plus three additional keys that concern the transaction configuration. These transaction-related keys are: isolationMode
, readOnly
and deferrable
.
Note that options cannot be supplied for individual queries within a transaction. Query and transaction options must instead be passed as the second argument of the transaction()
function. For example, this arrayMode
setting is ineffective (and TypeScript won't compile it): await sql.transaction([sql('SELECT now()', [], { arrayMode: true })])
. Instead, use await sql.transaction([sql('SELECT now()')], { arrayMode: true })
.
-
isolationMode
This option selects a Postgres transaction isolation mode. If present, it must be one of
ReadUncommitted
,ReadCommitted
,RepeatableRead
, orSerializable
. -
readOnly
If
true
, this option ensures that aREAD ONLY
transaction is used to execute the queries passed. This is a boolean option. The default value isfalse
. -
deferrable
If
true
(and ifreadOnly
is alsotrue
, andisolationMode
isSerializable
), this option ensures that aDEFERRABLE
transaction is used to execute the queries passed. This is a boolean option. The default value isfalse
.
For additional details, see transaction(...) function.
Advanced configuration options
For advanced configuration options, see neonConfig configuration, in the Neon serverless driver GitHub readme.
Use the driver over WebSockets
The Neon serverless driver supports the Pool and Client constructors for querying over WebSockets.
The Pool
and Client
constructors, provide session and transaction support, as well as node-postgres
compatibility. You can find the API guide for the Pool
and Client
constructors in the node-postgres documentation.
Consider using the driver with Pool
or Client
in the following scenarios:
- You already use
node-postgres
in your code base and would like to migrate to using@neondatabase/serverless
. - You are writing a new code base and want to use a package that expects a
node-postgres-compatible
driver. - Your backend service uses sessions / interactive transactions with multiple queries per connection.
You can use the Neon serverless driver in the same way you would use node-postgres
with Pool
and Client
. Where you usually import pg
, import @neondatabase/serverless
instead.
import { Pool } from '@neondatabase/serverless';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const posts = await pool.query('SELECT * FROM posts WHERE id =$1', [postId]);
pool.end();
Pool and Client usage notes
-
In Node.js and some other environments, there's no built-in WebSocket support. In these cases, supply a WebSocket constructor function.
import { Pool, neonConfig } from '@neondatabase/serverless'; import ws from 'ws'; neonConfig.webSocketConstructor = ws;
-
In serverless environments such as Vercel Edge Functions or Cloudflare Workers, WebSocket connections can't outlive a single request. That means
Pool
orClient
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.
For examples that demonstrate these points, see Pool and Client.
Advanced configuration options
For advanced configuration options, see neonConfig configuration, in the Neon serverless driver GitHub readme.
Example applications
Explore the example applications that use the Neon serverless driver.
UNESCO World Heritage sites app
Neon provides an example application to help you get started with the Neon serverless driver. The application generates a JSON
listing of the 10 nearest UNESCO World Heritage sites using IP geolocation (data copyright © 1992 – 2022 UNESCO/World Heritage Centre).
There are different implementations of the application to choose from.
Raw SQL + Vercel Edge Functions
Demonstrates using raw SQL with Neon's serverless driver on Vercel Edge Functions
Raw SQL via https + Vercel Edge Functions
Demonstrates Neon's serverless driver over HTTP on Vercel Edge Functions
Raw SQL + Cloudflare Workers
Demonstrates using the Neon serverless driver on Cloudflare Workers and employs caching for high performance.
Kysely + Vercel Edge Functions
Demonstrates using kysely and kysely-codegen with Neon's serverless driver on Vercel Edge Functions
Zapatos + Vercel Edge Functions
Demonstrates using Zapatos with Neon's serverless driver on Vercel Edge Functions
Demonstrates using Cloudflare's Hyperdrive to access your Neon database from Cloudflare Workers
Neon + Cloudflare Hyperdrive (Beta)
Ping Thing
The Ping Thing application pings a Neon Serverless Postgres database using a Vercel Edge Function and shows the journey your request makes. You can read more about this application in the accompanying blog post: How to use Postgres at the Edge
Neon serverless driver GitHub repository and changelog
The GitHub repository and changelog for the Neon serverless driver are found here.
References
- Fetch API
- node-postgres
- Drizzle-ORM
- Schema migration with Neon Postgres and Drizzle ORM
- kysely
- Zapatos
- Vercel Edge Functions
- Cloudflare Workers
- Use Neon with Cloudflare Workers
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. Users on paid plans can open a support ticket from the console. For more detail, see Getting Support.
Last updated on