Skip to content
TypeWire
THE @tahanabavi/* ECOSYSTEM

Typed contracts.
Any transport.

Define your API once as a Zod contract — then validate it end-to-end across HTTP, WebSocket, and the server. One source of truth, wired to everything.

zsh · ~/app
$

Reads your project, asks what it needs, wires it up.

  • 12 packages
  • 12 on npm
  • 8 with zero runtime deps
  • Zod 4
  • MIT
  • npmNodeBunDeno
contracts.ts
// contracts.ts
export const contracts = {
user: {
getUser: {
method: "GET",
path: "/users/:id",
request: z.object({ path: z.object({ id: z.string() }) }),
response: z.object({ id: z.string(), name: z.string() }),
},
},
} as const;
typefetchclient

await client.modules.user.getUser({ path: { id } })

typewire-nestjsserver

@TypeFetchEndpoint(contracts.user.getUser)

query-corecache

useQuery(contracts.user.getUser, { path: { id } })

type-devtoolsinspector

one timeline, every wire

one object · four consumers

// WHAT IS TYPEWIRE

One contract. Client, server, cache and devtools all read it.

You describe an endpoint once — method, path, request, response. Every package in the family consumes that same object.

01

Define it once

A plain object with Zod schemas, imported by the frontend and the backend.

export const contracts = {
user: {
getUser: {
method: "GET",
path: "/users/:id",
request: z.object({
path: z.object({ id: z.string() }),
}),
response: z.object({
id: z.string(),
name: z.string(),
}),
},
},
} as const;
02

Consume it on the client

Input and output are validated with the same schema that types them.

const user = await client.modules.user
.getUser({ path: { id: "123" } });

// user: { id: string; name: string }
03

Implement it on the server

The route is wired from the contract, so it cannot drift from the client.

type Input = InferRequest<
typeof contracts.user.getUser
>;

@TypeFetchEndpoint(contracts.user.getUser)
async getUser(@ContractInput() input: Input) {
return { id: input.path.id, name: "Taha" };
}

The route can't drift from the client. The client can't drift from the types. The types can't drift from what's validated at runtime.

The server renames a field

WITHOUT TYPEWIRE
// server, last Tuesday
res.json({ id, fullName })
// client, still says
type User = { id: string; name: string }
const { name } = await getUser(id)
→ undefined, at 2am, in production
→ typecheck passed. tests passed.
WITH TYPEWIRE
// contracts.ts — the only edit
response: z.object({
id: z.string(),
fullName: z.string(),
})
// client, next typecheck
→ Property 'name' does not exist
→ caught before it ran

// WHY TYPEWIRE

Built so the pieces cannot drift apart

Transport-agnostic devtools

REST, GraphQL, gRPC and WebSocket traffic land in one live timeline — each row badged by the wire it used, each failure named by the same taxonomy. Hover a wire to isolate it.

LIVE · 4 WIRES
WIREENDPOINTDURATIONSTATUS
HTTPGET user.getUser142ms200
GQLQUERY user.listUsers268ms200
gRPCUNARY billing.getPlan1.8stimeout
WSEMIT chat.sendMessage18msack ok
HTTPPOST user.updateUser312msvalidation
GQLQUERY search.query204mshttp_4xx
WSON chat.messageAdded6msnetwork
TRANSFER1.4 / 2.2 MB

overrides: force mock · force error · add latency · swap schema — without touching the contract

One source of truth

The contract is a plain object. Client, server, cache and devtools all read the same one — nothing to keep in sync.

Runtime-validated, not just typed

Every request and response is checked with Zod, so a wrong shape fails loudly instead of corrupting state silently.

Framework-agnostic core

The query engine is pure logic behind a subscribe/getSnapshot contract — React today; Vue, Angular and Svelte by design.

Zero-cost when unused

Instrumentation, overrides and every advanced feature are additive — the base clients behave exactly as before without them.

Zero runtime dependencies, asserted

Not claimed in a README: scripts/assert-no-deps.mjs fails CI the moment a dependency appears in the core.

// PACKAGES

12 packages, one contract

12 published on npm, 0 built and tested here awaiting a first publish. Everything below is read from each package's own manifest and README.

12 of 12 packages

Framework-less, dependency-free capability permissions: one shared bit map, evaluated identically on client and server, with layered resolution, codecs, a lock file and contract-linkable requirements.

0 deps5.90 KB gz

A strongly typed TypeScript HTTP client powered by Zod contracts, middleware, retries, mock data, contract testing, CLI tooling, and report generation.

HTTP
0 deps21.5 KB gz6 releases

Contract-driven, type-safe Socket.IO client — one Zod contract validated in both directions, with acks, middleware, queueing and a devtools instrumentation seam.

WebSocket
0 deps9.00 KB gz2 releases

React inspector panel for the TypeWire ecosystem — one timeline for HTTP and WS, a live query cache, a runtime override editor, and theme/sound settings.

HTTPGraphQLgRPCWebSocket
1 release

Transport-agnostic inspector bridge for the TypeFetch ecosystem (timeline, overrides, generic Source protocol).

HTTPGraphQLgRPCWebSocket
0 deps4.00 KB gz1 release

NestJS integration for the TypeWire ecosystem — serve one set of Zod contracts over every wire your client speaks: REST/HTTP, gRPC (Connect JSON), GraphQL and typesocket WebSocket gateways, with request/response validation on all of them.

HTTPGraphQLgRPCWebSocket
0 deps1 release

Framework-agnostic query engine for TypeFetch contracts (cache, dedup, staleness, mutations, invalidation).

0 deps8.30 KB gz

React adapter for the TypeFetch query engine — useQuery, useMutation, TypeFetchProvider.

1.00 KB gz

Command-line tools for TypeWire contracts — contract test runner, scaffolding, and reports.

1 release

Field-level request/response encryption middleware for TypeFetch contracts (AES, DES, RSA, Base64, custom).

2.30 KB gz1 release

GraphQL transport for TypeFetch contracts — one client, one middleware chain, and selection sets generated from your Zod response schema.

GraphQL
0 deps5.40 KB gz1 release

gRPC transport for TypeFetch contracts — Connect unary JSON out of the box, binary grpc-web behind a codec seam, zero dependencies.

gRPC
0 deps4.40 KB gz1 release

Published is the version on npm right now — this page asks the registry, it does not repeat a number written by hand. Unpublished means built and tested in the repo, awaiting its first publish: not an unfinished package, one that has not been given a version number yet.

How the monorepo fits together

Every one of these keys on the same "module.member" id — which is why adding a transport needed no change to query-core, devtools or the React adapter.

contracts.tstypewire-clitypewire-nestjstypefetch-grpctypefetchtypesockettypefetch-graphqltype-permissionquery-coretype-devtoolstypefetch-react
— solid = consumes▬ thick = transport·· dotted = optional link
Compare all packages →

// TRANSPORTS

The contract stays. Only the wire changes.

Same object, four call sites. The tab below switches the transport, not the source of truth.

CONTRACT UNCHANGED
contracts.ts
// contracts.ts — fixed
export const contracts = {
user: {
getUser: {
method: "GET",
path: "/users/:id",
request: z.object({ path: z.object({ id: z.string() }) }),
response: z.object({ id: z.string(), name: z.string() }),
},
},
} as const;
RESTvia in the core
const client = createClient({ contracts, transport: "http" });

const user = await client.modules.user.getUser({
path: { id: "123" },
});
// GET /users/123 — validated against response

The http adapter ships inside typefetch — nothing extra to install.

The GraphQL document is generated from the Zod schema

So the selection set cannot drift from the response type — the one thing no other GraphQL client can do.

response: z.object({
id: z.string(),
name: z.string(),
})
query getUser($id: ID!) {
user(id: $id) {
id
name
}
}

Three failure shapes, one error.kind

Handle failures once, whatever the wire produced.

  • HTTPAbortError: signal timed out
  • gRPCcode: DEADLINE_EXCEEDED
  • GQLerrors[0].extensions.code
kind: "timeout"

// LIVE FROM THE REPO

The guarantees, as they actually stand

Every number here is fetched from GitHub and npm at build time, revalidated hourly. Where an API declines to answer, the card says so rather than showing a zero.

Repository

stars
4
forks
1
watchers
1
open issues
0

last push 49 minutes ago

Commit activity · 52 weeks

59 commits
0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits6 commits0 commits3 commits0 commits0 commits0 commits0 commits3 commits0 commits1 commit2 commits2 commits0 commits0 commits2 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits0 commits2 commits7 commits5 commits0 commits2 commits24 commits
lessmore

Latest release

@tahanabavi/typewire-nestjs@4.0.0

2 hours ago

Release notes ↗

npm downloads · last week

868

  • typefetch236
  • type-devtools-core140
  • type-devtools123
  • typewire-cli63
  • typefetch-grpc63
  • typefetch-encryption61
  • typesocket60
  • typefetch-graphql57

Contributors

TahaNabavigithub-actions[bot]Th3Scp

Gzipped size budgets

CI ceilings, not measurements — a dashed track means the limit is asserted but no published figure exists to fill it.

  • type-permission≤ 6 kB
  • typefetch≤ 21 kB
  • typesocket≤ 9 kB
  • type-devtools-core≤ 4 kB
  • typefetch-query-core≤ 8 kB
  • typefetch-react≤ 1 kB
  • typefetch-encryption≤ 2 kB
  • typefetch-graphql≤ 5 kB
  • typefetch-grpc≤ 4 kB
All workflow runs ↗

// SUPPORT

Where to take a problem

Three routes, each with a different front door.

GitHub Issues

A bug, or a feature you want.

For anything non-trivial, open an issue first so the approach is agreed before you invest time in a PR.

Open an issue

GitHub Discussions

A question, a pattern, or something you built.

Usage questions and design discussion live here rather than in the issue tracker.

Start a discussion

Security advisories

A vulnerability.

Please do not report security vulnerabilities through public issues, discussions, or pull requests. Use GitHub's private reporting flow.

Report a vulnerability

Frequently asked

// BUILT FOR AGENTS

A contract is the best possible input for an AI agent

Machine-readable by construction, validated at runtime, and documented for agents in the repo itself.

Your agent already understands TypeWire

  • The contract is a plain object — an agent can read it, diff it and generate against it with no codegen step.
  • AGENTS.md ships in the repo: layout, a per-change definition of done, and the docs conventions an agent must follow.
  • Runtime validation means a hallucinated field fails loudly at the boundary instead of shipping.
  • npx typewire init detects the project and scaffolds a working setup in one command.
Read AGENTS.md ↗

Ask the docs

A docs assistant that answers from these pages and cites them.

It searches the same markdown these docs pages render, so every answer ends in the pages it read — and it will say it does not know rather than invent an option.

// FEEDBACK

The project is young. Tell us what breaks.

No testimonial wall yet — these slots hold real quotes when there are real quotes.

No quotes yet — be the first.

If TypeWire is running in something real, say so and it goes here.

Reaction

→ opens a prefilled issue on github.com/TahaNabavi/typewire

// COLLABORATE

A change that breaks another package cannot merge quietly

Contributing is a five-step flow with two gates behind it.

  1. fork
  2. branch
  3. change + test
  4. changeset
  5. PR

Every PR that changes a package's source must include a changeset.

Breakage alert

Every PR runs build → typecheck → test in topological order. Dependents typecheck against their dependency's freshly built types, so a breaking change in one package reddens the check for the packages that use it.

No broken publish

Release re-runs the same gate before changeset publish, and Changesets bumps every internal dependent — so npm consumers always resolve compatible versions.

Good first issues

None open right now — browse all issues ↗

Prerequisites

Node.js
≥ 22.13
pnpm
11.6.0

// ROADMAP

Shipped, and what is next

Read from the checklist in the repository README, so it cannot fall behind it.

  1. Monorepo + shared tooling (pnpm · Changesets · CI gates)

  2. typefetch runtime instrumentation & overrides (the devtools seam)

  3. typefetch-query-core + typefetch-react

    the React-Query-like layer

  4. type-devtools-core + type-devtools

    the cross-transport inspector

  5. type-permission

    framework-less capability permissions + optional contract link (NestJS guard)

  6. Pluggable transports

    the open TransportRegistry, typefetch-grpc, typefetch-graphql, and a core at zero dependencies

  7. typewire-cli

    typewire.config.ts, the project-detecting init wizard, multi-API projects

  8. typewire-nestjs beyond HTTP

    gRPC (Connect JSON), GraphQL, and typesocket WS gateways, all from the same contract file

  9. YOU ARE HERE

    12 of 12 packages on npm

  10. typewire snapshot + diffnext up

    breaking-change detection against a committed API-surface lockfile

  11. typewire lint · doctor · explain · mock · generate openapi

  12. Connect conformance runner in CI

    see docs/ROADMAP.md for why it is still open

  13. type-permission client pre-flight middleware + Vue/React binding recipes

  14. type-opengraph

    typed OpenGraph/metadata client

  15. typewire-vue / typewire-angular query adapters

typewire diffPROPOSED
$ npx typewire diff v1.4.0 v2.0.0
+ user.getUser.response.email
- user.getUser.response.name
~ user.listUsers.request.page → cursor
3 changes · 1 breaking
→ 2 call sites will fail typecheck

Not a shipped command. The CLI ships init, list, test and release-doc today; diff and lint are designed in docs/CLI.md and are what the roadmap above is heading towards.

Honest gaps

  • Size budgets are asserted ceilings, not published measurements — CI fails when one is exceeded, but no measured figure is shown.
Full sequencing, including the gaps that are still open ↗

Define it once.Wire it everywhere.

npx typewire init