The Arc CQRS server for Node.js: define commands and queries in TypeScript and serve them over the same HTTP contract as Arc on .NET.
Important
Early source preview; npm packages are not published. This repository contains the server core, adapters for Express, Fastify, and Hono, an optional MongoDB read helper, a bounded client generator, and an experimental, private Chronicle integration. No package is published to npm, and Arc for TypeScript does not have full parity with Arc on .NET. APIs and package names can still change. Check the capability reference before you design around a feature.
Arc is an opinionated CQRS application framework. You declare what your backend can do as commands and queries, and Arc handles routing, input binding, validation, authorization, correlation, tenancy, and the result envelope that Arc clients expect. Arc for TypeScript brings that model to Node.js as idiomatic TypeScript, not as a line-by-line port.
import { ArcServer, defineCommand, defineQuery, validation } from '@cratis/arc.server';
import { mountHono } from '@cratis/arc.server.hono';
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
import { z } from 'zod';
import { realpathSync } from 'node:fs';
import { pathToFileURL } from 'node:url';
const tasks = new Map<string, string>();
const create = defineCommand({
name: 'Create', namespace: 'Tasks', schema: z.object({ id: z.string(), title: z.string() }),
validate: ({ title }) => title.trim() ? [] : [validation('A title is required', ['title'])],
handle: ({ id, title }) => { tasks.set(id, title); return { id }; }
});
const list = defineQuery({
name: 'List', namespace: 'Tasks', schema: z.object({ search: z.string().default('') }),
perform: ({ search }) => [...tasks].filter(([, title]) => title.includes(search)).map(([id, title]) => ({ id, title }))
});
export const server = new ArcServer({ commands: [create], queries: [list] });
export const app = new Hono();
mountHono(app, server);
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
serve({ fetch: app.fetch, port: Number(process.env.PORT ?? 3000) });
}This is a self-contained example, not a copy of the sample. It serves POST /api/tasks/create, POST /api/tasks/create/validate, and GET or QUERY /api/tasks/list. The Tasks sample serves the same routes but keeps its tasks in a singleton TaskRepository service that the handlers declare as a dependency; Get started shows its complete source. The Zod schema is the runtime contract: TypeScript types are erased at runtime, so Arc parses every request with the schema, infers the handler's input type from it, and publishes it as JSON Schema.
| Package | Folder | Contents |
|---|---|---|
@cratis/arc.server |
Source |
ArcServer, defineCommand, defineQuery, the command and query pipelines, explicit services, authentication handlers, identity details, tenancy, results, introspection, OpenAPI, and exportClientManifest. The @cratis/arc.server/testing export provides ArcScenario and shouldHaveRuleFailure. |
@cratis/arc.server.express |
Integrations/Express |
mountExpress for Express 5 |
@cratis/arc.server.fastify |
Integrations/Fastify |
mountFastify for Fastify 5 |
@cratis/arc.server.hono |
Integrations/Hono |
mountHono for Hono 4 |
@cratis/arc.server.codegen |
CodeGeneration |
renderClientManifest, generateClient, and the arc-server-codegen CLI, which turn an exported JSON client manifest into .proxy.ts files for the published @cratis/arc client. Bounded to the shapes in Generate command and query clients. |
@cratis/arc.server.mongodb |
Integrations/MongoDB |
MongoReadModels, an optional tenant-aware read helper for queries, for the mongodb 6 driver |
@cratis/arc.server.chronicle |
Integrations/Chronicle |
Experimental and private. defineChronicleCommand, which appends events returned from a command. The pinned Chronicle TypeScript SDK 6.2.0 does not load in native Node.js; this adapter has not been verified against a live kernel. |
Every package manifest is at version 0.3.0. That is the version of this source preview, not an npm release, and the Chronicle package stays private. The packages ship ES modules only, and schemas use Zod 4. The core, host adapter, and MongoDB packages need Node.js 22 or later. The root workspace needs Node.js 22.19 or later, because it installs the Chronicle SDK; Node.js 24 LTS is recommended.
Until the packages are published, run the sample from a clone:
git clone https://github.com/Cratis/Arc.TypeScript.git
cd Arc.TypeScript
corepack enable
yarn install
yarn build
yarn workspace @cratis/arc.server.sample.tasks startThe sample listens on port 3000 on every network interface. Get started walks through calling it and explains every line.
Supported, with specs in this repository: commands and queries with Zod schemas, validation-only requests, validators and filters, declared and per-request authorization, authentication handlers, correlation IDs, execution scopes, in-memory and provider paging, exception redaction, introspection, OpenAPI, the three host adapters, and the MongoDB read helper.
Also supported, each one explicit or opt-in:
- Services. You register each service against a
serviceTokenassingleton,scoped, ortransient, and a definition declares the tokens it needs inhandlerDependenciesorvalidatorDependencies. Arc creates a scope for every HTTP or direct call and disposes the services it created there; singletons are disposed byawait server.dispose(), or by disposing aServiceRegistryyou passed in yourself. There is no automatic discovery and no integration with an application's dependency injection container. - Identity.
identityDetailsregistersGET /.cratis/meand sets a client-readable display cookie. The cookie is for display only; it is not a credential. - Host principals.
nativePrincipal: trueaccepts a principal your host framework has already verified, passed through an explicit adapter callback. It cannot be combined with Arc authentication handlers. - Tenancy. Besides the tenant header and
resolveTenant, thetenancyoption selects ordered header, query, claim, fixed, or subdomain sources, with optionalrequiredand membership-claim checks. - Testing.
@cratis/arc.server/testingruns specs through the real command, query, and HTTP pipelines. - Generated clients, bounded. Declare an explicit
clientOutputshape on each command and query, export a version 1 JSON manifest withexportClientManifest, and generate.proxy.tsfiles from it. The CLI reads only that JSON, never your application, and takes an absolute manifest path and an existing absolute output directory. The proxies are tested against@cratis/arc22.19.1,@cratis/fundamentals7.19.3, andrxjs7.8.2 in a strictBundlerfrontend withskipLibCheck: false; consumers that compile withNodeNextare not supported, because the published declarations use extensionless imports. The client's default origin is empty, so callsetOriginwith your server's origin on each instance. Zod defaults, transforms and refinements, nullable command fields, scalar query results, nested DTOs, observable queries, and React hooks are not generated. See Generate command and query clients.
A paired suite checks 33 bounded HTTP cases against Arc on .NET 22.22.0 and pins the known differences. That is not full parity.
Not implemented:
- Observable queries over HTTP, server-sent events, or WebSocket.
- Discovery of commands and queries by convention. You register every definition with
ArcServer, and client generation reads only the output shapes you declare, not a full type graph. It does not match Arc's .NET proxy generator. - Named authorization policies, SQL integrations, command operations and effects, and observable query test scenarios.
The Chronicle integration stays experimental and private: the pinned Chronicle TypeScript SDK 6.2.0 does not load in native Node.js, and this adapter has not been verified against a Chronicle kernel.
The capability reference lists every Arc feature family, its status, and the deliberate differences from Arc on .NET.
- Get started: run the Tasks sample and read it line by line.
- Host Arc in Express, Fastify, or Hono
- Call Arc from code
- Validate and authorize commands and queries
- Decide command outcomes
- Bind query arguments, page, and sort
- Configure the server
- Compose services and test pipelines
- Read models from MongoDB
- Generate command and query clients
- Append Chronicle events from commands (experimental)
- Capability reference
- Architecture
- Arc HTTP contract: the wire protocol every Arc backend speaks.
@cratis/arc is Arc's existing TypeScript client runtime, used by generated proxies and @cratis/arc.react to call an Arc backend. It is built and released from the Arc repository.
This repository builds the server side under its own @cratis/arc.server package names. It does not replace, rename, or republish @cratis/arc or any other Arc package. The existing client is the compatibility target for this server's wire behavior. The server core does not depend on @cratis/arc or on browser code; only the proxies that @cratis/arc.server.codegen writes import it, in your frontend.
Arc is a CQRS framework first. A command can validate input, call a service, write to current-state storage, and return a response without an event log, and the server core has no dependency on event sourcing or a database. Event sourcing comes from Chronicle as an optional integration. Here that integration is experimental and private: the pinned Chronicle TypeScript client 6.2.0 does not load in native Node.js, and this adapter has not been verified against a Chronicle kernel.
Arc for TypeScript is a framework library, not an application. Read CONTRIBUTING.md before opening a pull request, and start with an issue or a conversation on Discord for anything larger than a small fix.
Report security issues privately, as described in SECURITY.md.
| Path | Destination |
|---|---|
| Questions and discussion | Cratis Discord |
| Bugs and feature requests | GitHub Issues |
| Arc documentation | www.cratis.io/arc |
| Security reports | SECURITY.md |
| License | MIT |
This project is part of Cratis: free, MIT-licensed tools for building event-sourced and CQRS applications.
- Arc: the CQRS framework for ASP.NET Core, and home of the TypeScript client and React packages. Docs
- Arc for Kotlin and Java: Arc on Spring Boot.
- Chronicle: the event-sourcing database and runtime, with a TypeScript client. Docs
- Components: React components aligned with Arc patterns. Docs
- Fundamentals: shared primitives for .NET and TypeScript.
- Samples: runnable event sourcing and CQRS samples.
- AI: free AI skills and rules for building with the stack.