Skip to content
AI-assisted, verified against source

Watch Mode

Configuration for watch mode — one feature, two flavors selected by the --test flag:

CommandFlavorWatchesOn change
npgsqlrest ... --test --watchTest watchtest files, included fixtures/profiles, endpoint SQL files, the databasechanged test re-runs alone; endpoint or database change rebuilds endpoints in-process and re-runs everything
npgsqlrest ... --watchServer watchthe SQL file source tree, the configuration files, the databasethe server restarts (~1s)

Watch is interactive/dev-only. In both flavors a broken SQL file cannot kill the session — SqlFileSource.ErrorMode is forced from Exit to Skip while watching.

Overview

The Watch section is top-level (a sibling of NpgsqlRest, not nested inside it), because it drives both flavors:

json
json
{
  "Watch": {
    "Enabled": false,
    "DatabasePollingInterval": "2s"
  }
}
SettingTypeDefaultDescription
EnabledboolfalseTurn watch mode on. The --watch CLI flag is the shorthand for this setting.
DatabasePollingIntervalstring"2s"Poll the database for routine changes; 0 disables polling.

Enabled

Turns watch mode on; --watch on the command line is equivalent. The flavor is chosen by --test:

sh
sh
npgsqlrest ./config.json --watch               # server watch
npgsqlrest ./config.json --test --watch        # test watch
npgsqlrest ./config.json --watch:enabled=true  # same as --watch (standard config override syntax)

Server watch needs something to watch: an enabled SQL file source, database polling (on by default), or both — with neither, --watch exits with an error. Test watch always has its test files to watch.

DatabasePollingInterval

Routine-source endpoints (functions and procedures) have no files to watch — so watch mode polls the database instead, with perfect fidelity: the poll runs the same routine discovery query the endpoint source uses, with the same configured filters (schema/name/language includes and excludes), hashed server-side into a single value on a dedicated non-pooled connection. If the hash changes, the discovered endpoints changed — by definition.

Detected (because they change the discovery result):

  • create / create or replace / drop / alter of functions and procedures, including GRANT/REVOKE
  • COMMENT ON — i.e. annotation changes
  • changes to the composite types and tables used as parameter or return types (alter table users add column reshapes a returns setof users endpoint even though no function changed)

Never triggers (because the discovery query doesn't read them): unrelated tables, temp objects, data changes.

Accepts "2s", "500ms", "1m", a plain number of seconds, or "hh:mm:ss"; 0 disables polling. Polling is automatically inactive when the routine source is disabled (NpgsqlRest.RoutineOptions.Enabled: false).

This makes a routines-only project fully watchable: run npgsqlrest ./config.json --watch, then create or replace a function in psql — the endpoint is live about two seconds later, annotations included. In test watch, a database change shows as — change detected (database) — followed by an endpoint rebuild and a full rerun; the runner re-baselines after every rerun so self-inflicted changes never re-trigger.

Server watch behavior

The process becomes a small supervisor that spawns itself as a child server and watches for changes; the child runs the completely normal server pipeline — including code generation (TypeScript client, HTTP files, OpenAPI regenerate on every restart), so dev is production behavior (the same model as dotnet watch).

EventResult
.sql change under the source treerestart (files matching SkipPattern — test files — are ignored)
configuration file changerestart with the new configuration
database routine change (polling)restart
broken SQL filerestart; the error is logged, that endpoint drops, everything else keeps serving
child crashes/exits on its ownsupervisor waits for the next change (no crash-looping)
Ctrl+C / SIGTERM (docker stop)child stopped gracefully, both processes exit, port freed
supervisor killed hard (SIGKILL)the child detects the vanished parent and exits by itself — no orphan holding the port

Graceful child stop uses SIGTERM on Linux/macOS; on Windows the child is hard-killed (nothing needs teardown in a dev server). Works in every distribution: AOT executables, framework-dependent dotnet NpgsqlRestClient.dll, and both Docker image flavors.

Docker Desktop bind mounts

Where file events don't cross the filesystem boundary (Docker Desktop volume mounts, network shares), set the ecosystem-standard DOTNET_USE_POLLING_FILE_WATCHER=1 to switch the file watcher to a 1-second polling scan. This affects file watching only — database polling is unaffected.

Test watch behavior

Described in detail in the Testing Guide and the Test Runner configuration: a changed test file re-runs alone; endpoint file and database changes rebuild endpoints in-process (with a +/- endpoint delta report) and re-run everything; Teardown runs once, on exit — including on Ctrl+C and SIGTERM.

Comments