Repository navigation
Releases: mariuz/firebirdrest
Release list
v0.6.0
Binaries for Linux, macOS, Windows and FreeBSD, and .deb and .rpm packages with a systemd
service, are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.6.0. See CHANGELOG.md for details.
Added
- OpenTelemetry traces over OTLP/HTTP:
otel-traces-endpoint(or the standardOTEL_EXPORTER_OTLP_*
variables) exports a span per request with children for the JWT check, the transaction and
each SQL statement; W3Ctraceparentjoins the caller's trace and decides sampling,
otel-traces-sample-ratiosamples the rest. No new dependencies: the exporter is written for
FirebirdREST, like the metrics. Checked against Jaeger
(traces). - Built-in TLS:
server-tls-certandserver-tls-keyserve HTTPS (TLS 1.2+, HTTP/2).SIGHUP
reloads the certificate without dropping connections, keeping the current one if the new
files don't load, and reloads the schema cache;-checkvalidates the files and prints the
expiry, and a certificate expiring within 14 days is logged
(TLS). .deband.rpmpackages for amd64 and arm64 with every release: the binary, a hardened
systemd service running as afirebirdrestuser with a configuration check before start,
and/etc/firebirdrest/firebirdrest.confkept on upgrade
(packages).- A tutorial, Sign-in with Keycloak: tokens from an identity provider checked with
jwt-jwks-urlandjwt-aud, a role claim mapped from a user attribute, key rotation, and
the settings for Auth0 and Microsoft Entra ID.examples/keycloak/has the Compose file and
the realm setup script, tested against Keycloak 26.4. - A fuzz test of the URL grammar, run in CI: arbitrary query strings must produce SQL whose
values are all parameters and whose identifiers all come from the schema, and must be refused
with a 4xx error rather than a panic or a 500.
Changed
SIGHUPnow reloads the schema cache (and the TLS certificate), likeSIGUSR1; the documented
systemd unit uses it forsystemctl reload. The HTTP server's own errors, such as failed TLS
handshakes, go to the structured log at debug level.
Fixed
- Many-to-many embedding found no relationship when the junction table's foreign keys were not
linked to a loaded schema; only schemas built in memory (tests, tools) were affected.
v0.5.0
Binaries for Linux, macOS, Windows and FreeBSD are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.5.0. See CHANGELOG.md for details.
Added
- Realtime events:
GET /events/<name>[,<name>...]streams FirebirdPOST_EVENTnotifications
as Server-Sent Events, for the names listed indb-eventsand the roles indb-events-roles.
One database attachment serves every stream; keepalives every 15 s; metrics for streams and
notifications (events). The example
database postsorder_placedfromplace_orderand has anotify(name)procedure. - Query plans for writes:
Accept: application/vnd.pgrst.planonPOST,PATCH,PUTand
DELETEruns the request in a transaction that is always rolled back and returns the plans
of its statements, as PostgREST does. application/vnd.pgrst.plan+jsonaddstree, the explained plan parsed into
{node, children}nodes, next to the text inplan.
Changed
- Plain JSON reads (no embedded resources) encode each row as it is read instead of keeping
every row and then the JSON: a 200,000-row, 106 MB response peaks at 216 MB of process memory
instead of 536 MB and takes 1.7 s instead of 4.5 s. Prefer: params=multiple-objectson a selectable procedure takes up to 1024 objects (their
arguments within 48 KB) instead of 200: the calls are driven by a counting CTE in one
statement, so the procedure is one context however many objects there are.
v0.4.0
Binaries for Linux, macOS, Windows and FreeBSD are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.4.0. See CHANGELOG.md for details.
Changed
- Reads cost 4 round trips to Firebird instead of 7:
GET/HEADrequests reuse a long-lived
read-onlyREAD COMMITTEDtransaction per connection (same results as a fresh one, see
Transactions;
db-read-tx-reuse = falserestores the old behaviour), and the request context block no
longer triggers a row-count request. On the benchmark database: a row by key +30%, a
function call +60%, a computed field with order +25%, a 25-row embed +30%. - Multi-row inserts no longer run one statement per row. On Firebird 4+, a plain insert with
return=minimalgoes through the wire protocol's batch API (one batch per column set);
everything else (Firebird 3, upserts,return=representation, BLOB values) runs in
EXECUTE BLOCKs of up to 100 rows that return every row's keys. A 100-row insert takes a
third of the time it took, a 1000-row one less. Rows of a view are still inserted one by
one. Transactions are now bound to one pooled connection each, which the batch API needs.
Added
- Relationships across SQL schemas on Firebird 6: a foreign key between tables of two exposed
schemas (db-schemas) is an embeddable relationship in both directions, as in PostgREST,
also when the request's schema has a table of the same name. Foreign keys into schemas that
aren't exposed stay out (schemas). - Benchmarks against PostgREST on identical data, with the PostgreSQL schema and seed in
bench/postgrest/and the results and their interpretation on the Benchmarks page. - A stability promise saying
which interfaces are stable, which are experimental, and how deprecations and behaviour
changes are announced, and an upgrade guide
with the changes to check for each version. firebirdrest -check [config-file]validates the configuration and prints warnings without
connecting; the warnings are also logged at startup. Deprecated setting names, when there
are any, keep working for a minor version with a warning.
v0.3.0
Binaries for Linux, macOS, Windows and FreeBSD are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.3.0. See CHANGELOG.md for details.
Production readiness: Prometheus metrics, Server-Timing, JWKS keys from identity providers
with a token cache, single values and files in any media type, raw bodies for RPC, and the fixes
of a security review. A JavaScript client with the supabase-js API,
firebirdrest-js, checks compatibility in CI.
Behaviour changes to check before upgrading:
server-cors-allowed-origins = "*"no longer sendsAccess-Control-Allow-Credentials; list
the origins explicitly if browsers must send credentials.GET /describes only what the caller's role is granted; set
openapi-mode = "ignore-privileges"for the old document.- Requests are limited to 60 seconds and 16 MB bodies (
server-request-timeout,
server-max-body-size), andand=/or=nesting to 16 levels,in.()to 1000 values. - Permission errors no longer include Firebird's "Effective user is ..." line, and unexpected
errors return a generic message (the cause is logged). - Pool and statement cache metrics label roles in upper case.
- Responses carry
X-Content-Type-Options: nosniffand a sandboxingContent-Security-Policy.
Security
A review of the SQL generation, authentication and HTTP surface found no injection or
authentication bypass, and these weaknesses, all fixed here:
- A deeply nested
and=/or=filter or a longin.()list could take the Firebird connection
down or burn CPU for tens of seconds: nesting is limited to 16 levels andin.()to 1000
values (400beyond). - A
Rangeheader with a huge end overflowed the limit arithmetic and removeddb-max-rows;
such ranges are refused with416. - A single value requested as
text/html(or SVG, XML, script) was served as a page of the
API's origin: stored script in a column could run there. Every response now carries
X-Content-Type-Options: nosniffand a sandboxingContent-Security-Policy, and those types
are sent as downloads. - With the default
server-cors-allowed-origins = "*", any origin was reflected together with
Access-Control-Allow-Credentials: true; the wildcard now answers*without credentials, and
only listed origins get credentials (withVary: Origin). GET /described every table and routine to anonymous callers. The document now follows the
caller's privileges (openapi-mode, defaultfollow-privileges).- Permission errors named the database login ("Effective user is ..."), and unexpected errors
returned their internal text; both are now generic for the client and logged on the server. - Each case spelling of a role name (
web_user,WEB_USER...) opened its own connection pool;
names are folded to upper case, as Firebird does. - The HTTP server had no request timeout and read request bodies inside the transaction:
server-request-timeout(60 s),server-max-body-size(16 MB) and header limits are
enforced, and bodies are read before the transaction starts. - An empty boolean in the config file (
admin-pprof =) counted as true; it now keeps the
default. admin-server-hostbinds the admin server to its own address, e.g.127.0.0.1.
Added
-
Metrics in the Prometheus format at
GET /metricson the admin port: requests by method and
status, a request duration histogram, error codes, connection pool and statement cache usage
per role, schema cache loads, JWT cache and JWKS fetches, and Go runtime figures. See
Monitoring. -
server-timing-enabledadds aServer-Timingheader (jwt,parse,transaction,
response), as in PostgREST. -
jwt-secretaccepts a JWK or a JWKS as JSON, PEM certificates and PKCS#1 RSA keys, several PEM
keys at once, Ed25519 keys (EdDSA) and@/path/to/file;PS256/384/512tokens are accepted
with RSA keys. Tokens with akidare checked against the matching key. -
jwt-secret-is-base64for base64 encoded HMAC secrets. -
jwt-jwks-urlfetches the signing keys of an identity provider (Auth0, Keycloak, Okta, Azure
AD, ...) at startup and everyjwt-jwks-refreshseconds, and again when a token names an
unknownkid, so rotated keys work right away. -
A JWT validation cache (
jwt-cache-max-entries, default 1000) skips the signature check for
tokens already accepted, until they expire or for 5 minutes. -
Single values as is: any concrete
Accepttype that isn't a built-in format (text/plain,
application/octet-stream,image/png,application/geo+json...) returns the one selected
value of the one matching row as the body, with thatContent-Type. Binary columns are sent
as their bytes, so files can be served straight from the database. Works for reads, writes
withreturn=representationand RPC. -
Raw request bodies for RPC: a
POST /rpc/<name>whoseContent-Typeisn't JSON or a form puts
the body into the routine's only parameter, as text or bytes (FBRST133otherwise). -
The example database has a
coverstable with a binary BLOB and ablob_sizefunction. -
A raw single-value response sends a binary value as the driver returns it, without a base64
encode/decode round trip, so serving a file holds one copy of it in memory.
Fixed
-
Acceptentries with a parameter that has no value, such as the
application/vnd.pgrst.plan+text; for="application/json"; options=;that postgrest-js sends
forexplain(), were rejected with406. The media type is now read even when a parameter
is malformed. -
Responses are sent only after the transaction commits. A failed
COMMITnow returns an error
instead of a success response, and headers set before an error (such asContent-Rangeon a
416) no longer appear on the error response. -
Graceful shutdown waits for requests in flight (up to 10 seconds) before closing the database
connections; before, requests running atSIGTERMcould fail with500.
v0.2.0
Binaries for Linux, macOS, Windows and FreeBSD are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.2.0. See CHANGELOG.md for details.
SQL schemas for Firebird 6, chosen per request with Accept-Profile and Content-Profile.
Schema support is experimental: it is tested against the Firebird 6 snapshot and may change
if Firebird 6 changes before its release. Existing setups keep working as before: Firebird 3-5
are unaffected, and Firebird 6 without db-schemas serves PUBLIC as it did.
Added
- SQL schemas (Firebird 6, experimental):
db-schemaslists the schemas to expose, the first
being the default (publicwhen unset). Requests pick one withAccept-Profile(reads) orContent-Profile
(writes), as in PostgREST; responses carryContent-Profile, and unexposed schemas get406
(FBRST106). Each schema has its own cache and OpenAPI description, statements use
schema-qualified names, and embedding follows foreign keys within the schema.
examples/bookstore-fb6.sqladds aninventoryschema.db-pre-requestacceptsschema.proc.
Fixed
- On Firebird 6 the schema cache matches system-table rows by schema as well as by name (domains,
constraints, indexes, routine parameters), instead of relying on names being unique.
v0.1.2
Binaries for Linux, macOS, Windows and FreeBSD are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.1.2. See CHANGELOG.md for details.
Write guards with max-affected, query plans, and per-request time zones.
Added
Prefer: handling=strictrefuses unknown or invalid preferences (FBRST130); the default
lenientignores them, as before.Prefer: max-affected=n(withhandling=strict, as in PostgREST): aPATCHorDELETEthat
would change more thannrows fails withFBRST131and is rolled back.- Query plans: with
db-plan-enabled = true,Accept: application/vnd.pgrst.plan(+textor
+json) returns Firebird's explained plan of every statement aGET/HEADrequest runs,
including embeds and counts, instead of the data. Prefer: timezone=<IANA name>(Firebird 4+):TIME/TIMESTAMP WITH TIME ZONEvalues are
returned in that zone, and the Firebird session runs in it (SET TIME ZONE, reset before the
connection is reused). Unknown zones are ignored, or refused withhandling=strict.examples/bookstore-fb4.sqladds ameetingstable with time zone columns for Firebird 4+.- The release workflow can also publish the Docker image to Docker Hub, a Homebrew cask and a
Scoop manifest, each once its secret is configured (seeRELEASING.md).
Changed
- Release notes are taken from the version's section of
CHANGELOG.md, also for a release first
drafted on GitHub.RELEASING.mddescribes the release steps.
v0.1.1 Release
Binaries for Linux, macOS, Windows and FreeBSD are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.1.1. See CHANGELOG.md for details.
Ordering by embedded columns, limited PATCH/DELETE, bulk RPC calls, estimated counts, and
Firebird 6 support (tested against the 6.0 snapshot).
Added
- Ordering by a column of a to-one embed, as in PostgREST:
order=authors(name).desc. It works at
any level (books.order=authors(name)), by alias and on spread embeds. To-many embeds are
refused withFBRST126. PATCHandDELETEwithlimitandoffset, as in PostgREST:?order=created_at&limit=10
changes only the first 10 matching rows. A limit needs anorder(FBRST127), and the primary
key is added to it as a tie-breaker.db-safe-updatesaccepts a limit instead of a filter.Prefer: params=multiple-objectson/rpc: an array body calls the routine once per object in
one transaction. Selectable procedures return the combined rows as one result (oneUNION ALL
query, soselect, filters,orderandlimitapply to all of them; at most 200 objects),
executable procedures and functions an array of results.Prefer: params=single-objecton/rpc: the raw JSON body is passed to a routine's only text
parameter. The example database haslog_requestfor this.Prefer: count=plannedandcount=estimated.plannedestimates a table's rows from the
statistics of its unique indexes without reading the table;estimatedcounts exactly up to
db-max-rows(or 1000) and uses the estimate above. Where no estimate applies (filters, views,
procedures, no statistics) the count is exact andPreference-Appliedsayscount=exact.
Unknowncount=values are ignored instead of being treated asexact. The example database
computes its index statistics after loading data.- CI runs the integration suite against the Firebird 6 snapshot (
firebirdsql/firebird:6-snapshot)
on every push and weekly. The job may fail without failing the build until Firebird 6 is released.
Fixed
- A
PATCHwith?limit=andreturn=representationupdated every matching row but returned
onlylimitof them; the limit now applies to the update itself. - Schema loading reports errors that happen while reading the system tables, instead of
silently loading an incomplete schema (on Firebird 6 this hid every foreign key). - Foreign keys and unique constraints load on Firebird 6.
- On Firebird 6, the
hintof a PSQL exception names the exception without the"PUBLIC".
schema prefix, as on earlier versions.
Full Changelog: v0.1.0...v0.1.1
v0.1.0
Binaries for Linux, macOS, Windows and FreeBSD are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.1.0. See CHANGELOG.md for details.
Changelog
- ae55914 Add aggregates, spread embeds and packaged routines
- e99ba45 Add computed fields and computed relationships
- fb1468f Add documentation site (MkDocs Material, published to GitHub Pages)
- 0abc0cb Add tagged releases: GoReleaser binaries and multi-arch Docker image
- 92072dc FirebirdREST: REST API server for Firebird databases
- 8b624f4 Performance: statement cache, conditional request context, fast JSON
- 4655399 Support spread on to-many relationships
- 712a7fb release: add a Run workflow button that tags and releases