Unified server configuration file
- Dominant language
- TypeScript
- Stars
- 481
- Forks
- 108
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 5
Description
## Description
Graph Explorer's configuration system has grown organically and is now a source of friction for users and contributors. Environment variables are inconsistently named (some prefixed `GRAPH_EXP_`, some `PROXY_SERVER_`, some unprefixed). Configuration is split across `.env` files, `config.json`, `defaultConnection.json`, Docker env vars, and shell scripts (`process-environment.sh`, `docker-entrypoint.sh`, `setup-ssl.sh`). The Docker image is based on Amazon Linux with manual Node installation and self-signed cert generation. Neptune Notebook is a special-cased build arg that changes base paths, ports, and log styles. The result is a system that is hard to configure, hard to debug, and hard to extend.
## Solution
Replace the entire configuration surface with a single JSON config file (`graph-explorer.config.json`) plus a small set of consistently-named environment variables (`GE_*` prefix). The config file becomes the single source of truth for connections, styles, and app-level settings. Environment variables handle deployment-specific overrides (port, TLS, log level). The Docker image is simplified to a `node:24-alpine` base with no shell scripts. Self-signed cert auto-generation is dropped in favor of user-provided certs. The server serves the client at `/` with all API routes under `/api/`. This is a breaking change requiring a new major version.
This work is blocked by the relative routes change (#1618), which eliminates the need for proxy URLs in connections and base path baking.
## User Stories
1. As a Docker deployer, I want to volume-mount a single config file to configure Graph Explorer, so that I don't need to manage dozens of environment variables
2. As a Docker deployer, I want a small, standard Docker image (node:24-alpine), so that my deployments are lighter and more secure
3. As a developer, I want `pnpm dev` to work with zero configuration, so that I can start contributing immediately
4. As a developer, I want to drop a `graph-explorer.config.json` in the project root to configure my local connections, so that setup is simple and consistent with production
5. As an ops engineer, I want environment variables to override config file values for port, TLS, and log settings, so that I can customize per-environment without editing files
6. As a Neptune Notebook deployer, I want to use the same Docker image with different config values, so that I don't need a special build
7. As a team lead, I want to define multiple named connections in the config file that are locked/managed, so that my team gets a consistent set of connections on every startup
8. As a team lead, I want to define default styles for nodes and edges in the config file, so that my team sees consistent visual defaults
9. As a user, I want to create my own connections through the UI alongside managed connections, so that I can explore additional databases
10. As a user, I want "reset styles" to return to the config-file defaults rather than hard-coded defaults, so that my team's visual standards are preserved
11. As a user, I want a clear error if my config file is malformed, so that I can fix it quickly
12. As an existing user, I want a migration tool that converts my old config to the new format, so that upgrading is painless
13. As a future Electron deployer, I want the config file location to be overridable via `GE_CONFIG`, so that the app can point to a user data directory
14. As a deployer, I want TLS to be opt-in with my own certs rather than auto-generated self-signed certs, so that I avoid browser trust issues
15. As a deployer, I want to optionally restrict CORS origins in the config, so that I can lock down access
16. As a developer, I want all API routes under `/api/` and static files at `/`, so that there's a clean separation and no route collisions
## Implementation Decisions
### Config File
- Format: JSON, named `graph-explorer.config.json`
- Location: `GE_CONFIG` env var, defaults to `./graph-explorer.config.json` relative to cwd
- Optional: app starts with empty state (no connections) if no config file found
- Validated with Zod schema on server startup
- A `graph-explorer.config.example.json` is checked into the repo; the actual config file is gitignored
### Config File Schema
```json
{
"server": {
"port": 80,
"logLevel": "info",
"logFormat": "pretty",
"tls": {
"cert": "/path/to/cert.pem",
"key": "/path/to/key.pem"
},
"corsOrigin": "https://example.com"
},
"connections": {
"prod-neptune": {
"name": "Production Neptune",
"url": "https://my-cluster.neptune.amazonaws.com:8182",
"queryEngine": "openCypher",
"fetchTimeout": 240000,
"nodeExpansionLimit": 500,
"authentication": {
"type": "iam",
"region": "us-east-1",
"serviceType": "neptune-db"
},
"schemaFile": "./schemas/prod-neptune.json"
}
},
"feedbackUrl": "https://github.com/aws/graph-explorer/issues/new/choose"
}
```
### Environment Variables
Prefix: `GE_`
Env-only (not in config file):
- `GE_CONFIG` — path to config file
- `NODE_ENV` — standard Node convention
Both (config sets base, env overrides):
- `GE_PORT` (default: 80)
- `GE_LOG_LEVEL` (default: info)
- `GE_LOG_FORMAT` (pretty | json, default: pretty)
- `GE_TLS` (default: false)
- `GE_TLS_CERT` — path to cert file
- `GE_TLS_KEY` — path to key file
All existing env vars are dropped.
### Connection Behavior
- Config-file connections are managed/locked: re-applied on every startup, not editable in UI
- Users can create additional connections through the UI (persisted to IndexedDB)
- Connection object key (e.g., `"prod-neptune"`) is the stable ID used for matching across restarts
- Removing a connection from config removes it from the client on next load
- Modifying a connection in config updates it but preserves user's schema/session data
- Authentication is a discriminated union on `type` (currently only `"iam"`, extensible)
### TLS
- Default: HTTP (no TLS)
- `GE_TLS=true` + `GE_TLS_CERT`/`GE_TLS_KEY` → use provided certs
- No auto-generation of self-signed certs
- `setup-ssl.sh` deleted
### Docker
- Base image: `node:24-alpine`
- No shell scripts (all deleted)
- Entrypoint: `node dist/node-server.js`
- Config via volume mount + `GE_CONFIG` env var
- `NEPTUNE_NOTEBOOK` build arg removed
- Serve client at `/`
### Client Config Loading
- Server exposes `GET /api/config` returning the client-relevant subset
- Client fetches once at startup, replaces current `/defaultConnection` mechanism
- Managed connections merged into state; user-created connections preserved in IndexedDB
### Migration
- Hard cut: new major version, old config stops working
- Migration CLI tool (`pnpm migrate-config`) reads old `.env`/`config.json`/`defaultConnection.json` and generates `graph-explorer.config.json`
- Migration guide in release docs
## Testing Decisions
- Config file loading and Zod validation: unit tests for valid configs, malformed configs, missing files, env var overrides
- Connection merge logic: unit tests for managed vs user-owned connections, add/remove/update from config
- Style merge logic: unit tests for global + per-connection + user override layering and reset behavior
- Migration tool: unit tests converting old format to new format
- Docker: manual verification of simplified image build and startup
- Prior art: existing tests in `packages/graph-explorer-proxy-server/src/__tests__/` and client-side tests using `renderHookWithState`
## Related Issues
- Blocked by #1670
- Blocked by #1618
---
> [!IMPORTANT]
> If you are interested in working on this issue, please leave a comment.
> [!TIP]
> Please use a 👍 reaction to provide a +1/vote. This helps the community and maintainers prioritize this request.
Contributor guide
Assessment
This issue has not been assessed yet.