An admin panel for your PostgreSQL database, mounted inside your own Go server.
Hand it a pgxpool. It reads your schema and serves lists, search, filters, forms and CSV export.
That screen is examples/basic. This is all the admin code in it:
admin, err := pgdesk.New(pool, // your existing *pgxpool.Pool
pgdesk.WithTitle("Shop Admin"),
pgdesk.WithSecretKey(secret), // signs CSRF tokens
pgdesk.WithMiddleware(yourAuth), // who is signed in
pgdesk.WithAuthorizer(pgdesk.AllowAll), // what they may do
pgdesk.WithResource("orders", func(r *pgdesk.Resource) {
r.ListDisplay("id", "customer_id", "status", "total", "placed_at")
r.SearchFields("customer_id") // find orders by the customer's name
r.Filters("status", "placed_at")
r.FieldLabel("customer_id", "Customer")
r.DefaultSort("-placed_at")
}),
pgdesk.WithResource("customers", func(r *pgdesk.Resource) {
r.ListDisplay("id", "name", "email", "country")
r.SearchFields("name", "email")
r.Filters("country")
}),
)
admin.Mount(mux) // live at /admin/The code only names columns. pgdesk works out the rest from the database:
| In your schema | What you get |
|---|---|
customer_id REFERENCES customers |
Shows Ada Lovelace instead of 1, links to her record, gives the form a searchable customer picker, and lets you search orders by customer name |
CHECK (status IN ('pending', 'paid', ...)) |
A dropdown in the form and in the filter |
GENERATED ALWAYS AS IDENTITY, DEFAULT now() |
id is read-only; leave placed_at empty to get the default |
timestamptz |
A date-range filter |
| A view PostgreSQL can't update | No edit button |
The form comes from the column types and constraints.
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=pgdesk postgres:16
git clone https://github.com/pgdesk/pgdesk && cd pgdesk
DATABASE_URL=postgres://postgres:pgdesk@localhost:5432/postgres go run ./examples/basic
# open http://localhost:8080/admin/browser ─▶ your Go server ─▶ your auth middleware ─▶ pgdesk (an http.Handler) ─▶ PostgreSQL
"who is this?" schema from pg_catalog,
parameterized SQL only
- Keep your stack. It works with any router (Gin) and any ORM (GORM), because pgdesk reads the database, not your models. Its only dependency is
pgx. - Keep your auth. pgdesk has no login page. Your middleware says who the user is (example). An
Authorizerdecides what they may do. An optionalScoperdecides which rows they see, and pgdesk adds that to the SQLWHERE(how scoping is enforced). - Nothing is exposed until you name it. It shows only the tables you list, or all of them if you opt into
WithAutoRegister.
Concurrent edits can't silently overwrite each other, and with WithTxAuditLogger each change and its audit entry commit together or not at all.
pgdesk is built for internal tools used by trusted operators. Put it behind your auth and your network, not on the open internet. Serve it over HTTPS. On a trusted network without TLS, pass WithInsecureCookies().
Go 1.25+ and PostgreSQL 12+.
go get github.com/pgdesk/pgdesk@latestThe API may change before v1. The API reference is on pkg.go.dev. MIT licensed.
Logo based on the Go gopher by Renée French, licensed under CC BY 4.0.