Skip to main 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.

note

Please know, that the docs is still work in progress. Many features or use cases are probably already in the lib but not documented well. We are working on it.

Adding ITI to your Project

# with npm
npm install -S iti iti-react

# or with yarn
yarn add iti iti-react
tip

If you are building a React App there are useful React bindings (iti-react) are available.

TL;DR

with ITI

import { app } from "./app"

// Proxy Getter: Lazily creates PaymentService instance
const paymentService = await app.items.paymentService
paymentService.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()
})

Usage

// (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>
}
// 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
// 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),
}))
// 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>
}