Skip to content

feat: add coordinatorPlugin with auto-register reply-from + helper decorators#6

Merged
marcopiraccini merged 2 commits into
mainfrom
feat/fastify-plugin
May 19, 2026
Merged

feat: add coordinatorPlugin with auto-register reply-from + helper decorators#6
marcopiraccini merged 2 commits into
mainfrom
feat/fastify-plugin

Conversation

@marcopiraccini
Copy link
Copy Markdown
Contributor

Summary

  • One-step Fastify integration: app.register(coordinatorPlugin, { redis, keyPrefix, strategy }) now wires up @fastify/reply-from, constructs the Registry, and exposes both the registry and the helper route-handler factories on app.coordinator. The bare import '@fastify/reply-from' inside the helpers only attaches TypeScript types, so consumers previously had to install and register reply-from separately — a leaky requirement.
  • Helper methods on the decorator (app.coordinator.lookupAndProxy, lookupLockAndProxy, pickAndRegister, lookupAndDeregister, proxyVia) curry away the registry argument and remove most of the boilerplate.
  • Standalone helper imports stay supported for advanced users who manage their own registry and reply-from setup.

Motivation

Discovered while wiring coordinator-demo: without app.register(replyFrom) in the consumer app, the helpers fail at runtime with reply.from is not a function. Documentation mentioned the prereq (README line 187) but the API itself didn't enforce or assist with it. This change removes the leak while keeping the original surface working.

What changed

  • New src/plugin.ts — a fastify-plugin-wrapped plugin (name: '@platformatic/coordinator', fastify: '5.x'). Options:
    • All RegistryOptions (redis, keyPrefix, strategy, cache, requestTimeout) when no existing registry is passed.
    • registry?: Registry — reuse an existing instance (plugin will not close it).
    • replyFrom?: FastifyReplyFromOptions — forwarded to @fastify/reply-from.
    • decorateAs?: string — defaults to coordinator.
    • registerReplyFrom?: boolean — opt-out for hosts that already registered it.
  • src/index.ts re-exports the plugin as coordinatorPlugin and the type augmentation adds app.coordinator: Coordinator.
  • test/plugin.test.ts covers: reply-from registration, decorator wiring, all five helper-as-method paths, existing-registry reuse, decorateAs, opt-out, idempotency.
  • examples/storage-db switched to the new plugin; dropped @fastify/reply-from from its dependencies.
  • docker-compose.yml gains a Postgres service on host port 15432 (matches CI; avoids the common 5432 clash on dev machines). Renamed scripts to test:deps:up / test:deps:down.
  • test/e2e/storage-db.test.ts defaults updated to match (REDIS_URL=:6390, PG_URL=:15432). CI workflow updated to map Postgres host port to 15432.

Backwards compatibility

  • The standalone helper exports (lookupAndProxy, lookupLockAndProxy, pickAndRegister, lookupAndDeregister, proxyVia) are unchanged.
  • Registry, Member, strategies, TTLCache, proxyRequest are unchanged.
  • The only removals are the redundant test:redis:up / test:redis:down script aliases (the docker-compose target is identical to test:deps:up / test:deps:down).

Test plan

  • pnpm test — 73/73 pass
  • pnpm test:e2e — 10/10 pass (after pnpm test:deps:up)
  • pnpm lint — clean
  • Manual: storage-db example builds and runs with the new wiring
  • CI workflow runs green on the PR

🤖 Generated with Claude Code

marcopiraccini and others added 2 commits May 19, 2026 11:26
…corators

Consumers no longer need a separate `npm i @fastify/reply-from` step plus a
manual `app.register(replyFrom)` and `new Registry(...)` to use the Fastify
helpers. `app.register(coordinatorPlugin, { redis, keyPrefix, ... })` now:

- Registers @fastify/reply-from (idempotently, via hasReplyDecorator check)
- Constructs a Registry from the passed options (or accepts an existing one
  via `registry`, in which case the plugin will not close it)
- Decorates the Fastify instance with `coordinator` (renamable via
  `decorateAs`), exposing:
    - `app.coordinator.registry` (the underlying Registry)
    - `app.coordinator.lookupAndProxy(opts)`
    - `app.coordinator.lookupLockAndProxy(opts)`
    - `app.coordinator.pickAndRegister(opts)`
    - `app.coordinator.lookupAndDeregister(opts)`
    - `app.coordinator.proxyVia(resolve, opts)`
- Closes the registry on `app.close()` (only when the plugin created it)

The existing standalone helper imports (`lookupAndProxy`, etc.) keep working;
they remain documented as the advanced/manual path.

Also:
- Adds Postgres to docker-compose.yml (host port 15432) so test:deps:up
  brings up both Redis and Postgres in one step.
- Updates the storage-db example to register the new coordinatorPlugin
  instead of doing reply-from + Registry wiring by hand.
- Updates e2e test defaults to match the new compose ports
  (REDIS_URL=:6390, PG_URL=:15432). CI workflow updated accordingly.
- Renames script aliases to test:deps:up / test:deps:down; drops the
  now-redundant test:redis:* aliases.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous wait step only checked that /pods returned 200, which happens
immediately after the coordinator boots regardless of pod registration. On a
slow runner the smoke script could then race ahead and see count=0, returning
503 on the first POST /tenants/<id>. Now we poll until count >= 3.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@marcopiraccini marcopiraccini merged commit 1a0bd81 into main May 19, 2026
16 checks passed
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.

1 participant