Get started

What Carrick covers

What Carrick scans, the requirements a repo needs to scan cleanly, the framework and protocol story, and the cases that fall outside MVP scope.

Carrick statically scans TypeScript services to extract operations, outbound client calls, and request/response type definitions. It maintains an index of cross-service interfaces that powers diagnostics in your editor, your AI coding agent, and your pull requests.

Supported protocols

Carrick indexes operations and communication patterns across four primary protocols:

REST and HTTP

Carrick extracts route handlers on the producer side and matching client calls (such as fetch, axios, got, and ky) on the consumer side. It pairs producers with consumers based on HTTP method and path patterns, resolving complete TypeScript request and response types for both sides.

GraphQL

Carrick parses Schema Definition Language (SDL) schemas (.graphql, .gql, and gql/graphql tagged template literals) to identify producer fields, and scans executable documents (queries and mutations in .graphql files or tagged templates) to identify consumer operations. Extraction uses a deterministic parser rather than model inference.

Code-first schemas, persisted query manifests, and Relay compiled artefacts are supported when a generated schema.graphql is committed to the repository. A printed schema in a build folder, or outside the service’s own directory, is read when the service names it in graphqlSchemas. Documents written against a committed schema that the repository does not serve, such as a downloaded vendor schema, are not indexed as calls. See GraphQL documents for another team’s API.

Where a GraphQL call is indexed

A document written in a .graphql or .gql file and compiled into a typed declaration (OrdersDocument) is sent from the code that passes that declaration to a client, such as useQuery(OrdersDocument). Carrick indexes the operation’s fields at each of those calls, resolving the import through relative paths, tsconfig path aliases and workspace packages. An operation that no call executes stays indexed at its line in the document file. A gql template written in source stays indexed where it is written.

WebSockets

Carrick indexes bidirectional realtime events by event name and direction:

  • Producers: Event listeners such as socket.on("event_name", ...).
  • Consumers: Event emitters such as socket.emit("event_name", ...).

While Socket.IO is the most widely tested client, Carrick supports any realtime library using standard on/emit call shapes. Event names must be string literals. Dynamic event names, custom namespaces (io.of(...)), and reserved lifecycle events (connect, disconnect) are excluded from contract tracking.

Pub/sub

Carrick extracts topic-based messaging operations across message brokers including Kafka, NATS, Redis pub/sub, and BullMQ:

  • Subscribers: Handlers registered for a specific topic act as producers.
  • Publishers: Client calls sending messages to a topic act as consumers.

Operations match on literal topic strings. When a topic payload is decoded using a declared TypeScript type, Carrick extracts and compares the payload shape.

Framework support

Carrick detects REST frameworks by analyzing imported symbols and call shapes rather than relying strictly on package manifest names. It supports major Node and Deno web frameworks:

  • Express: Comprehensive producer and consumer extraction across Express 4 and Express 5, including path parameters, query parameters, request bodies, and response types.
  • Koa, Fastify, Hapi, and NestJS: Supported with dedicated end-to-end test fixtures validating route extraction and contract matching.
  • Hono: Supported across standard routing patterns.
  • Next.js App Router: Carrick reads route handlers in app/**/route.ts (or src/app/**/route.ts) from the file layout. The directory chain gives the route path, including dynamic segments ([id]), catch-all segments ([...slug]), and route groups, and each exported method function (GET, POST, and so on) is one operation.
  • Custom routers: In-house routers following Express-style routing conventions (app.get, router.use, route decorators) are extracted automatically.

Repository requirements

Repositories must meet the following criteria to scan cleanly:

TypeScript source

Carrick relies on TypeScript compiler APIs to resolve request, response, and payload types. While JavaScript repositories can produce endpoint route maps, their parameter and payload types resolve to unknown.

Project manifest

Node projects must define a package.json. Deno projects must define a deno.json or deno.jsonc and require Deno 2.9.4 or newer on PATH, which the GitHub Action configures automatically.

During scans, Carrick prepares dependencies using the project’s lockfile (npm, pnpm, Yarn, Bun, or deno install --frozen --node-modules-dir=none) with lifecycle scripts disabled. You can disable this step by setting install-dependencies: false on the GitHub Action. See Dependencies.

Statically discoverable routes

Routes must be defined as string literals or resolvable expressions in source code. Routes constructed dynamically at runtime from databases, external configuration files, or arbitrary runtime loops cannot be discovered statically.

Configuration and initial scan

A carrick.json at a repository’s root defines service boundaries and classifies environment variables. For a repository without one, Carrick writes and keeps the configuration itself. Carrick builds the initial index after you install the GitHub App, and the optional Carrick GitHub Action updates the index automatically on default-branch pushes.

Monorepos

Carrick natively supports both single-service repositories and multi-service monorepos:

  • Workspace monorepos: Monorepos managed with npm, pnpm, Yarn, or Deno workspaces are split into distinct services automatically based on workspace package manifests.
  • Single-package monorepos: Repositories containing multiple services under a single root package.json define a services array in carrick.json. Carrick scans, type-checks, and indexes each service independently, evaluating contracts between services within the same repository.

Limitations and unsupported patterns

The following patterns fall outside the scope of static analysis:

Non-TypeScript languages

Carrick does not scan Python, Go, Rust, Java, or Ruby services. In polyglot environments, Carrick indexes the TypeScript services while non-TypeScript services remain unindexed.

Deno runtime scope limits

Carrick resolves modules using Deno’s standard module graph (deno.window and deno.ns scopes). Worker scopes, arbitrary runtime library combinations, and ambient Node types omitted by deno types require explicit declarations.

Binary and RPC protocols

gRPC, tRPC over WebSockets, and custom binary RPC protocols are not indexed.

Advanced broker queue topologies

While topic names in message queues like BullMQ are indexed for pub/sub matching, broker-specific operational semantics (such as consumer groups, delayed job retries, exchange bindings, and dead-letter queues) are not modelled.

Dynamic runtime routing

Routes registered through dynamic runtime execution cannot be indexed:

  • Routes evaluated from remote feature flags or database queries at startup
  • Routes registered in dynamic loops with non-literal string concatenation
  • Plugin architectures that register handlers after application bootstrap

Pre-compiled and generated code

Carrick scans original TypeScript source files. It does not scan .d.ts-only packages without source, pre-compiled bundles, or build output in .gitignore unless generated source files are checked into version control.

  • Introduction outlines Carrick’s three core surfaces and indexing model.
  • carrick.json explains service configuration, monorepos, and environment variable classification.