pRESTd: REST and MCP endpoints over an existing Postgres database
PostgreSQL ➕ REST, low-code, simplify and accelerate development, ⚡ instant, realtime, high-performance on any Postgres application, existing or new, MCP server
At a glance
- What is it?
- pRESTd (prest/prest) exposes CRUD, custom SQL routes, ACL and a read-only MCP endpoint on top of Postgres 9.5 or higher without a hand-written backend. The interesting part is not the CRUD, it is the query templating rules, and those rules are also where it can bite.
- Who is it for?
- Adopt pRESTd if you already have a Postgres schema you trust and you want an HTTP surface over it without writing handlers, and if you are willing to keep every user-supplied value in a bound helper rather than an interpolated template. Do not adopt it if your API needs per-row business logic, or if you expect the interpolation screen to accept arbitrary search phrases.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap pRESTd fills: a schema that already exists
Most backend generators start from a model file and create the database for you. pRESTd goes the other direction. The README describes it as delivering REST and MCP APIs on top of your existing or new Postgres database, with the endpoint pattern GET /{database}/{schema}/{table}. That is the whole premise: the tables, columns and constraints are already there, and the server reads them at request time instead of from a generated schema file. The audience is teams with a working Postgres instance, a reporting replica, or a legacy database where the cost of writing and maintaining a CRUD layer is the actual problem. It is a poor fit for anyone who has not settled on Postgres, since the README states PostgreSQL version 9.5 or higher as the floor and the project name is a contraction of Postgres and REST. The repository topics list TimescaleDB alongside postgres, and the integration suite has a separate TimescaleDB target, so time-series deployments are at least acknowledged in the test layout.
What the server actually does between HTTP and Postgres
The visible architecture is a Go binary that speaks HTTP on one side and Postgres on the other. A request to /{database}/{schema}/{table} is translated into SQL against the identified table, which is why the database and schema appear as path segments rather than as configuration for a single connection. Multi-database operation is a documented topic, and the integration compose file starts several prestd containers at once (prestd, prestd-multicluster, prestd-auth, prestd-queries), which tells you the same binary is expected to serve more than one database and more than one configuration profile. Alongside the CRUD routes there is a custom query mechanism driven by a _QUERIES setting, and a read-only MCP endpoint, the Model Context Protocol surface that the README lists as a first-class feature rather than an add-on. Auth, ACL and JWT appear in the topics and in the README's feature list; the documentation site is where the details live, and this review cannot confirm the exact ACL grammar because the README does not include it.
The templating helpers are the real design decision
The most concrete part of the README is the section on passing values to query scripts, and it is worth reading closely because it defines how you are supposed to write every custom query. A _QUERIES script can interpolate values with a template syntax. Interpolated values become part of the SQL text, so pRESTd screens them: anything carrying quotes, --, ::, or, for multi-word values, a SQL keyword is refused, and a refused value that is interpolated fails the request with a 400. The README's own example is telling. SELECT * FROM articles WHERE slug = '{{.slug}}' is rejected for a value like compra do mes, because the phrase contains a word the screen treats as a SQL keyword. The alternative is to bind: SELECT * FROM articles WHERE slug = {{sqlVal "slug"}} renders as $1 and travels to Postgres out of band, so the screen does not apply. Three helpers are documented. sqlVal renders a single value as $1. sqlList handles a repeated query parameter such as ?tag=a&tag=b and renders ($1,$2). ident renders a table or column name, which cannot be bound, as "public"."users". Both sqlVal and sqlList can also read headers through the header prefix, and credential headers such as Authorization and Cookie are always withheld. The README's advice is unambiguous: prefer binding for anything user-supplied, search phrases especially. This is a design where the safe path is also the one that requires the query author to think, and the unsafe path fails loudly with a 400 rather than silently concatenating.
Getting a local instance up
The README points installation to the Get pREST documentation page and lists Docker, Homebrew and Go as the routes; the Homebrew badge references a prestd formula. Configuration is done through environment variables, with PREST_PG_URL named first, then pg.* or DATABASE_URL as alternatives. Once the server is pointed at a database, the first call is GET /{database}/{schema}/{table}. Two build commands appear in the README. A local development image compiles from source with docker build -t prest/prest:latest . , and release builds reuse the same Dockerfile and Dockerfile.noplugins with a pre-built prestd binary. Version metadata can be injected at build time through VERSION, COMMIT and DATE build arguments. For testing, make test-unit runs the unit suite locally, make test-integration-postgres runs the Postgres integration suites inside Docker with no local Postgres required, and make test-integration-timescaledb runs the Timescale-specific end-to-end tests. The compose route is explicit: docker compose -f integration/postgres/docker-compose.yml up -d --wait with the postgres, postgres-b, db-init, prestd, prestd-multicluster, prestd-auth and prestd-queries services, then run --rm --no-deps tests, then down -v --remove-orphans. Network tests require PREST_TEST_URL and skip when it is unset outside Compose. That last detail matters if you plan to run the suite in your own CI without the compose stack.
Where the interpolation screen becomes a user-facing bug
The screen is a security control, but it is also a behavioural constraint that leaks into your API contract. Any query parameter that flows into an interpolated template and happens to contain a quote, a double colon, a SQL comment marker, or a multi-word phrase with a common keyword will produce a 400. The README names do, as and or as examples of common words that trigger it. That means a search endpoint written the quick way will reject ordinary user input, and the failure is a client-visible error rather than a degraded result. The fix is documented and simple, but it has to be applied per query: move the value into sqlVal or sqlList. There is a second boundary in the same table. ident exists because identifiers cannot be bound, so any dynamic table or column name still goes through the screen and still renders as a quoted identifier. If your API design requires the caller to choose a table at runtime, you are back in the screened path with no binding escape hatch. A third limitation is structural rather than security-related: pRESTd maps HTTP onto database objects. Anything that needs multi-step business logic, external calls, or per-row decisions belongs in a query you write, and at that point you are maintaining SQL inside a configuration mechanism rather than application code. Teams that want a typed service layer with unit-testable handlers will find the abstraction in the wrong place.
How this differs from PostgREST
The obvious comparison is PostgREST, which also derives an HTTP API from a Postgres schema. The difference in approach is where the custom logic lives. PostgREST pushes custom behaviour into database functions and views, so the database is the API definition and the server stays thin. pRESTd keeps a template layer in its own configuration: _QUERIES scripts with {{sqlVal}}, {{sqlList}} and {{ident}} helpers that render parameters into a statement the server executes. That gives you a place to put a query without creating a database function, and it gives you a screening rule that PostgREST's function-call model does not need in the same form. The second difference is surface area. pRESTd ships an MCP endpoint as a documented feature and a read-only one at that, which is aimed at AI clients such as Cursor and Claude, and it lists multi-database operation as a first-class concern. If your reason for looking at this category is exposing a database to an MCP-capable client, that is a concrete distinction rather than a marketing one. If your reason is a plain CRUD API over functions you already wrote, the template layer is extra machinery you would not use.
Licence, release cadence and the cost of upgrading
The repository is MIT licensed, which permits commercial use and modification; the README also asks contributors to sign a Contributor License Agreement through CLA assistant, which affects people sending patches rather than people running the binary. This is not legal advice, and the CLA terms are the ones to read if you intend to contribute. On maintenance, the release history shown here covers v2.4.0, v2.4.1 and v2.4.2 within roughly two weeks in mid-2026, with the last push to the default branch in September 2026 and the repository not archived. The Go module path is versioned as github.com/prest/prest/v2, so major-version upgrades follow Go module semantics and v2 import paths. The practical upgrade cost is not the binary, it is your _QUERIES scripts: the templating helpers and the interpolation screen are the part of the system that your own code depends on, and a change to what the screen refuses would surface as 400s on endpoints you did not touch. Running make test-integration-postgres against a copy of your schema before an upgrade is the check that exercises those paths, and the compose file in integration/postgres is the harness for it.
Editorial conclusion
Adopt pRESTd if you already have a Postgres schema you trust and you want an HTTP surface over it without writing handlers, and if you are willing to keep every user-supplied value in a bound helper rather than an interpolated template. Do not adopt it if your API needs per-row business logic, or if you expect the interpolation screen to accept arbitrary search phrases. Before committing, run make test-integration-postgres against a copy of your own schema and check that each of your real query parameters survives the screen or is moved to {{sqlVal}} or {{sqlList}}.
Community notes