@prisma/ppg
Version:
Lightweight client for Prisma Postgres
673 lines (667 loc) • 25 kB
TypeScript
/**
* Constant for binary parameter format (PostgreSQL bytea type).
* Use with {@link byteArrayParameter} and {@link boundedByteStreamParameter}.
*/
declare const BINARY = "binary";
declare const /**
* Constant for text parameter format (PostgreSQL text type).
* Use with {@link byteArrayParameter} and {@link boundedByteStreamParameter}.
*/
TEXT = "text";
/**
* Format for binary parameters - either "binary" or "text".
*/
type ParameterFormat = typeof BINARY | typeof TEXT;
/**
* A ReadableStream parameter with a predetermined byte length.
* Used for streaming large binary or text data to PostgreSQL efficiently.
*/
interface BoundedByteStreamParameter extends ReadableStream<Uint8Array> {
/** Total number of bytes in the stream */
readonly byteLength: number;
/** Format: "binary" for bytea, "text" for text */
readonly format: ParameterFormat;
}
/**
* Creates a bounded byte stream parameter for efficient streaming of large binary/text data.
*
* @param readableStream - The stream of bytes to send
* @param format - BINARY for bytea, TEXT for text encoding
* @param byteLength - Total number of bytes in the stream (must be known upfront)
* @returns A parameter suitable for query/exec methods
*
* @example
* ```ts
* const stream = getFileStream();
* const param = boundedByteStreamParameter(stream, BINARY, 1024);
* await client.query("INSERT INTO files (data) VALUES ($1)", param);
* ```
*/
declare function boundedByteStreamParameter(readableStream: ReadableStream<Uint8Array>, format: ParameterFormat, byteLength: number): ReadableStream<Uint8Array<ArrayBufferLike>> & {
byteLength: number;
format: ParameterFormat;
};
/**
* A Uint8Array parameter with format specification.
* Used for sending binary or text data to PostgreSQL.
*/
interface ByteArrayParameter extends Uint8Array {
/** Format: "binary" for bytea, "text" for text encoding */
format: ParameterFormat;
}
/**
* Creates a byte array parameter from a Uint8Array.
*
* @param array - The bytes to send
* @param format - BINARY for bytea, TEXT for text encoding
* @returns A parameter suitable for query/exec methods
*
* @example
* ```ts
* const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
* const param = byteArrayParameter(bytes, BINARY);
* await client.query("INSERT INTO files (data) VALUES ($1)", param);
* ```
*/
declare function byteArrayParameter(array: Uint8Array, format: ParameterFormat): Uint8Array<ArrayBufferLike> & {
format: ParameterFormat;
};
/**
* Raw parameter types accepted by query and exec methods.
* Includes strings, null, and binary parameters.
*/
type RawParameter = string | null | ByteArrayParameter | BoundedByteStreamParameter;
/**
* Extended async iterator that can collect remaining elements into an array.
* Useful when you want to stream initially, then collect the rest, or just
* collect all the items at once.
*/
interface CollectableIterator<T> extends AsyncIterableIterator<T> {
/**
* Collects the remaining (not yet consumed from the iterator) elements into an array.
* Once called, further iteration will be empty.
*
* ```ts
* const result = await client.query("SELECT * FROM users");
* // Process first row manually
* const { value: firstRow } = await result.rows.next();
* // Collect the rest
* const remainingRows = await result.rows.collect();
* ```
*/
collect(): Promise<T[]>;
}
/**
* Base error class for all PPG client errors.
* All specific error types (ValidationError, DatabaseError, etc.) extend this class.
*/
declare class GenericError extends Error {
constructor(msg: string, opts?: ErrorOptions);
}
/**
* Error thrown when input validation fails (invalid parameters, configuration, etc.).
*/
declare class ValidationError extends GenericError {
constructor(msg: string, opts?: ErrorOptions);
}
interface HttpResponseErrorDetails {
readonly statusCode: number;
readonly message: string;
}
/**
* Error thrown when an HTTP request to the database fails.
* Contains the HTTP status code for debugging.
*/
declare class HttpResponseError extends GenericError {
/** HTTP status code from the failed request */
readonly status: number;
constructor({ message, statusCode }: HttpResponseErrorDetails, opts?: ErrorOptions);
}
interface WebSocketErrorDetails {
readonly closureCode?: number;
readonly closureReason?: string;
readonly message: string;
}
/**
* Error thrown when a WebSocket connection fails or closes unexpectedly.
* Contains the WebSocket closure code and reason for debugging.
*/
declare class WebSocketError extends GenericError {
/** WebSocket closure code (e.g., 1000 for normal closure) */
readonly closureCode?: number;
/** Human-readable closure reason */
readonly closureReason?: string;
constructor({ message, closureCode, closureReason }: WebSocketErrorDetails, opts?: ErrorOptions);
}
/**
* Details for database errors from PostgreSQL.
*/
interface DatabaseErrorDetails {
/** Error message from PostgreSQL */
readonly message: string;
/** PostgreSQL error code (e.g., "23505" for unique violation) */
readonly code: string;
/** Additional error fields from PostgreSQL */
readonly [key: string]: string;
}
/**
* Error thrown when PostgreSQL returns an error.
* Contains the PostgreSQL error code and additional details for debugging.
*
* @example
* ```ts
* try {
* await client.query("INSERT INTO users (email) VALUES ($1)", "duplicate@example.com");
* } catch (error) {
* if (error instanceof DatabaseError) {
* console.log(error.code); // "23505" (unique violation)
* console.log(error.details); // Additional PostgreSQL error fields
* }
* }
* ```
*/
declare class DatabaseError extends GenericError {
/** PostgreSQL error code (SQLSTATE) */
readonly code: string;
/** Additional error details from PostgreSQL (severity, hint, etc.) */
readonly details: Record<string, string>;
constructor(details: DatabaseErrorDetails, opts?: ErrorOptions);
}
/**
* Resultset column descriptor.
*/
interface Column {
/**
* Name of the column.
*/
name: string;
/**
* Object identifier of the column type.
*
* If you need to know the column type name, you can use the `oid` to query
* the `pg_type` catalog:
*
* ```ts
* await client.run({
* sql: `SELECT typname FROM pg_type WHERE oid = $1`,
* parameters: [column.oid],
* });
* ```
*/
oid: number;
}
/**
* Prisma Postgres low-level Serverless Client API Module.
*/
/**
* Base configuration shared between Client and Session configurations.
*/
interface BaseConfig {
/**
* Custom value parsers for specific PostgreSQL type OIDs.
* These override the default parsing behavior for their respective types.
*/
parsers?: ValueParser<unknown>[];
/**
* Custom value serializers for specific JavaScript types.
* These override the default serialization behavior.
*/
serializers?: ValueSerializer<unknown>[];
}
/**
* Configuration for the Prisma Postgres Serverless Client.
*/
interface ClientConfig extends BaseConfig {
/**
* Use the direct TCP connection string for your Prisma Postgres database.
*/
connectionString: string | URL;
}
/**
* Configuration for a database Session.
* Allows per-session customization of parsing and serialization behavior.
*/
interface SessionConfig extends BaseConfig {
}
/**
* A mixin interface allowing single query execution.
* Implemented by both Client and Session.
*/
interface Statements {
/**
* Executes a SQL query and returns the full result set with streaming rows.
* Use this for SELECT queries or commands where you need to access result data.
*
* @param sql - SQL query string with $1, $2, ... placeholders
* @param params - Query parameters to substitute into placeholders
* @returns Promise resolving to the result set (rowsAffected will be 0)
*
* ```ts
* const result = await client.query("SELECT * FROM users WHERE id = $1", userId);
* for await (const row of result.rows) {
* console.log(row.values);
* }
* ```
*/
query(sql: string, ...params: unknown[]): Promise<Resultset>;
/**
* Executes a SQL command (INSERT, UPDATE, DELETE) and returns the number of affected rows.
* This method is optimized for write operations where result data is not needed.
*
* @param sql - SQL command string with $1, $2, ... placeholders
* @param params - Command parameters to substitute into placeholders
* @returns Promise resolving to the number of rows affected
*
* ```ts
* const affected = await client.exec("DELETE FROM users WHERE id = $1", userId);
* console.log(`Deleted ${affected} user(s)`);
* ```
*/
exec(sql: string, ...params: unknown[]): Promise<number>;
}
/**
* Prisma Postgres Serverless Client which allows
* running individual raw queries or long running Sessions, which
* can be used to implement interactive transactions.
*/
interface Client extends Statements {
/**
* Creates a new Session to run multiple queries interactively.
* Sessions are useful for running transactions or maintaining state across queries.
* The Session does not start an explicit transaction automatically - you must
* issue BEGIN/COMMIT/ROLLBACK commands explicitly.
*
* @param config - Optional session configuration to customize parsing/serialization
* behavior for this specific session, overriding the client defaults.
* @returns A new Session instance
*
* ```ts
* using session = await client.newSession();
* await session.query("BEGIN");
* await session.query("INSERT INTO users VALUES ($1, $2)", "Alice", 25);
* await session.query("COMMIT");
* await session.close();
* ```
*/
newSession(config?: SessionConfig): Promise<Session>;
}
/**
* A Session represents a long-running stateful conversation with the
* database. Multiple queries can be run while the Session is active.
* The main use for Session is to run interactive transactions.
*/
interface Session extends Statements, Disposable {
/**
* Gracefully closes the current Session. Please notice
* This will not run any implicit transaction command: the database
* will automatically rollback any pending transaction when
* closing without commit.
*
* This is an alias for [Symbol.dispose]()
*/
close(): void;
/**
* If true, the Session can still accept new queries. If false,
* the Session should not be used anymore, and any query request
* will immediately produce an error.
*/
readonly active: boolean;
}
/**
* Represents a single query parameter value that can be passed to a SQL query.
*
* - `string`: Text data
* - `null`: SQL NULL value
* - `ByteArrayParameter`: Fixed size byte array, carrying text or binary data
* - `BoundedByteStreamParameter`: Streamed text or binary data, with a predetermined length
*/
type QueryParameter = RawParameter;
/**
* Query result set.
*/
interface Resultset {
/**
* Resultset column descriptors, corresponding to the number of query result fields.
*/
columns: Column[];
/**
* Resultset data rows async iterator. Allows streaming rows as they arrive
* from the database, which is memory-efficient for large result sets.
*
* ```ts
* const result = await client.query("SELECT * FROM users");
*
* // Stream rows one by one
* for await (const row of result.rows) {
* const [id, name, email] = row.values;
* // process values
* }
*
* // Or collect all rows at once
* const allRows = await result.rows.collect();
* ```
*/
rows: CollectableIterator<Row>;
}
/**
* Single data row in a result set.
*/
interface Row {
/**
* Array of values for this row. The number of elements in the array
* corresponds to the number of column descriptors in the result set.
*
* ```ts
* const [id, name, email] = row.values;
* ```
*/
values: unknown[];
}
/**
* Custom parser for PostgreSQL values of a specific type OID.
* A default set of parsers is provided for standard PostgreSQL types,
* but these can be overridden by providing custom parsers in the client configuration.
*/
interface ValueParser<T> {
/**
* PostgreSQL type OID that this parser handles.
*/
readonly oid: number;
/**
* Parses the raw string value from PostgreSQL into a JavaScript value.
*
* @param value - Raw string value from PostgreSQL, or null
* @returns Parsed JavaScript value
*/
parse(value: string | null): T;
}
/**
* Custom serializer for JavaScript values to PostgreSQL query parameters.
* Serializers take precedence over the default encoding rules, which use
* `toString()` for most types.
*
* Use serializers sparingly as the `supports()` check is invoked for every
* parameter value until a matching serializer is found. If no match is found,
* the default encoding rules apply.
*/
interface ValueSerializer<T> {
/**
* Type guard that checks if this serializer can handle the given value.
* This method is called for each parameter value until a match is found.
*
* @param value - The value to check
* @returns True if this serializer supports the value type
*/
supports(value: unknown): value is T;
/**
* Serializes the JavaScript value into a query parameter format.
*
* @param value - The value to serialize
* @returns Serialized query parameter
*/
serialize(value: T): QueryParameter;
}
declare function defaultClientConfig(connectionString: string | URL): ClientConfig;
/**
* Creates a new {@link Client}.
* @param config Client configuration ({@link ClientConfig}).
*/
declare function client(config: ClientConfig): Client;
/**
* Configuration for the PrismaPostgres client.
* Identical to {@link ClientConfig} - use {@link defaultClientConfig} for recommended defaults.
*/
interface PrismaPostgresConfig extends ClientConfig {
}
/**
* SQL template literal tag interface for query execution.
* Provides a convenient template syntax for building parameterized queries.
*/
interface SqlTemplateStatements {
/**
* Executes a SQL query using template literal syntax and returns a stream of typed rows.
*
* @param strings - Template literal strings
* @param values - Template literal interpolated values (used as query parameters)
* @returns Iterator of typed result rows
*
* ```ts
* const userId = 42;
* const rows = ppg.sql<User>`SELECT * FROM users WHERE id = ${userId}`;
*
* // Option 1: Stream rows one by one
* for await (const user of rows) {
* console.log(user);
* }
*
* // Option 2: Collect all rows at once
* const allUsers = await rows.collect();
* ```
*/
<R extends NonNullable<object> = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]): CollectableIterator<R>;
/**
* Executes a SQL command using template literal syntax and returns the number of affected rows.
*
* @param strings - Template literal strings
* @param values - Template literal interpolated values (used as command parameters)
* @returns Promise resolving to the number of rows affected
*
* ```ts
* const userId = 42;
* const affected = await ppg.sql.exec`DELETE FROM users WHERE id = ${userId}`;
* console.log(`Deleted ${affected} user(s)`);
* ```
*/
exec(strings: TemplateStringsArray, ...values: unknown[]): Promise<number>;
}
interface PrismaPostgresStatements {
/**
* SQL template literal tag for executing queries with type-safe results.
*
* ```ts
* const userId = 42;
* const rows = ppg.sql<User>`SELECT * FROM users WHERE id = ${userId}`;
*
* // Stream rows one by one
* for await (const user of rows) {
* console.log(user);
* }
*
* // Or collect all rows at once
* const allUsers = await rows.collect();
* ```
*/
sql: SqlTemplateStatements;
/**
* Executes a raw SQL query with parameterized placeholders and returns a stream of typed rows.
*
* @param sql - SQL query string with $1, $2, ... placeholders
* @param params - Query parameters to substitute into placeholders
* @returns Iterator of typed result rows
*
* ```ts
* const rows = ppg.query<User>("SELECT * FROM users WHERE id = $1", userId);
*
* // Stream rows one by one
* for await (const user of rows) {
* console.log(user);
* }
*
* // Or collect all rows at once
* const allUsers = await rows.collect();
* ```
*/
query<R extends NonNullable<object> = Record<string, unknown>>(sql: string, ...params: unknown[]): CollectableIterator<R>;
/**
* Executes a SQL command (INSERT, UPDATE, DELETE) and returns the number of affected rows.
*
* @param sql - SQL command string with $1, $2, ... placeholders
* @param params - Command parameters to substitute into placeholders
* @returns Promise resolving to the number of rows affected
*
* ```ts
* const affected = await ppg.exec("DELETE FROM users WHERE id = $1", userId);
* console.log(`Deleted ${affected} user(s)`);
* ```
*/
exec(sql: string, ...params: unknown[]): Promise<number>;
}
interface PrismaPostgresTransactions {
/**
* Executes an interactive transaction, providing the `Sql` interface in the
* given callback to run interactive queries/commands. The transaction BEGIN
* is performed automatically before the callback is invoked, COMMIT is performed
* automatically on return, ROLLBACK is performed automatically when throwing errors
*
* ```ts
* ppg.transaction(async (sql) => {
* // BEGIN is performed transparently
* const user = ['John', 'Doe'], userId = 13;
* await sql`INSERT INTO users VALUES (${userId}, ${user[0]}, ${user[1]})`;
*
* const subId = 55;
* await sql`UPDATE subscriptions SET user_count = user_count + 1 WHERE id = ${subId}`;
* // COMMIT is performed automatically on return
* // ROLLBACK is performed automatically when throwing errors
* })
*/
transaction<T = void>(callback: (statements: PrismaPostgresStatements) => Promise<T>): Promise<T>;
/**
* Starts building a batch transaction using a fluent API.
* Useful for dynamically constructing batches or when you prefer method chaining.
*
* @returns A BatchQueryBuilder to chain query/exec calls and execute the batch
*
* ```ts
* const [orders, affected, users] = await ppg.batch()
* .query<Order>("SELECT * FROM orders WHERE id = $1", orderId)
* .exec("UPDATE inventory SET stock = stock - $1 WHERE id = $2", qty, inventoryId)
* .query<User>("SELECT * FROM users WHERE id = $1", userId)
* .run();
* // orders: Order[], affected: number, users: User[]
* ```
*/
batch(): BatchQueryBuilder<[]>;
/**
* Executes a batch transaction with a fixed set of queries or commands. The transaction BEGIN
* is performed automatically, followed by all queries/commands in sequence, then COMMIT is
* performed automatically. ROLLBACK is performed automatically if any query fails.
*
* Use `{ query: "...", parameters: [...] }` for SELECT queries that return rows.
* Use `{ exec: "...", parameters: [...] }` for INSERT/UPDATE/DELETE commands that return affected row counts.
*
* The tuple type determines the return types:
* - Array types (e.g., `User[]`) expect a `query` statement
* - Number types expect an `exec` statement
*
* @param queries - Variable number of query or exec statements matching the tuple type
* @returns Promise resolving to a tuple of results matching the input types
*
* ```ts
* const [orders, affected, users] = await ppg.batch<[Order[], number, User[]]>(
* { query: "SELECT * FROM orders WHERE id = $1", parameters: [orderId] },
* { exec: "UPDATE inventory SET stock = stock - $1 WHERE id = $2", parameters: [qty, inventoryId] },
* { query: "SELECT * FROM users WHERE id = $1", parameters: [userId] }
* );
* // orders: Order[], affected: number, users: User[]
* ```
*/
batch<T extends BatchTuple>(...statements: StatementsBatch<T>): Promise<T>;
}
/**
* High-level Prisma Postgres client interface with convenient query methods.
*/
interface PrismaPostgres extends PrismaPostgresStatements, PrismaPostgresTransactions {
}
/**
* Type representing a tuple of batch result types.
* Each element can be an array (for query results) or a number (for exec results).
*/
type BatchTuple = readonly unknown[];
/**
* Fluent builder interface for constructing batch transactions dynamically.
* Allows chaining multiple query() and exec() calls, then executing the batch.
*/
interface BatchQueryBuilder<T extends BatchTuple> {
/**
* Adds a SELECT query to the batch that returns typed rows.
*
* @param sql - SQL query string with $1, $2, ... placeholders
* @param params - Query parameters
* @returns Builder with the new query appended to the result tuple
*/
query<R extends NonNullable<object> = Record<string, unknown>>(sql: string, ...params: unknown[]): BatchQueryBuilder<[...T, R[]]>;
/**
* Adds an INSERT/UPDATE/DELETE command to the batch that returns affected row count.
*
* @param sql - SQL command string with $1, $2, ... placeholders
* @param params - Command parameters
* @returns Builder with the affected row count appended to the result tuple
*/
exec(sql: string, ...params: unknown[]): BatchQueryBuilder<[...T, number]>;
/**
* Runs the batch transaction and returns the tuple of results.
*
* @returns Tuple of results matching the chained query/exec calls
*/
run(): Promise<T>;
}
/**
* Statement for a SELECT query that returns rows.
*/
interface QueryStatement {
/**
* SQL query string with $1, $2, ... placeholders
*/
query: string;
/**
* Query parameters to substitute into placeholders
*/
parameters?: unknown[];
}
/**
* Statement for an INSERT/UPDATE/DELETE command that returns affected row count.
*/
interface ExecStatement {
/**
* SQL command string with $1, $2, ... placeholders
*/
exec: string;
/**
* Command parameters to substitute into placeholders
*/
parameters?: unknown[];
}
/**
* Mapped type that enforces correct statement types based on the result tuple.
* - Number types in the tuple require ExecStatement
* - Array types in the tuple require QueryStatement
*/
type StatementsBatch<T extends BatchTuple = BatchTuple> = {
[K in keyof T]: T[K] extends number ? ExecStatement : T[K] extends Array<unknown> ? QueryStatement : never;
};
/**
* Creates a new high-level Prisma Postgres client with convenient query methods
* including SQL template literals and transaction support.
*
* @param config - Client configuration with connection string and optional parsers/serializers
* @returns PrismaPostgres client instance
*
* ```ts
* const ppg = prismaPostgres({ connectionString: "postgres://user:pass@host:5432/db" });
*
* // SQL template literals
* const users = await ppg.sql<User>`SELECT * FROM users WHERE id = ${userId}`.collect();
*
* // Raw queries
* const rows = await ppg.query<User>("SELECT * FROM users WHERE id = $1", userId).collect();
*
* // Transactions
* await ppg.transaction(async (sql) => {
* await sql`INSERT INTO users VALUES (${userId}, ${name})`;
* await sql`UPDATE subscriptions SET user_count = user_count + 1`;
* });
* ```
*/
declare function prismaPostgres(config: PrismaPostgresConfig): PrismaPostgres;
export { BINARY, type BatchQueryBuilder, type BatchTuple, type BoundedByteStreamParameter, type ByteArrayParameter, type Client, type ClientConfig, type CollectableIterator, type Column, DatabaseError, type DatabaseErrorDetails, type ExecStatement, GenericError, HttpResponseError, type ParameterFormat, type PrismaPostgres, type PrismaPostgresConfig, type PrismaPostgresStatements, type PrismaPostgresTransactions, type QueryParameter, type QueryStatement, type Resultset, type Row, type Session, type SessionConfig, type SqlTemplateStatements, type Statements, type StatementsBatch, TEXT, ValidationError, type ValueParser, type ValueSerializer, WebSocketError, boundedByteStreamParameter, byteArrayParameter, client, defaultClientConfig, prismaPostgres };