Arc for TypeScript
Arc for TypeScript is a Node.js server implementation of Arc, the Cratis CQRS framework: you declare commands and read models as decorated classes, and Arc hosts them behind one HTTP contract that the existing Arc clients already understand.
Without it, a Node.js backend for an Arc frontend means writing every route, request parser, validation response, and status code by hand, then keeping all of it in step with the frontend. With it, commands and queries run through one pipeline that owns those concerns, the wire behavior follows Arc on .NET, and the proxy generator writes the typed frontend client from your source.
What it looks like
Section titled “What it looks like”@command()export class RegisterTask { @field(TaskId) id!: TaskId; @field(TaskTitle) title!: TaskTitle;
handle(tasks: Tasks): TaskId { tasks.register(this.id, this.title); return this.id; }}
@readModel()export class TaskItem { @field(TaskId) id!: TaskId; @field(TaskTitle) title!: TaskTitle;
@query(service(Tasks)) static allTasks(tasks: Tasks): TaskItem[] { return tasks.all(); }}These excerpts are from the Tasks sample. The sample serves them at POST /api/tasks/registration/register-task and GET /api/tasks/listing/all-tasks, with no routing code. Generated metadata tells Arc that handle() needs the Tasks service. Get started runs it in a few commands.
What you get
Section titled “What you get”- Model-bound commands and read models with typed fields, concepts,
provide(), command context, response value handlers, and operations that Arc executes and compensates. - Validation with
CommandValidator,QueryValidator, andConceptValidatorrules, and a/validateroute for every command. - Queries with argument binding, paging and sorting, renderers and interceptors, and observable queries over server-sent events, WebSockets, and multiplexed hubs.
- Security: authentication handlers including JWT bearer and EasyAuth, roles and policies, identity details, and tenancy.
- Hosting on Node’s own HTTP server, or in Express, Fastify, or Hono.
- Generated proxies for the published
@cratis/arcclient, read from your TypeScript source byarc-proxygenerator. - Testing through the real pipelines with scenarios, and ESLint rules that catch binding mistakes in the editor with code analysis.
- Optional integrations: MongoDB, SQL with Drizzle, OpenTelemetry observability, and the experimental Chronicle event store.
CQRS first, event sourcing optional
Section titled “CQRS first, event sourcing optional”Arc is a CQRS framework. A command can validate input, call a service, write to current-state storage, and return a response without any event log. The core has no dependency on event sourcing or on a database. The Chronicle integration is a separate, experimental package; see CQRS without event sourcing for how that boundary works in Arc generally.
A server for the clients you already have
Section titled “A server for the clients you already have”Arc’s TypeScript client packages, @cratis/arc, @cratis/arc.react, and @cratis/arc.react.mvvm, are built and released from the Arc repository. This project does not replace, rename, or republish them; they are its compatibility target. The server packages are listed in Packages. See Frontend for the client side.
One wire contract
Section titled “One wire contract”Arc on .NET is the reference implementation, and the language-neutral Arc HTTP contract is the specification. Arc for TypeScript matches that observable behavior in idiomatic TypeScript; it does not port .NET mechanics such as attribute reflection or dependency injection containers. A paired suite sends the same requests to a .NET host on Cratis.Arc 22.45.0 and pins the known differences. The largest deliberate one: an HTTP client cannot use X-Allowed-Severity: 3 to let error-severity validation results pass. See the HTTP contract reference.
On the shared Arc pages
Section titled “On the shared Arc pages”The shared Arc pages, such as the tutorial and the scenarios, show a TypeScript tab beside C#, Kotlin, and Java. The TypeScript snippets use the model-bound API and are compiled against this repository’s packages. Read them with these differences in mind:
- Repositories and catalogs in the snippets, such as
AuthorRepository, are application-owned abstract classes that you implement and register withbuilder.services. An abstract class serves as its own service token; an interface does not exist at runtime. - An observable query declares
@query({ observable: true }, ...)in addition to returning an observable source. - In the snippets,
provide()takes no parameters: it resolves services withcurrentServices()and reads the request’s cancellation signal fromcurrentContext(). You can also declare its parameters with@inject(...), as Model-bound commands shows. - A Chronicle command scenario builds reducer-backed and supported flat projection-backed read models from seeded events; projections outside that boundary are rejected. Test those with a kernel scenario.
- The shared validator examples inject an application-owned repository. Arc for TypeScript validators do not take read models as constructor parameters; read one inside an async rule with
readModelForValidation. - Proxy generation on those pages describes the C# and JVM generators. Arc for TypeScript generates the same kind of proxies from TypeScript source; Proxy generation lists what differs.
Releases
Section titled “Releases”Arc for TypeScript is versioned independently of Arc on .NET. GitHub source previews are available; npm publication is disabled. A major release is never made automatically: it requires verified full parity with Arc on .NET and an explicit merge by a maintainer. See Prepare and publish a TypeScript release.
Where to go next
Section titled “Where to go next”- Why Arc for TypeScript: who it is for, what it removes, and when it is the wrong fit.
- Get started: run the Tasks sample and call its command and query.
- Coming from Express and NestJS: compare Arc with the code you write today.
- Hosting overview: choose the standalone host or a framework adapter.
- Architecture: the core, the adapters, and how Arc concepts map to TypeScript.
- Troubleshooting: fixes for the common decorator, discovery, and hosting problems.