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.
Adding ITI to your Project
Section titled “Adding ITI to your Project”npm install -S iti iti-react
pnpm add iti iti-react
bun add iti iti-react
yarn add iti iti-react
with ITI
import { app } from "./app"
// Proxy Getter: Lazily creates PaymentService instanceconst paymentService = await app.items.paymentServicepaymentService.sendMoney()
Without ITI
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 Reactimport { 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)
// Part 1: Normal Application Business Logicinterface 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)
// Part 2: ITI boilerplate. Manual DI alternativeimport { 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: Usageimport { app } from "./app"
// Will lazily fetch data and create PaymentService instanceconst paymentService = await app.items.paymentServicepaymentService.sendMoney()
// (Optional) With Reactimport { 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>}