Skip to content

Quick Start

ITI is a 1kb dependency injections framework for asynchronous applications. You are probably using some form of manual DI and ITI is a logical upgrade.

If you are building a react there are useful react bindings are available.

Terminal window
npm install -S iti iti-react

with ITI

with.tsx
import { app } from "./app"
// Proxy Getter: Lazily creates PaymentService instance
const paymentService = await app.items.paymentService
paymentService.sendMoney()

Without ITI

without.ts
import {
PaymentService,
AuthService,
CookieStorageService,
} from "./business-logic"
import { PinoLogger, ConsoleLogger } from "./loggers"
const logger =
process.env.NODE_ENV === "production" ? new PinoLogger() : new ConsoleLogger()
const app = async () => {
const cookieStorage = new CookieStorageService()
const auth = new AuthService(cookieStorage)
const userData = await auth.getUserData()
const paymentService = new PaymentService(logger, userData)
return {
paymentService,
}
}
app().then(({ paymentService }) => {
paymentService.sendMoney()
})
// (Optional) With React
import { useContainer } from "./_containers/main-app"
function Profile() {
const [user, userErr] = useContainer().userData
if (!user || userErr) return <div>loading... or error</div>
return <div>Hello {user.name}!</div>
}

Business Logic (Part 1)

business-logic.ts
// Part 1: Normal Application Business Logic
interface Logger {
info: (msg: string) => void
}
interface UserData {
name: string
}
export class CookieStorageService {
async getSessionToken(): Promise<{ token: string }> {
return { token: "magicToken123" }
}
}
export class AuthService {
constructor(private cookieStorageService: CookieStorageService) {}
async getUserData(): Promise<UserData> {
const { token } = await this.cookieStorageService.getSessionToken()
if (token === "magicToken123") {
return { name: "Big Lebowski" }
}
throw new Error("Unauthorized")
}
}
export class PaymentService {
constructor(
private readonly logger: Logger,
private readonly user: UserData
) {}
sendMoney() {
this.logger.info(`Sending money to the: ${this.user.name} `)
return true
}
}
// Application code is free of framework dependencies like decorators

Container Setup (Part 2)

app.ts
// Part 2: ITI boilerplate. Manual DI alternative
import { createContainer } from "iti"
import {
PaymentService,
AuthService,
CookieStorageService,
} from "./business-logic"
import { PinoLogger, ConsoleLogger } from "./loggers"
export const app = createContainer()
.add({
// Add token `logger` and assign some logger instance
logger: () =>
process.env.NODE_ENV === "production"
? new PinoLogger()
: new ConsoleLogger(),
// Add token `cookieStorage` ...
cookieStorage: () => new CookieStorageService(),
})
.add((ctx) => ({
auth: () => new AuthService(ctx.cookieStorage),
}))
.add((ctx) => ({
userData: async () => await ctx.auth.getUserData(),
}))
.add((ctx) => ({
paymentService: async () =>
new PaymentService(ctx.logger, await ctx.userData),
}))

Usage (Part 3)

// Part 3: Usage
import { app } from "./app"
// Will lazily fetch data and create PaymentService instance
const paymentService = await app.items.paymentService
paymentService.sendMoney()
// (Optional) With React
import { useContainer } from "./_containers/main-app"
function Profile() {
const [user, userErr] = useContainer().userData
if (!user || userErr) return <div>loading... or error</div>
return <div>Hello {user.name}!</div>
}