Skip to content

docs: add Express graceful shutdown guide with pinned behaviour tests - #661

Open
paulolubanwo391-cloud wants to merge 1 commit into
Anchor-kit:mainfrom
paulolubanwo391-cloud:docs/610-express-graceful-shutdown
Open

paulolubanwo391-cloud wants to merge 1 commit into
Anchor-kit:mainfrom
paulolubanwo391-cloud:docs/610-express-graceful-shutdown

Conversation

@paulolubanwo391-cloud

Copy link
Copy Markdown

Summary

Adds docs/graceful-shutdown.md: how an Express host should close its HTTP server and release Anchor-Kit resources in the correct order, with a working SIGINT/SIGTERM example.

Closes #610.

Why

docs/plugin-lifecycle.md states the host owns shutdown, but never showed how to combine it with closing the host's own HTTP server. Hosts were left to guess the ordering, and the two failure modes (a listener that outlives cleanup, and a forced exit that truncates a drain) are both easy to get wrong.

The ordering matters: anchor.shutdown() stops the watchers and the queue, so exiting first can lose work the queue already accepted.

The guide

  • Why drain-then-release, and what breaks if you invert it.
  • A complete Express example: a request listener that counts in-flight work, a server.close() drain, then await anchor.shutdown(), then exit.
  • A "What anchor.shutdown() actually does" section covering the real semantics.
  • A note on why the example does not call process.exit(0) unconditionally, and why it exits non-zero on cleanup failure.

Tests

tests/core/graceful-shutdown.test.ts — 8 cases that pin every behavioural claim in the guide, so the documentation cannot drift away from the implementation:

  • releases the database on shutdown
  • safe to await shutdown more than once (a second signal must not disconnect twice)
  • deduplicates concurrent shutdown calls
  • no-op when never initialized
  • propagates a database disconnect failure to the host — this is why the guide tells hosts to log and exit non-zero rather than swallow it
  • can be reinitialized and shut down again
  • stops background jobs as part of shutdown
  • getExpressRouter() is unavailable after shutdown

I confirmed these bite: removing the in-flight shutdownPromise guard in src/core/factory.ts fails the dedup test.

Two corrections found by type-checking the example

I compiled the documented example against the real API rather than assuming it was right, and it did not type-check at first:

  1. The in-flight counter is a 'request' listener on the Node server, which has no next callback — the original draft passed one and failed to compile. Express middleware is what supplies next, and the router is mounted above this listener.
  2. getExpressRouter() returns ExpressLikeMiddleware, so the example mounts it directly instead of casting to express.RequestHandler, matching example/express-app.ts.

Verification

Check Result
bun test 865 pass, 0 fail (was 857 — 8 new)
bun run typecheck (tsc --noEmit && eslint . --max-warnings 0) Clean
prettier --check Clean
Documented example Compiles under tsc (verified with a temporary harness, since an unused snippet in a .md file is not otherwise type-checked)

No production code changed, so no runtime behavior is affected. No new dependencies.

Notes for review

  • The example uses config and await anchor.init() in the way the existing example/express-app.ts does; I kept the snippet focused on shutdown rather than restating full config setup.
  • I added one line to the README docs list so the guide is discoverable. Happy to drop that if docs-index changes should be separate.

Lifecycle docs described that a host owns shutdown but never showed how
to close the HTTP server alongside anchor.shutdown(), so hosts had no
copy-pasteable ordering to follow.

Adds docs/graceful-shutdown.md covering the drain-then-release order, with
an Express example covering both SIGINT and SIGTERM.

The behavioural claims in the guide are pinned by
tests/core/graceful-shutdown.test.ts so the documentation cannot drift
away from the implementation: shutdown waits for a pending init, repeated
and concurrent calls clean up once, it is a no-op before init, it
propagates a database disconnect failure, and the router is unusable
afterwards.

Two details worth noting, both found by type-checking the example
against the real API rather than assuming:

- The in-flight request counter is a 'request' listener on the Node
  server, which has no `next` callback; passing one does not type-check.
  Express middleware is what supplies `next`, and the router is mounted
  above this listener.
- getExpressRouter() returns ExpressLikeMiddleware, so the example mounts
  it directly rather than casting, matching example/express-app.ts.
@drips-wave

drips-wave Bot commented Sep 30, 2026

Copy link
Copy Markdown

@paulolubanwo391-cloud Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add graceful shutdown guidance for Express hosts

2 participants