For day‑to‑day development, run the app inside Docker, not via box server start on your host machine.
-
Preferred: from the project root (
ServePointdirectory), start the full stack with:docker compose --env-file .env.dev -f docker/docker-compose.yml up
This builds the app image, starts the app and Postgres containers, and wires all environment variables from
.env.dev. -
Not preferred: running
box server startdirectly on your host. That path is only for troubleshooting; it does not match the containerized production‑like environment, and may behave differently (Java, paths, CF packages, etc.).
- Docker Compose (
docker/docker-compose.yml) starts the app and a local PostgreSQL container. Use it with.env.devwhen you want a local, dev-only database so migrations, seeds, and experiments don’t touch the remote Render database. This is the recommended setup for day‑to‑day development. - Remote database (e.g. Render): For deployment or for testing against the live DB, run only the app container (e.g.
docker build -t servepoint -f docker/Dockerfile .thendocker run --env-file .env.deploy -p 8080:8080 servepoint). Render builds from the Dockerfile only and does not use docker-compose. You do not need docker-compose for Render deployment; you do need it (or another local Postgres) if you want an isolated local database for development.
This app uses:
- commandbox-dotenv (preinstalled in the Docker image) to load environment variables.
- A familiar
.env.devfile in the project root for secrets and configuration (database credentials, etc.).
When you run the app via Docker (docker compose --env-file .env.dev -f docker/docker-compose.yml up), those .env.dev values are injected into the containers and used by CF/ColdBox; you should not commit .env.dev to version control.
- On application startup, the ORM can automatically seed the database with an administrator user and sample demo data.
- This is controlled by the
SERVEPOINT_AUTO_SEEDenvironment variable:- If unset or blank, seeding runs by default.
- If set to
1,true,yes, oron(case-insensitive), seeding runs. - Any other value disables automatic seeding.
- For Docker workflows, add
SERVEPOINT_AUTO_SEEDto your.env.devfile so it is injected into the container. - For local CommandBox workflows, set
SERVEPOINT_AUTO_SEEDin your shell environment before runningbox server start.
- All persistent entities (
Users,Cases,Document,LogEntry) extendcborm.models.ActiveEntityand are mapped according todesign/mermaid/data-model.md. - Required vs optional fields, uniqueness rules (e.g.,
Users.emailunique), and high-level index expectations are documented indesign/mermaid/data-model.mdand should be treated as the contract for migrations and DB schema.
- The database schema is managed only by cfmigrations. ORM is used to validate that the existing schema matches the entity mappings; it does not create or alter tables. All schema changes (new columns, tables, indexes, etc.) go in new migration files under
resources/database/migrations/. - ORM_DBCREATE: The value is read from the
ORM_DBCREATEenvironment variable (in bothApplication.cfcandconfig/Coldbox.cfc). Allowed values:validate,update,dropcreate,none. Default isvalidate. Usevalidatefor production and for Render so ORM only checks the schema; use migrations for any schema changes. - Startup order (
Application.cfconApplicationStart):- ColdBox is bootstrapped.
runMigrations()runs:migrationService.install()(ensures thecfmigrationstracking table exists), thenmigrationService.up()(runs any pending migrations).- ORM is initialized (
ormGetSessionFactory()), so the schema is in place before validation. - Optional seeding via
SeedServicewhenSERVEPOINT_AUTO_SEEDindicates seeding should run.
- cfmigrations:
- Migrations live under
resources/database/migrations/as timestamped CFCs withup()/down()methods. - The default manager is configured in
config/Coldbox.cfcto target theservepointdatasource with a Postgres grammar.
- Migrations live under
- Developer workflow:
- For local work, ensure CommandBox dependencies are installed (
box install), then start the stack via Docker as usual; migrations will run automatically on first request/startup. - Any schema-changing feature (new columns, indexes, archive flags, etc.) must add a new migration in
resources/database/migrations/rather than relying ondbcreate.
- For local work, ensure CommandBox dependencies are installed (
- Render database reset: To wipe the Render Postgres database and run the single bootstrap migration from a clean state, follow the steps in RENDER_DATABASE.md.
- Soft archive only: Cases can be archived at the business level. Data stays in the main tables; the case’s
archived_at(and optionalarchived_by,archive_reason) mark it as archived. - Default query behavior: Case lists used by the app return only active cases by default (
archived_at IS NULL). UseCaseService.listActive()for the default list andCaseService.listAll( includeArchived = true )when archived cases should be included (e.g. admin or reporting). - Archive and restore: Use
CaseService.archiveCase( caseId, userId, reason )andCaseService.restoreCase( caseId, userId ). These optionally create aLogEntryfor audit. Documents and log entries have no separate archive state; visibility follows the case’s archive flag. - A future hard archive (separate archive tables or export to storage) is out of scope for this phase.
- "graphqlclient not found" with CF 2025:
- When starting the app with ColdFusion 2025, the first request (when the app first loads) can trigger a
ModuleNotAvailableException: The graphqlclient package is not installederror. - Cause: CF 2025's
ApplicationSettings.loadAppDatasources()callsServiceFactory.getGraphQLClientService()when resolving application scope; if the optional graphqlclient package is not installed, it throws. - This app does not use GraphQL; it appears that the error originates within ColdFusion 2025 itself.
- Workaround: In
Application.cfconError(), we detect this exception and issue a 302 redirect to the same URL. The second request succeeds because the application scope is already resolved. See GitHub issue #10.
- When starting the app with ColdFusion 2025, the first request (when the app first loads) can trigger a