Type safety all the way down
No decorators. No string tokens. No runtime magic. Just TypeScript types doing real work.
Compile-Time Safety
Missing dependencies are TypeScript errors — caught before your code runs, not in production at 3 AM.
Zero Reflection
No decorators. No reflect-metadata. No runtime overhead. Pure TypeScript structural types do the work.
Hexagonal Architecture
Ports define contracts, adapters provide implementations. Your domain logic stays clean and framework-free.
React Integration
Provider, scope, and resolution hooks for React. usePort() resolves typed services directly from your DI graph — no prop drilling, no context boilerplate.
Lifetime Management
Singleton, Scoped, and Transient lifetimes. The container enforces scope boundaries — scoped services cannot leak outside their scope.
Framework Agnostic
Works with any TypeScript project: Node.js, React, Hono, Express, Deno. No lock-in, ever.
A broken dependency graph won't compile
HexDI encodes your dependency graph into TypeScript's type system. If a port has no adapter, the error appears before you run a single line of code.
From scratch to type-safe in 5 steps
- 01Define your interfaces as plain TypeScript contracts
- 02Create ports — typed handles that name and represent each dependency
- 03Build adapters that satisfy those contracts
- 04Compose a type-checked dependency graph
- 05Resolve instances — fully typed, no casts
Built on hexagonal principles
Ports define contracts. Adapters provide implementations. Your domain stays clean.
hex-diPorts, adapters, graph builder, and container. Everything you need.
@hex-di/reactReact provider, context, and hooks that mirror your DI graph.
@hex-di/testingType-safe mock adapters, graph assertions, and renderWithContainer — DI-aware test utilities for Vitest and Testing Library.
A full ecosystem, wired through one graph
Every library exposes its functionality as ports. Wire them through the same container, and each library's services are fully typed and resolvable alongside your own.
@hex-di/loggerStructured logging
pino · winston · bunyan
@hex-di/tracingDistributed tracing
OTel · Datadog · Jaeger · Zipkin
@hex-di/queryData fetching & caching
port-based query layer
@hex-di/storeReactive state
signal-based DI port
@hex-di/flowState machines
injected service FSMs
@hex-di/sagaWorkflow orchestration
distributed saga pattern
@hex-di/guardAuth & permissions
role · policy · ABAC
@hex-di/clockTestable time
virtual clock for tests
@hex-di/http-clientHTTP client port
typed client with retry
Control instance lifecycles
Declare how long each service lives. The container enforces it.
One instance for the entire container lifetime. Shared across all consumers.
Loggers, configuration providers, API clients, and services with no per-request state.
One instance per scope. Shared within the scope, isolated across scopes.
Request auth context, database connections, per-user state, unit-of-work.
New instance every resolution. Never shared between consumers.
Stateful services that must not be shared between callers — each consumer gets its own instance.
The problem with traditional DI
- —Requires reflect-metadata and experimental decorators
- —Errors discovered at runtime, not at compile time
- —Magic token strings instead of type-level contracts
- —Framework code bleeds into your domain logic
- —Testing requires brittle mock module overrides
- +Pure TypeScript — no decorators, no magic
- +Graph validated structurally at compile time
- +Ports as typed handles — no string tokens, no injection decorators
- +Domain code stays completely framework-free
- +Testing via clean, scoped container overrides
“If a dependency is missing,— The HexDI Philosophy
the code shouldn't compile.”
Start building type-safe
applications today
Catch dependency errors at compile time. Ship with confidence.