Skip to main content
Skip to main content
Type-Safe DI for TypeScript

The compiler
enforces
the architecture

Catch missing dependencies at compile time, not in production. HexDI encodes your entire dependency graph into TypeScript types — missing adapters are type errors, not runtime crashes.

$ npm install hex-di
CONTAINERcreateContainer()LOGGERportDATABASEportCONSOLEadapterPOSTGRESadaptercompile-time valid ✓
0
runtime dependencies
in @hex-di/core
3
core packages
one install: hex-di
9
ecosystem libraries
ports-first design
TS 5+
TypeScript support
strict structural types
— CAPABILITIES —

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.

— COMPILE-TIME GUARANTEE —

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.

graph.ts
// Service that requires LoggerPort
const UserServiceAdapter = createAdapter({
  provides: UserServicePort,
  requires: [LoggerPort],       // ← dependency declared
  lifetime: 'singleton',
  factory: ({ Logger }) => ({ /* ... */ }),
});

// Graph missing the required adapter
const graph = GraphBuilder.create()
  .provide(UserServiceAdapter)  // needs LoggerPort
  .build();                     // ← COMPILE ERROR ✗
TypeScript Error
 ERROR [HEX008]
 ─────────────────────────────────────
 Missing adapters for required ports:

LoggerPort

 UserServiceAdapter declares LoggerPort
 as a required dependency, but no
 adapter in this graph provides it.

 Fix: .provide(LoggerAdapter)
 ─────────────────────────────────────
 Detected at compile time — not at 3AM.
— QUICK START —

From scratch to type-safe in 5 steps

  1. 01Define your interfaces as plain TypeScript contracts
  2. 02Create ports — typed handles that name and represent each dependency
  3. 03Build adapters that satisfy those contracts
  4. 04Compose a type-checked dependency graph
  5. 05Resolve instances — fully typed, no casts
main.ts
import { port, createAdapter, GraphBuilder, createContainer } from 'hex-di';

// 1. Define a contract
interface Logger { log(msg: string): void }

// 2. Create a port — name is inferred as a literal type
const LoggerPort = port<Logger>()({ name: 'Logger' });

// 3. Declare an adapter with explicit dependencies
const ConsoleLogger = createAdapter({
  provides: LoggerPort,
  requires: [],
  lifetime: 'singleton',
  factory: () => ({
    log: (msg) => console.log('[App]', msg),
  }),
});

// 4. Build the graph — validated at compile time
// Missing dependencies are TypeScript errors ✗
const graph = GraphBuilder.create()
  .provide(ConsoleLogger)
  .build();

// 5. Resolve — fully typed, no casts
const container = createContainer({ graph, name: 'App' });
const logger = container.resolve(LoggerPort);
// ^— TypeScript knows this is Logger ✓
logger.log('System online.');
— ARCHITECTURE —

Built on hexagonal principles

Ports define contracts. Adapters provide implementations. Your domain stays clean.

loggerloggingtracingtelemetryqueryfetchingstorestateflowmachinessagaworkflowsguardauthclocktimehttp-clienttransportHEX-DI
COREhex-di

Ports, adapters, graph builder, and container. Everything you need.

OPTIONAL@hex-di/react

React provider, context, and hooks that mirror your DI graph.

OPTIONAL@hex-di/testing

Type-safe mock adapters, graph assertions, and renderWithContainer — DI-aware test utilities for Vitest and Testing Library.

+ 9 ECOSYSTEM LIBRARIES
loggertracingquerystoreflowsagaguardclockhttp-client
— THE ECOSYSTEM —

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/logger

Structured logging

pino · winston · bunyan

@hex-di/tracing

Distributed tracing

OTel · Datadog · Jaeger · Zipkin

@hex-di/query

Data fetching & caching

port-based query layer

@hex-di/store

Reactive state

signal-based DI port

@hex-di/flow

State machines

injected service FSMs

@hex-di/saga

Workflow orchestration

distributed saga pattern

@hex-di/guard

Auth & permissions

role · policy · ABAC

@hex-di/clock

Testable time

virtual clock for tests

@hex-di/http-client

HTTP client port

typed client with retry

— LIFETIME SCOPES —

Control instance lifecycles

Declare how long each service lives. The container enforces it.

SINGLETON
lifetime: 'singleton'

One instance for the entire container lifetime. Shared across all consumers.

Loggers, configuration providers, API clients, and services with no per-request state.

SCOPED
lifetime: 'scoped'

One instance per scope. Shared within the scope, isolated across scopes.

Request auth context, database connections, per-user state, unit-of-work.

TRANSIENT
lifetime: 'transient'

New instance every resolution. Never shared between consumers.

Stateful services that must not be shared between callers — each consumer gets its own instance.

— WHY HEX-DI —

The problem with traditional DI

✗ 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
✓ WITH HEX-DI
  • +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 code shouldn't compile.”
— The HexDI Philosophy
◈ READY TO COMPILE ◈

Start building type-safe
applications today

Catch dependency errors at compile time. Ship with confidence.