@adrdp/pg-rest 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +52 -52
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -7,26 +7,26 @@ A [PostgREST](https://postgrest.org) (v16 behaviour) reimplementation as a **lib
7
7
  of its own. It is tested on Bun and Node.js 22 and should run anywhere your Postgres driver does.
8
8
  - **Bring your own driver.** You create the driver and pass it in through a small adapter. Adapters for
9
9
  Bun.sql, node-postgres, postgres.js and PGlite ship as separate entry points (`pg-rest/adapters/*`); the drivers are
10
- *optional peer dependencies*, so you only install the one you use.
10
+ _optional peer dependencies_, so you only install the one you use.
11
11
  - **No JWT and no server.** You decide who the caller is (cookie, API key, session, mTLS…) and hand pg-rest a role, claims and
12
12
  settings; they are applied per request with `set_config`, so Postgres Row Level Security sees them exactly as it would behind PostgREST.
13
13
 
14
14
  ## Quick start
15
15
 
16
16
  ```sh
17
- npm i pg-rest pg # or: bun add pg-rest (Bun.sql is built in), postgres, @electric-sql/pglite
17
+ npm i @adrdp/pg-rest pg # or: bun add @adrdp/pg-rest (Bun.sql is built in), postgres, @electric-sql/pglite
18
18
  ```
19
19
 
20
20
  ```ts
21
21
  import pg from "pg";
22
- import { createPgRest } from "pg-rest";
23
- import { nodePostgresAdapter } from "pg-rest/adapters/node-postgres";
22
+ import { createPgRest } from "@adrdp/pg-rest";
23
+ import { nodePostgresAdapter } from "@adrdp/pg-rest/adapters/node-postgres";
24
24
 
25
25
  const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
26
26
 
27
27
  const pgRest = await createPgRest({
28
- db: nodePostgresAdapter(pool), // you create and close the driver
29
- schemas: ["api"], // first one is the default schema
28
+ db: nodePostgresAdapter(pool), // you create and close the driver
29
+ schemas: ["api"], // first one is the default schema
30
30
  anonymousRole: "anon",
31
31
  resolveContext: async (req) => {
32
32
  const user = await mySessionLookup(req);
@@ -44,13 +44,13 @@ const pgRest = await createPgRest({
44
44
 
45
45
  Self-contained projects in [`examples/`](examples) (a todo API with Row Level Security; each has its own README, schema and `server.ts`):
46
46
 
47
- | example | runtime | driver | needs a database server |
48
- | --- | --- | --- | --- |
49
- | [`with-bun`](examples/with-bun) | Bun | `Bun.sql` (built in) | yes (docker-compose included) |
50
- | [`with-node-postgres`](examples/with-node-postgres) | Node.js ≥ 22.18 | `pg` | yes (docker-compose included) |
51
- | [`with-postgres-js`](examples/with-postgres-js) | Node.js ≥ 22.18 | `postgres` | yes (docker-compose included) |
52
- | [`with-pglite`](examples/with-pglite) | Node.js ≥ 22.18 | `@electric-sql/pglite` | no (Postgres in WebAssembly) |
53
- | [`with-better-auth`](examples/with-better-auth) | Node.js ≥ 22.18 | `pg` + [Better Auth](https://better-auth.com) sessions | yes (docker-compose included) |
47
+ | example | runtime | driver | needs a database server |
48
+ | --------------------------------------------------- | --------------- | ------------------------------------------------------ | ----------------------------- |
49
+ | [`with-bun`](examples/with-bun) | Bun | `Bun.sql` (built in) | yes (docker-compose included) |
50
+ | [`with-node-postgres`](examples/with-node-postgres) | Node.js ≥ 22.18 | `pg` | yes (docker-compose included) |
51
+ | [`with-postgres-js`](examples/with-postgres-js) | Node.js ≥ 22.18 | `postgres` | yes (docker-compose included) |
52
+ | [`with-pglite`](examples/with-pglite) | Node.js ≥ 22.18 | `@electric-sql/pglite` | no (Postgres in WebAssembly) |
53
+ | [`with-better-auth`](examples/with-better-auth) | Node.js ≥ 22.18 | `pg` + [Better Auth](https://better-auth.com) sessions | yes (docker-compose included) |
54
54
 
55
55
  ## Database adapters
56
56
 
@@ -64,12 +64,12 @@ interface DbAdapter {
64
64
  }
65
65
  ```
66
66
 
67
- | import | wraps | peer dependency |
68
- | --- | --- | --- |
69
- | `pg-rest/adapters/bun` → `bunSqlAdapter(sql)` | `new SQL(url)` from `"bun"` | none (built into Bun) |
70
- | `pg-rest/adapters/node-postgres` → `nodePostgresAdapter(pool)` | `new pg.Pool(...)` | `pg` ≥ 8 |
71
- | `pg-rest/adapters/postgres-js` → `postgresJsAdapter(sql)` | `postgres(url)` | `postgres` ≥ 3 |
72
- | `pg-rest/adapters/pglite` → `pgliteAdapter(db)` | `new PGlite()` | `@electric-sql/pglite` |
67
+ | import | wraps | peer dependency |
68
+ | --------------------------------------------------------------------- | --------------------------- | ---------------------- |
69
+ | `@adrdp/pg-rest/adapters/bun` → `bunSqlAdapter(sql)` | `new SQL(url)` from `"bun"` | none (built into Bun) |
70
+ | `@adrdp/pg-rest/adapters/node-postgres` → `nodePostgresAdapter(pool)` | `new pg.Pool(...)` | `pg` ≥ 8 |
71
+ | `@adrdp/pg-rest/adapters/postgres-js` → `postgresJsAdapter(sql)` | `postgres(url)` | `postgres` ≥ 3 |
72
+ | `@adrdp/pg-rest/adapters/pglite` → `pgliteAdapter(db)` | `new PGlite()` | `@electric-sql/pglite` |
73
73
 
74
74
  The adapters are structural (they never import the driver; you pass the client in), so a project only loads the code it imports.
75
75
  To support another driver, implement the interface above (about 20 lines) — the adapter sources in `src/adapters` are good templates.
@@ -94,32 +94,32 @@ when run on Node (it also uses more memory); the benchmark includes `bun build -
94
94
 
95
95
  Numbers from one run on a small 2-vCPU sandbox (indicative only; see the benchmark README). Throughput is relative to PostgREST 16.
96
96
 
97
- | | Bun (`bun run`) | Bun, compiled (`bun compile`, experimental) | Node + `pg` |
98
- | --- | --- | --- | --- |
99
- | Throughput | 1.2–1.5× on most reads, ~2× on writes | same as plain Bun (within noise) | 0.7–0.9× on the cheapest queries, ahead on heavier ones |
100
- | Peak memory | ~80 MB | ~50 MB | ~125 MB |
101
- | Setup | nothing extra | single executable; needs a recent (canary) Bun | Node (tested on 22) |
102
- | Pick it when | you can run Bun (default choice) | memory or deployment size matters and you accept an experimental feature | Bun is not an option |
97
+ | | Bun (`bun run`) | Bun, compiled (`bun compile`, experimental) | Node + `pg` |
98
+ | ------------ | ------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------- |
99
+ | Throughput | 1.2–1.5× on most reads, ~2× on writes | same as plain Bun (within noise) | 0.7–0.9× on the cheapest queries, ahead on heavier ones |
100
+ | Peak memory | ~80 MB | ~50 MB | ~125 MB |
101
+ | Setup | nothing extra | single executable; needs a recent (canary) Bun | Node (tested on 22) |
102
+ | Pick it when | you can run Bun (default choice) | memory or deployment size matters and you accept an experimental feature | Bun is not an option |
103
103
 
104
104
  `bun build --compile` and `--bytecode` performed like plain Bun, so they are only worth it if you want a single binary. PostgREST itself uses the least memory (about 40–47 MB).
105
105
 
106
106
  ## Options
107
107
 
108
- | option | meaning |
109
- | --- | --- |
110
- | `db` | a `DbAdapter` built from your driver (see [Database adapters](#database-adapters)) |
111
- | `schemas` | exposed schemas; selected with `Accept-Profile` / `Content-Profile` |
112
- | `anonymousRole` | role when no context is resolved (`PGRST302` if unset and none given); `false` refuses anonymous requests |
113
- | `planEnabled` | allow `Accept: application/vnd.pgrst.plan` (EXPLAIN output); off by default, like `db-plan-enabled` |
114
- | `resolveContext(req)` | return `{ role, claims, settings }`; throw `PgRestError` to reject (e.g. 401) |
115
- | `handle(req, ctx?)` | an explicit context overrides `resolveContext` |
116
- | `maxRows` | cap on rows returned |
117
- | `basePath` | URL prefix to strip |
118
- | `extraSearchPath` | extra schemas appended to `search_path` |
119
- | `requestSettings` | set `request.method/path/headers/cookies` GUCs (default on) |
120
- | `preRequest` | SQL function name or `(session, info) => void`, run inside the request transaction |
121
- | `allowTxOverride` | honour `Prefer: tx=rollback` |
122
- | `openApi` | serve the Swagger 2.0 document at `/` |
108
+ | option | meaning |
109
+ | --------------------- | --------------------------------------------------------------------------------------------------------- |
110
+ | `db` | a `DbAdapter` built from your driver (see [Database adapters](#database-adapters)) |
111
+ | `schemas` | exposed schemas; selected with `Accept-Profile` / `Content-Profile` |
112
+ | `anonymousRole` | role when no context is resolved (`PGRST302` if unset and none given); `false` refuses anonymous requests |
113
+ | `planEnabled` | allow `Accept: application/vnd.pgrst.plan` (EXPLAIN output); off by default, like `db-plan-enabled` |
114
+ | `resolveContext(req)` | return `{ role, claims, settings }`; throw `PgRestError` to reject (e.g. 401) |
115
+ | `handle(req, ctx?)` | an explicit context overrides `resolveContext` |
116
+ | `maxRows` | cap on rows returned |
117
+ | `basePath` | URL prefix to strip |
118
+ | `extraSearchPath` | extra schemas appended to `search_path` |
119
+ | `requestSettings` | set `request.method/path/headers/cookies` GUCs (default on) |
120
+ | `preRequest` | SQL function name or `(session, info) => void`, run inside the request transaction |
121
+ | `allowTxOverride` | honour `Prefer: tx=rollback` |
122
+ | `openApi` | serve the Swagger 2.0 document at `/` |
123
123
 
124
124
  Also returned: `reloadSchema()` (call after migrations), `schemaCache()`, `close()`.
125
125
 
@@ -140,18 +140,18 @@ Two different things are listed here. Don't confuse them.
140
140
  ### By design: will not be implemented
141
141
 
142
142
  pg-rest is a library, not a server. Anything whose job is to run the process, authenticate the caller, or configure PostgREST from outside the
143
- code is handled by *your* application instead. These are not gaps: adding them would work against the goals of the project.
144
-
145
- | PostgREST feature | Why pg-rest doesn't do it | What to do instead |
146
- | --- | --- | --- |
147
- | JWT validation, JWKS, `jwt-secret`, audience, role claim path | The point of the project is that **you** decide who the caller is, by any means: a session cookie, an API key, Better Auth, mTLS. Baking JWT in would force one scheme and add crypto and key-rotation code (and dependencies) to the library. | `resolveContext(req)` or `handle(req, ctx)` returns `{ role, claims, settings }`. pg-rest applies it with `set_config`, so RLS and `current_setting('request.jwt.claims')` work as usual. See `examples/with-better-auth`. |
148
- | Standalone server, CLI, host/port, unix sockets, TLS, `log-level`, `server-timing` | `handle(Request) => Response` is web-standard and runs under `Bun.serve`, Hono (`@hono/node-server`), or any framework that gives you a `Request`... Owning the server would tie the library to one runtime and duplicate what your framework already does. | Mount `handle` in your own server. |
149
- | Admin server (`/live`, `/ready`, `/metrics`) | Health checks and metrics belong to the host process. | Expose them in your app. |
150
- | CORS headers | Policy depends on your deployment. | Set them in your server or middleware. |
151
- | Config file, environment variables, `PGRST_*` settings | Configuration is the `createPgRest(options)` call, which is typed and lives with the code. | Pass options; read env vars yourself. |
152
- | Connection pool and `db-uri` | The pool belongs to your driver, and the driver is your choice (`DbAdapter`). pg-rest never opens connections. | Create the pool, wrap it in an adapter. |
153
- | In-database configuration (`db-pre-config`, per-role `ALTER ROLE ... SET pgrst.*`) | Settings are options in code, not rows in the database, so they are versioned and visible with the application. | Use the options; `preRequest` can still call a SQL function if you want database-side logic. |
154
- | Schema cache refresh over SQL: `NOTIFY pgrst, 'reload schema'` / `db-channel`, `SIGUSR1`/`SIGUSR2`, DDL event triggers | They require a dedicated `LISTEN` connection and signal handling, which a library must not hold open or assume (serverless, PGlite and pooled connections can't `LISTEN`). The introspection itself (`pg_catalog`) is implemented. | Call `reloadSchema()` after migrations, or wire it to your own `LISTEN`. A failed reload keeps the old cache. |
143
+ code is handled by _your_ application instead. These are not gaps: adding them would work against the goals of the project.
144
+
145
+ | PostgREST feature | Why pg-rest doesn't do it | What to do instead |
146
+ | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
147
+ | JWT validation, JWKS, `jwt-secret`, audience, role claim path | The point of the project is that **you** decide who the caller is, by any means: a session cookie, an API key, Better Auth, mTLS. Baking JWT in would force one scheme and add crypto and key-rotation code (and dependencies) to the library. | `resolveContext(req)` or `handle(req, ctx)` returns `{ role, claims, settings }`. pg-rest applies it with `set_config`, so RLS and `current_setting('request.jwt.claims')` work as usual. See `examples/with-better-auth`. |
148
+ | Standalone server, CLI, host/port, unix sockets, TLS, `log-level`, `server-timing` | `handle(Request) => Response` is web-standard and runs under `Bun.serve`, Hono (`@hono/node-server`), or any framework that gives you a `Request`... Owning the server would tie the library to one runtime and duplicate what your framework already does. | Mount `handle` in your own server. |
149
+ | Admin server (`/live`, `/ready`, `/metrics`) | Health checks and metrics belong to the host process. | Expose them in your app. |
150
+ | CORS headers | Policy depends on your deployment. | Set them in your server or middleware. |
151
+ | Config file, environment variables, `PGRST_*` settings | Configuration is the `createPgRest(options)` call, which is typed and lives with the code. | Pass options; read env vars yourself. |
152
+ | Connection pool and `db-uri` | The pool belongs to your driver, and the driver is your choice (`DbAdapter`). pg-rest never opens connections. | Create the pool, wrap it in an adapter. |
153
+ | In-database configuration (`db-pre-config`, per-role `ALTER ROLE ... SET pgrst.*`) | Settings are options in code, not rows in the database, so they are versioned and visible with the application. | Use the options; `preRequest` can still call a SQL function if you want database-side logic. |
154
+ | Schema cache refresh over SQL: `NOTIFY pgrst, 'reload schema'` / `db-channel`, `SIGUSR1`/`SIGUSR2`, DDL event triggers | They require a dedicated `LISTEN` connection and signal handling, which a library must not hold open or assume (serverless, PGlite and pooled connections can't `LISTEN`). The introspection itself (`pg_catalog`) is implemented. | Call `reloadSchema()` after migrations, or wire it to your own `LISTEN`. A failed reload keeps the old cache. |
155
155
 
156
156
  ## Security notes
157
157
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adrdp/pg-rest",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "A PostgREST-compatible REST layer for PostgreSQL, consumed as a library (Bun, Node.js and other runtimes with web-standard Request/Response). You decide who the user is (RLS) — no JWT required.",
5
5
  "license": "MIT",
6
6
  "author": "Alexandre Dos Reis",