All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

v0.2.0 — 2026-08-23

Added

  • db_prepared_statements (PostgREST db-prepared-statements, PGRST_DB_PREPARED_STATEMENTS, default true): the hot-path statements — the auth preamble, reads, mutations, and RPC — are cached as named prepared statements on each pool connection, skipping the parse step when a query shape repeats. Set it to false behind a transaction-mode pooler such as PgBouncer (#127).

Changed

  • Typed filter values and RPC scalar arguments are now bound as parameters (($n::text)::<type>) instead of being inlined as escaped literals ('<v>'::<type>). The server-side coercion — and every error it can raise — is identical (PostgreSQL I/O-conversion casts), the conformance suite is byte-for-byte unchanged, and it matches the SQL PostgREST executes ("id" = $1). This is what makes the statement cache effective: requests differing only in their values now share one SQL text (#127).

Fixed

  • PGRST205/PGRST202 not-found errors (and their "Perhaps you meant" hints) now qualify the missing table/function with the request's active schema, matching real PostgREST (Error.hs builds qi <> "." <> name from the resolved profile). Previously area-mirror schemas were reported as test.<name> — an assumption the conformance suite's oracle disproved. Conformance spec/ bumped to v16.0.0-suite.3, which pins the corrected behavior (cases 1360/1368/1373).

v0.1.0 — 2026-08-18

First release. Bier serves a RESTful API generated at boot from PostgreSQL introspection, reproducing the request/response behavior of PostgREST v16.0.

The API surface

  • Reads with the full PostgREST query grammar: select (columns, aliases, casts, JSON paths, computed columns, aggregates), horizontal filters and the operator set, logical trees (and/or, negation, nesting), quantifiers, ordering, limit/offset and Range pagination, and resource embedding (many-to-one, one-to-many, many-to-many, !inner, spread, aliases, disambiguation).
  • Mutations — POST insert, PATCH update, PUT single-row upsert, DELETE — with Prefer: return=, resolution=, missing=default, handling=strict and max-affected=.
  • /rpc/<function> calls over GET/HEAD/POST, rendering every routine return kind (scalar, composite, SETOF, TABLE(...), void).
  • Every request compiled into a single parameterized SQL statement whose response bodies are byte-identical to PostgREST's, row separators and embedded-JSON internals included.
  • Content negotiation across application/json, text/csv, application/geo+json (relations, mutations, RPC and embedded reads, wherever PostGIS is installed), the vnd.pgrst.object/array variants with nulls=stripped, and vnd.pgrst.plan.
  • Prefer: timezone=<tz> for per-request timestamptz rendering, including numeric UTC offsets.
  • A generated OpenAPI document at the root, with per-role privilege filtering, an opt-in OpenAPI 3.0.3 emitter (openapi_version: "3.0", a Bier extension), and db_root_spec to replace it wholesale.

Authentication

  • JWT verification through JOSE: HS256/384/512, plus RS/ES/PS/EdDSA from a JWK or JWK Set, with the algorithm family decided by the key's shape rather than the token's alg header.
  • Role switching and request-scoped GUCs (request.jwt.claims, headers, cookies, app.settings.*), applied as a single batched SELECT set_config(…) statement per request — one database round trip for the whole preamble, the same shape PostgREST executes.
  • jwt_role_claim_key as an RFC 9535 JSON Path into the claims, jwt_secret_is_base64, jwt_aud, and a per-instance verification cache.
  • db_pre_request, run inside the request transaction before the main query.

Operations

  • Multiple named instances per BEAM node, each with its own configuration, connection pool, runtime-built router, and Bandit server.
  • Schema-cache reload over LISTEN/NOTIFY and Bier.reload_schema_cache/1; a failed reload leaves the previous snapshot serving.
  • Standalone operation with no host application: PostgREST-compatible PGRST_* environment variables, a config-file parser, the in-database (ALTER ROLE … SET pgrst.*) configuration source, a bier escript with --dump-config/--example, a mix release target, and a Dockerfile.
  • Observability: :telemetry events for requests, schema-cache loads, pool status, JWT cache and SSE; an Apache-combined access log gated by log_level, with optional log_query; Server-Timing; a trace-header passthrough; and /live + /ready on an optional admin listener.
  • Query cancellation at the PostgreSQL backend when the HTTP client disconnects (cancel_on_disconnect, on by default) — something PostgREST cannot do.

Beyond PostgREST

  • Realtime events: a config-gated SSE endpoint bridging Postgres LISTEN/NOTIFY to browsers (events_channels, events_path, events_heartbeat_interval). PostgREST has no equivalent.
  • Vary: Origin on CORS responses that echo the request's Origin, which upstream omits.
  • RFC 4180 CSV quoting, where upstream emits malformed CSV for values containing quotes or newlines.
  • Server: bier/<version> and an OpenAPI info.version reporting Bier's own version. The PostgREST dialect is advertised through the document's externalDocs instead.

The README's "Deliberate divergences from PostgREST" section is the authoritative list; everything else is intended to match upstream byte for byte.