Modern read-only database explorer for PostgreSQL, shipped as a Spring Boot auto-configuration starter.
Early release — not production-ready. PostgreSQL is the currently supported database. Expect breaking changes.
Browse PostgreSQL table data with stable primary-key pagination:
Inspect columns, native types, nullability, primary keys, unique constraints, and foreign keys:
- Drop
gothdb-spring-boot-starterinto a Spring Boot app with aDataSource— it auto-configures itself, no manual wiring. - Read-only REST API over
DatabaseMetaData:GET /gothdb/api/statusGET /gothdb/api/schemasGET /gothdb/api/schemas/{schema}/tablesGET /gothdb/api/schemas/{schema}/tables/{table}/columnsGET /gothdb/api/schemas/{schema}/tables/{table}/primary-keyGET /gothdb/api/schemas/{schema}/tables/{table}/foreign-keysGET /gothdb/api/schemas/{schema}/tables/{table}/indexesGET /gothdb/api/schemas/{schema}/tables/{table}/rows?page=&size=
- Unified error handling (400 bad params, 404 unknown schema/table, generic 500 — no JDBC internals leaked), scoped to the GothDB controllers so it never intercepts errors from the host application.
- Authentication required by default — HTTP Basic on its own, or your existing Spring Security setup. See Security.
- A minimalist black-and-white UI (
ui/): schemas → tables → columns/data, with connection status.
Requirements:
- Java 21+
- Maven 3.9+
- Docker
Build the project:
mvn packageStart PostgreSQL on its standard port 5432:
docker run --name gothdb-postgres \
-e POSTGRES_DB=gothdb \
-e POSTGRES_USER=gothdb \
-e POSTGRES_PASSWORD=gothdb \
-p 5432:5432 \
-d postgres:17.6-alpineIf host port 5432 is already in use, choose another one (for example -p 5433:5432) and use the
same port in DATABASE_URL.
Wait until PostgreSQL is ready:
docker logs -f gothdb-postgresRun the external-style consumer application against that container:
DATABASE_URL=jdbc:postgresql://localhost:5432/gothdb \
DATABASE_USERNAME=gothdb \
DATABASE_PASSWORD=gothdb \
java -jar integration-tests/consumer-app/target/gothdb-consumer-app-0.0.1-SNAPSHOT.jarOn the first run, Spring initializes the same sample catalog previously used by the demo: friends,
products, orders, and order items. Open the UI at http://localhost:8080/gothdb/ and sign in with the sample
credentials gothdb / gothdb from application.yml; override them with GOTHDB_USERNAME and GOTHDB_PASSWORD.
The database remains initialized while the container exists. For subsequent application starts, skip the SQL initializer to avoid recreating the same tables:
DATABASE_URL=jdbc:postgresql://localhost:5432/gothdb \
DATABASE_USERNAME=gothdb \
DATABASE_PASSWORD=gothdb \
java -jar integration-tests/consumer-app/target/gothdb-consumer-app-0.0.1-SNAPSHOT.jar \
--spring.sql.init.mode=neverTo verify a custom GothDB path without editing application.yml:
DATABASE_URL=jdbc:postgresql://localhost:5432/gothdb \
DATABASE_USERNAME=gothdb \
DATABASE_PASSWORD=gothdb \
java -jar integration-tests/consumer-app/target/gothdb-consumer-app-0.0.1-SNAPSHOT.jar \
--spring.sql.init.mode=never \
--gothdb.path=/ur-pathThe UI and API then move together:
- UI:
http://localhost:8080/ur-path/ - API status:
http://localhost:8080/ur-path/api/status - the old
/gothdb/path returns404
Stop the container while keeping its database:
docker stop gothdb-postgresStart it again later:
docker start gothdb-postgresOr permanently remove the test container and its data:
docker rm -f gothdb-postgresThe Maven build installs a project-local Node.js, runs npm ci, bundles the UI into the
gothdb-autoconfigure JAR under META-INF/gothdb, and serves it from gothdb.path. A separate Node.js
process is not needed for normal use.
Add the starter and PostgreSQL driver to a Spring Boot application that already configures a JDBC
DataSource:
<dependency>
<groupId>io.github.lessmade</groupId>
<artifactId>gothdb-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>No GothDB beans need to be declared manually. With a servlet application and a DataSource, the API
and embedded UI are auto-configured.
For frontend development, run the Vite dev server; it proxies API calls to the backend:
cd ui
npm install
npm run devOpen http://localhost:5173.
Run the unit and auto-configuration tests without external services:
mvn testRun all PostgreSQL integration tests (Docker is required):
mvn verify -Ppostgresql-integration-testsTestcontainers starts isolated PostgreSQL 17.6 containers on random ports, runs both the core metadata
integration test and the consumer application end-to-end HTTP test, and removes the containers after
the build. A manually started gothdb-postgres container is not required for this command.
GothDB serves the full contents of every visible schema, so the API and UI require authentication out of the box.
gothdb.security.mode decides how:
| Mode | Behaviour |
|---|---|
auto (default) |
spring-security when Spring Security is on the classpath, otherwise basic. |
basic |
HTTP Basic against gothdb.security.username / gothdb.security.password, with no dependency on Spring Security. |
spring-security |
Requires a user authenticated by the application's own filter chain, optionally holding one of gothdb.security.roles. |
none |
No authentication. Logs a warning at startup. |
In basic mode without a configured password, one is generated on every start and written to the log:
Using generated GothDB password: 2f1c9a7e-...
That is a development convenience — set gothdb.security.password for anything else.
GothDB never registers a SecurityFilterChain of its own. That matters: Spring Boot's default chain backs off as soon
as any SecurityFilterChain bean exists, so a starter that contributed one would silently unprotect the rest of the
host application. Authorization runs in a servlet filter scoped to gothdb.path instead, and your own chain keeps
working untouched.
To write your own rules against the GothDB path, inject the GothDbRequestMatcher bean:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, GothDbRequestMatcher gothDb) throws Exception {
return http
.authorizeHttpRequests(requests -> requests
.requestMatchers(gothDb).hasRole("DBA")
.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.build();
}Responses under gothdb.path also carry Cache-Control: no-store, X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: no-referrer and a default-src 'self' content security policy.
Independently of the mode, every connection GothDB opens is marked read-only before use.
gothdb:
enabled: true # default true, auto-disables without a servlet DataSource
path: /gothdb # base path for the API and UI
ui:
enabled: true # set false to expose only the REST API
schemas:
include: [] # empty means all schemas
exclude: # PostgreSQL system schemas are excluded by default
- information_schema
- pg_catalog
- pg_toast
- pg_temp_*
- pg_toast_temp_*
rows:
count-mode: exact # exact returns totals; none avoids COUNT(*)
max-page-size: 200
query-timeout: 5s
security:
mode: auto # auto | basic | spring-security | none
username: gothdb # basic mode only
password: # basic mode only, generated and logged when empty
realm: GothDB # basic mode only
roles: [] # spring-security mode only, empty accepts any authenticated usercore— framework-agnostic JDBC metadata reading, no Spring dependency.autoconfigure— Spring Boot auto-configuration, REST controllers, error handling.spring-boot-starter— the dependency consumers actually add to their project.integration-tests/consumer-app— runnable PostgreSQL consumer and end-to-end Testcontainers test; it depends on GothDB through the starter.ui— the Vite + React frontend, built by Maven and packaged intogothdb-autoconfigure.

