pREST v2.0.0-rc6 is a release-candidate milestone for pREST, the open-source tool that turns PostgreSQL into a REST API. The headline change is architectural: the monolithic Adapter interface is decomposed into smaller port interfaces, and HTTP controllers are rewritten as dependency-injected handlers following hexagonal (ports-and-adapters) design. The composite Adapter still exists so the Postgres implementation satisfies one type and existing wiring stays compatible. See the v2.0.0-rc6 release for binaries and Docker images.
Table of Contents
- Table of Contents
- What changed in v2.0.0-rc6
- The problem: monolithic Adapter and global controllers
- Hexagonal architecture in pREST
- Dependency injection wiring
- What this unlocks
- Other notable release changes
- Trying v2.0.0-rc6
- FAQ
What changed in v2.0.0-rc6
pREST exposes PostgreSQL tables and custom SQL scripts over HTTP — a low-code REST layer for existing or new Postgres applications. Version 2 is a major internal overhaul shipped as release candidates; v2.0.0-rc6 is the latest RC, not final GA.
Highlights from the changelog:
- Architecture — Adapter split, DI controllers, hexagonal refactor (PR #968)
- Security — JWT auth bypass fix when enforcement runs without a key (#960)
- Query — OR clause support (#958)
- Tooling — GoReleaser Docker images (#953), Go 1.26, split unit/integration CI
- Observability —
slogmigration (#950)
The architecture work is the through-line. Everything else — security fixes, query features, build tooling — lands on a cleaner foundation.
The problem: monolithic Adapter and global controllers
Before v2, pREST’s database layer was expressed as a single large Adapter interface. That one type owned catalog queries (databases, schemas, tables), request-to-SQL building, query execution, permissions checks, script running, and transaction management.
Controllers compounded the coupling. Package-level functions reached into global config and the global adapter instance. A handler that only needed to execute a SELECT still lived in a package implicitly tied to the full adapter surface.
The practical consequences:
- Testing — mocking required satisfying the entire
Adapterinterface even when a test only exercised one code path. - Change isolation — edits to catalog SQL building risked touching CRUD or script paths because everything shared one interface boundary.
- Extensibility — adding a new database backend meant implementing every method on day one, with no way to depend on a subset.
Hexagonal architecture in pREST
v2 applies hexagonal architecture (ports and adapters). The application core — HTTP handlers — depends on narrow port interfaces. Concrete implementations (the Postgres adapter) sit on the outside as driven adapters.
flowchart TB
subgraph driving [Driving adapters]
HTTP[HTTP controllers]
end
subgraph core [Application core]
Handlers[AuthHandler CatalogHandler CRUDHandler ...]
Ports[Port interfaces]
end
subgraph driven [Driven adapters]
PG[postgres.Adapter]
end
HTTP --> Handlers
Handlers --> Ports
Ports --> PG
Port interfaces
The monolithic Adapter is split into focused interfaces in adapters/:
| Port | Responsibility |
|---|---|
CatalogQuerier |
SQL for listing databases, schemas, and tables |
RequestQueryBuilder |
Build queries from HTTP request parameters |
QueryExecutor |
Execute queries and return result scanners |
SQLBuilder |
Construct INSERT/UPDATE/DELETE/SELECT statements |
PermissionsChecker |
Table- and field-level access control |
ScriptRunner |
Execute custom SQL scripts |
DatabaseRegistry |
Resolve database names and connections |
TransactionManager |
Begin and manage transactions |
LegacyExecutor |
Non-context execution with transaction support |
Composite Adapter
The composite Adapter interface still exists. It embeds all port interfaces so the Postgres implementation satisfies one type and callers that need the full surface can keep using Adapter:
// adapters/adapter.go
type Adapter interface {
RequestQueryBuilder
QueryExecutor
CatalogQuerier
SQLBuilder
PermissionsChecker
ScriptRunner
DatabaseRegistry
TransactionManager
LegacyExecutor
}
Handlers depend only on the ports they need. CatalogHandler takes a CatalogQuerier; CRUDHandler takes QueryExecutor, PermissionsChecker, and related ports — not the full adapter.
Driving adapters
HTTP controllers in controllers/ are structs with injected dependencies: AuthHandler, CatalogHandler, CRUDHandler, TableHandler, ScriptHandler, and HealthHandler. The router registers methods on these structs instead of package-level functions.
Dependency injection wiring
Dependency assembly lives in controllers/deps.go. A Deps struct holds typed port references:
type Deps struct {
Catalog adapters.CatalogQuerier
Builder adapters.RequestQueryBuilder
Executor adapters.QueryExecutor
SQL adapters.SQLBuilder
Perms adapters.PermissionsChecker
Scripts adapters.ScriptRunner
DB adapters.DatabaseRegistry
Cache ResponseCacher
// ...
}
NewDepsFromConfig maps a single Postgres Adapter instance into each port field. The same concrete object is passed multiple times, narrowed to different interface types. NewHandlers then constructs each handler with only its required deps.
Middleware follows the same pattern. AccessControl and CRUDStack receive a PermissionsChecker via injection instead of reaching for global state.
Request flow example: CRUD read
A GET /{database}/{schema}/{table} request flows like this:
- Router — dispatches to the CRUD middleware stack
- CRUDStack middleware — calls
PermissionsChecker.TablePermissionsto verify the user can read the table - CRUDHandler.Select — calls
PermissionsChecker.FieldsPermissionsfor column-level access, builds SQL via the query builder, executes throughQueryExecutor.QueryCtx - Response — JSON bytes returned to the client; optionally cached via
ResponseCacher
Each step depends on an interface, not a global. Unit tests can inject gomock implementations per port without a live database.
What this unlocks
- Testability — gomock and sqlmock unit tests per handler; integration tests run in a separate CI workflow against Docker Compose Postgres
- CI split — dedicated
test-unit.ymlandtest-integration.ymlworkflows replace a single combined test job - Extensibility — a new database backend implements the port interfaces its handlers need; HTTP layer stays unchanged
- Safer defaults — bcrypt replaces MD5 as the default password encryption algorithm in v2 config
Other notable release changes
Beyond architecture, v2.0.0-rc6 includes meaningful functional and operational improvements.
JWT auth bypass fix (#960) — when JWT enforcement was enabled but no signing key was configured, requests could bypass authentication. The fix ensures misconfiguration fails closed.
OR clause support (#958) — filter queries can now express OR conditions, expanding the query surface for clients that build filters from HTTP parameters.
GoReleaser Docker images (#953) — official container images are now built through GoReleaser, aligning release artifacts with binary builds.
Structured logging (#950) — the API layer migrates to Go’s slog for structured, leveled logging.
Trying v2.0.0-rc6
This is a release candidate — pin the version and watch the prest/prest repo for GA.
Docker:
docker pull prest/prest:v2.0.0-rc6
Binaries — download platform-specific builds from the v2.0.0-rc6 release assets.
For issues, feedback, or migration questions, open a discussion or issue on GitHub. The v2.0.0-rc6 changelog links to every merged PR.
FAQ
What is pREST v2.0.0-rc6?
pREST v2.0.0-rc6 is the latest release candidate for pREST 2.0, an open-source Go tool that exposes PostgreSQL as a REST API. It includes a major architectural refactor (hexagonal design, Adapter decomposition, dependency-injected controllers) plus security fixes, OR clause support, GoReleaser Docker images, and structured logging via slog.
What is the Adapter interface refactor?
The v2 refactor splits pREST’s monolithic Adapter interface into nine smaller port interfaces (CatalogQuerier, QueryExecutor, RequestQueryBuilder, and others). A composite Adapter interface still embeds all ports so the Postgres implementation satisfies one type. HTTP handlers depend only on the ports they need, which improves testability and makes future database backends easier to add.
What is hexagonal architecture in the context of pREST?
In pREST v2, hexagonal architecture means HTTP controllers (driving adapters) depend on port interfaces in the application core, and the Postgres adapter (driven adapter) implements those ports. Dependencies point inward: handlers never import Postgres-specific code. Wiring happens at startup through NewDepsFromConfig and NewHandlers.
Is v2.0.0-rc6 production-ready?
v2.0.0-rc6 is a release candidate, not a final GA release. It is suitable for evaluation and staging environments where teams can pin the version and provide feedback. Production deployments should wait for a stable v2.0.0 GA or accept RC-level risk with a pinned image tag and a rollback plan.