Clients & Versioning

Generate strongly-typed RPC clients from running services, and bump versions across many services to trigger CI.

Generating typed RPC clients

imq client generate produces a strongly-typed RPC client from a running service — the service must be up (and Redis reachable) so its interface can be introspected; otherwise generation fails.

The generated client is a file you commit. It is written into your repository like any other source, which is what gives the service's interface a reviewable artifact: change a method signature, regenerate, and the change appears as a diff in the pull request that causes it. The reviewer reads the contract change instead of inferring it. There is no separate schema file to check in, because the committed client is the checked-in contract — generated from the implementation rather than hand-maintained beside it.

imq client generate <name> [path]
Flag Meaning
(positional) name the service's queue name (required) — see below
(positional) path directory to place the client file (default: cwd)
-o, --overwrite overwrite an existing client without prompting
-w, --timeout seconds to wait for the service to respond before giving up (default 30; 0 waits forever)

The name is the queue the service listens on, which IMQService defaults to its own class name (this.name = name || this.constructor.name), and the CLI passes it straight to IMQClient.create(). So it is BillingService, not billing — and in particular it is not the project directory name that imq ctl -s takes. Get it wrong and generation waits for a queue nobody is listening on, then times out.

# from within a project, with the BillingService class running locally
imq client generate BillingService ./src/clients -o

Typical flow during development. The two names in it are different things: -s selects the local service by directory, while generate takes the queue name:

imq ctl start -s billing -c     # bring the service up and wait for readiness
imq client generate BillingService ./src/clients
imq ctl stop -s billing

Bumping versions across many services

imq service update-version releases a new version across one or many services under a directory and pushes, triggering CI builds.

imq service update-version <path> [branch]
Flag Meaning
(positional) path directory containing the services (or a single service)
(positional) branch / -b branch to checkout/use (default master)
-n, --npm-version, --bump bump type: major|minor|patch|prerelease (default prerelease). Unlike imq up, update-version does not constrain the keyword — any value is passed through to npm version.

For each detected service it runs, stopping that service on the first failing step:

git checkout <branch>  →  git pull  →  npm version <n>  →  git push --follow-tags

Detection here is by loading the built module and checking whether any export derives from IMQService (by walking the prototype chain — the service class need not be named *Service). Compare with imq up/imq ctl, which detect by scanning source. Use update-version for a release action against built, committed services; use imq up for dependency maintenance.

# patch-release every service under ./services on the main branch
imq service update-version ./services main -n patch

update-version vs up

imq service update-version imq up
Purpose release/version bump dependency maintenance
Detects services by module load (prototype chain) source scan (extends IMQService/IMQClient)
Touches deps? no yes (ncu -u + reinstall)
Git flow checkout → pull → version → push (optionally) commit → version → push
Branch control -b/[branch] uses current branch

FAQ

Is the generated client committed to the repository?

Yes. imq client generate writes the client into your source tree and it is committed like any other file. That is deliberate: it is what makes an interface change reviewable. Regenerating after a signature change produces a diff in the pull request that causes it, so the contract change is visible at review time rather than discovered at runtime by a caller.

Should I generate clients in CI instead of committing them?

No, and it is usually impractical. Generation introspects a running service, so a CI job would have to start the service and a Redis for every build just to produce a file that is deterministic anyway. Committing it is cheaper, and it keeps the property that matters — the diff. Regenerate locally with imq client generate <QueueName> <path> -o as part of the change that alters the interface, and commit the result alongside it.

Last updated

Read this page as plain markdown — no HTML, no navigation. For pasting into an LLM, or for an agent to fetch.