pREST is an open-source Go server that turns an existing PostgreSQL database into a REST API and, since v2.1.0, a read-only MCP endpoint, without writing backend code. I maintain it, and I have written a post for every v2 release since July. This page is the index: what each version changed, which ones carry a security advisory, which one to run today (v2.4.2), and which post to read first depending on what you want to do.
Table of Contents
Every v2 release so far
Dates are from the GitHub releases page. Each row links to the post I wrote about it and to the release itself.
| Version | Released | What changed | Post | Release |
|---|---|---|---|---|
| v2.0.0-rc6 | Jul 1, 2026 | Hexagonal refactor: the monolithic Adapter split into port interfaces, dependency-injected controllers, JWT bypass fix, OR filters, slog |
Architecture post | v2.0.0-rc6 |
| v2.0.0 | Jul 8, 2026 | First stable v2: multi-database registry with alias routing, connection pooling, config resilience, /_ready |
v2.0.0 / v2.1.0 post | v2.0.0 |
| v2.1.0 | Jul 9, 2026 | Read-only MCP endpoint at /_mcp on the same HTTP server, inheriting auth and ACL |
same post, MCP tutorial | v2.1.0 |
| v2.2.0 | Jul 18, 2026 | Embedded Studio UI at /_studio/, custom queries stored in a prest_queries table, TimescaleDB integration tests |
v2.2.0 post | v2.2.0 |
| v2.3.0 | Jul 22, 2026 | Fix for an unauthenticated SQL injection in _select (CVSS 9.8), JWKS on jwx v3, adapter registry |
v2.3.0 post | v2.3.0 |
| v2.4.0 | Jul 27, 2026 | pgvector ordering and distance filters, opt-in OpenTelemetry, six more injection-adjacent fixes | v2.4.0 post | v2.4.0 |
| v2.4.1 | Jul 29, 2026 | Fix for _QUERIES template injection (CVSS 8.6) and /_mcp ignoring [expose]; introduced a keyword screen that blanked real data |
v2.4.1 / v2.4.2 post | v2.4.1 |
| v2.4.2 | Aug 11, 2026 | Parameter binding with sqlVal and sqlList, script path containment, SQL out of logs, RFC 7518 minimum JWT key sizes |
same post | v2.4.2 |
If you only read one of those posts, read the v2.4.1 / v2.4.2 one. It explains why a SQL keyword blacklist cannot be made safe and why binding is the only fix that is both safe and lossless, which is the lesson I most want people to take from this series.
Upgrade path
Run v2.4.2. Every earlier v2 release has at least one open advisory, and v2.4.1 has a correctness regression on top. The table maps each advisory to the versions it affects, taken from the release posts above.
| Advisory | Severity | What it hits | Affected | Fixed in |
|---|---|---|---|---|
| GHSA-qvx3-q8vx-9q3c | CVSS 9.8 | Unauthenticated SQL injection through _select |
v2.0.0 to v2.2.0 | v2.3.0 |
| GHSA-v9v2-98xq-627c | CVSS 8.1 | Same class of gap in _groupby |
releases before v2.4.0 (see advisory) | v2.4.0 |
| GHSA-r3hj-2fxx-7f3h | CVSS 8.1 | Request headers interpolated into _QUERIES templates without screening |
releases before v2.4.0 (see advisory) | v2.4.0 |
| GHSA-rf54-gp27-88rv | multiple fixes | /batch skipped table ACL, _QUERIES skipped database validation, credential leaks in logs |
releases before v2.4.0 (see advisory) | v2.4.0 |
| GHSA-5rwc-2hg5-2hmc | CVSS 8.6 | Read-only SQL injection through unquoted {{.param}} in _QUERIES templates |
v2.0.0 to v2.4.0 | v2.4.1 |
| GHSA-x62p-38px-pp73 | CVSS 5.3 | /_mcp returned the full catalog regardless of [expose] |
releases before v2.4.1 | v2.4.1 |
| Issue #1030 | regression | Keyword screen silently blanked parameter values containing words like do or as, returning wrong rows with HTTP 200 |
v2.4.1 only | v2.4.2 |
The order I would do things in, condensed from the upgrade sections of the v2.3.0, v2.4.0 and v2.4.1 / v2.4.2 posts:
- Patch to v2.4.2 first, especially if
prestdis reachable from clients you do not control. The v2.3.0 and v2.4.1 injections are unauthenticated on a default config. Skip v2.4.1 entirely. - Check
jwt.keylength before restarting. From v2.4.2, HS256 needs at least 32 bytes, HS384 48 and HS512 64. An undersized key is treated as unusable at startup rather than failing at request time, so you can end up with auth disabled and a log line telling you why. The auth docs list the minimums. - Audit
_QUERIEStemplates. Move every free-form value from'{{.x}}'or{{.x}}to{{sqlVal "x"}}, and repeated parameters to{{sqlList "x"}}. Quoted interpolation was never exploitable by the v2.4.1 advisory, but from v2.4.2 an interpolated value that trips the screen fails the request with a 400, so binding is also what keeps your endpoints working for values likecompra-do-mes. - If you used to block
/_mcpat a reverse proxy because it ignored[expose], that rule is redundant from v2.4.1. - Then fix the defaults the v2.3.0 advisory called out: set
auth.enabled = true, setaccess.restrict = truewith explicit[[access.tables]], and stop connecting as the Postgres superuser. Patching closes the injections. It does not change your config.
Two more notes on the path. Coming from v1.x, expect breaking changes: the Go module path is github.com/prest/prest/v2, bcrypt replaced MD5 as the default password encryption, and the config shape changed, so test in staging (v2.0.0 post). Coming from rc6, the upgrade is incremental and there is no second rewrite.
Where to start
Pick by what you want to do.
You want a REST API over a Postgres database you already have. Start with pREST in 10 minutes. It goes from a Docker Compose file to list, filter, insert, update and delete requests, then turns on JWT auth, restricts a table, adds a custom SQL route with a bound parameter and calls the MCP endpoint with curl.
You want an AI agent to read your database without handing it a connection string. The MCP server tutorial walks through /_mcp with curl and wires it into Cursor and Claude Desktop. The AI tooling post covers the prest-mcp stdio adapter and the Cursor and OpenClaw plugins.
You are deciding between tools. pREST vs PostgREST is the comparison most people ask me for. If the question is which MCP server to put in front of Postgres, Postgres MCP servers compared covers the options, pREST included.
You care how it is built. The rc6 architecture post explains the port interfaces and dependency injection, and the v2.3.0 post explains how the adapter registry sits on top of that.
What’s in the box
One line per feature, from the docs.
REST routes. GET, POST, PATCH/PUT and DELETE on /{database}/{schema}/{table} map to SELECT, INSERT, UPDATE and DELETE, with _page, _page_size, _order, _select, _count, _groupby and filter operators such as $gt, $in, $ilike and $null in the query string (parameters).
Auth. POST /auth with a username and password from a configured users table returns a JWT; jwt.default = true enforces it on every route except the whitelist, and HMAC keys have minimum sizes since v2.4.2 (auth).
ACL. [access] restrict = true makes unlisted tables inaccessible, [[access.tables]] grants read, write and delete per table with an optional fields list, and [[access.users]] overrides per identity (permissions).
Custom queries. SQL files under queries.location become /_QUERIES/{folder}/{script} routes, with .read.sql, .write.sql, .update.sql and .delete.sql suffixes picking the HTTP verb and sqlVal, sqlList and ident binding request values (custom queries).
Multi-database. With PREST_PG_SINGLE=false and [[databases]] entries or DATABASE_ALIAS_N / DATABASE_URL_N pairs, the first path segment selects a registered alias, and /_ready checks every alias (multi-database).
MCP. GET /_mcp returns the discovery payload and POST /_mcp takes JSON-RPC initialize, tools/list and tools/call; tools are read-only, capped at 100 rows, and honor the same auth, permissions and [expose] settings as REST (MCP over HTTP).
pgvector. _korder=embedding:l2:[...] orders by vector distance and embedding:vecdist=l2:lt:[...]:0.5 filters by a distance threshold, both since v2.4.0 (parameters).
OpenTelemetry. An [otel] section, off by default, pushes traces, metrics and logs over OTLP/gRPC with one span per HTTP request, SQL round trip and MCP tool call (v2.4.0 post).
Studio. A read-only explorer embedded in the binary at /_studio/, on by default and disabled with PREST_STUDIO_ENABLED=false, that browses the catalog, builds REST requests and invokes MCP tools as a same-origin client (Studio).
FAQ
What is pREST?
pREST is an open-source Go server that exposes a PostgreSQL database as a REST API and, since v2.1.0, as a read-only MCP endpoint for AI agents. The README describes it as CRUD, custom SQL routes, auth, ACL and MCP on top of an existing or new Postgres database, version 9.5 or higher, without hand-writing a backend. It runs as a single prestd binary or the prest/prest Docker image.
Is pREST production-ready?
The README calls it production-ready, and it ships as a single binary with auth, ACL and multi-database routing built in. The caveats are the defaults: auth.enabled and access.restrict are both off out of the box, and the v2.3.0 advisory notes the official Docker images connect as the Postgres superuser, so a production deployment should turn auth on, restrict tables explicitly, use a non-superuser role and run v2.4.2 or later.
Which pREST version should I run?
v2.4.2, the latest release as of this page. Every earlier v2 release is affected by at least one of the advisories in the upgrade table above, and v2.4.1 also has a regression that silently blanked parameter values. Pull prest/prest:v2.4.2 or download a binary from the release page.
Is pREST free?
Yes. pREST is open source under the MIT License, and the binaries, Docker images and Homebrew formula are all free to use. The docs do not describe a paid tier.
Does pREST support MySQL or SQLite, or only Postgres?
Only PostgreSQL and Postgres-compatible engines today. The databases page lists PostgreSQL as native, CockroachDB and YugabyteDB as certified over the Postgres wire protocol, and TimescaleDB and Amazon Redshift as compatible with documented caveats. MySQL, SQLite and SQL Server are on the roadmap, and the docs say not to expect a MySQL or SQLite connection string to work with current releases.