API Reference
work in progress
- container - is a collection of items
- item - is a value you want to store in a container, like a class instance
- token - is a unique string identifier for an item
Events
Section titled “Events”In a container, if you update or replace an item, users will be notified.
In React it happens automatically
const kitchenApp = createContainer((ctx) => ({ // you can use tokens (`oven`, `kitchen`) here and later on oven: async () => ovenContainer(), kitchen: async () => kitchenContainer(await ctx.oven()),}))
kitchenApp.on("itemCreated", (event) => { console.log(`event: 'itemCreated' ~~> token: '${event.key}'`) // `event.newItem` is also available here})
kitchenApp.on("itemUpdated", (event) => { console.log(`event: 'itemUpdated' ~~> token: '${event.key}'`)})
kitchenApp.on("itemUpserted", (event) => { console.log(`event: 'itemUpserted' ~~> token: '${event.key}'`)})
kitchenApp.on("itemDeleted", (event) => { console.log(`event: 'itemDeleted' ~~> token: '${event.key}'`)})
kitchenApp.on("itemDisposed", (event) => { console.log(`event: 'itemDisposed' ~~> token: '${event.key}'`)})
// Legacy container events (still supported)kitchenApp.on("containerCreated", (event) => { console.log(`event: 'containerCreated' ~~> token: '${event.key}'`)})
kitchenApp.on("containerRequested", (event) => { console.log(`event: 'containerRequested' ~~> token: '${event.key}' `)})
kitchenApp.on("containerRemoved", (event) => { console.log(`event: 'containerRemoved' ~~> token: '${event.key}' `)})
await kitchenApp.items.kitchen
// event: 'containerRequested' ~~> token: 'kitchen'// event: 'containerRequested' ~~> token: 'oven'// event: 'itemCreated' ~~> token: 'oven'// event: 'containerCreated' ~~> token: 'oven'// event: 'itemCreated' ~~> token: 'kitchen'// event: 'containerCreated' ~~> token: 'kitchen'
// Notice how oven was created before kitchen.// This is because kitchen depends on ovenAPI documentation JS / TS
Section titled “API documentation JS / TS”createContainer Setting app root
Section titled “createContainer Setting app root”import { createContainer } from "iti"export function getMainMockAppContainer() { return createContainer().add({ kitchen: () => new Kitchen(/* deps */) })}items getter
Section titled “items getter”let container = getMainPizzaAppContainer()let kitchen = await container.items.kitchenkitchen.oven.pizzaCapacity // 4get(token) - Get single item
Section titled “get(token) - Get single item”const service = await container.get("myService")// Returns a Promise that resolves to the service instancegetSync(token) - Get item synchronously ✨ New in v0.8.0
Section titled “getSync(token) - Get item synchronously ✨ New in v0.8.0”// Get an item synchronously if it's already resolvedconst service = container.getSync("myService")// Returns the instance immediately if cached, otherwise undefinedgetItems(tokens) - Get multiple items
Section titled “getItems(tokens) - Get multiple items”// Get multiple instances at onceconst items = await container.getItems(["serviceA", "serviceB"])// Returns: { serviceA: ..., serviceB: ... }
// Callback styleconst items = await container.getItems((c) => [c.serviceA, c.serviceB])getItemsSync(tokens) - Get multiple items synchronously ✨ New in v0.8.0
Section titled “getItemsSync(tokens) - Get multiple items synchronously ✨ New in v0.8.0”// Returns synchronously if all items are resolved, otherwise returns a Promiseconst items = container.getItemsSync(["serviceA", "serviceB"])// Can be either: { serviceA: ..., serviceB: ... } OR Promise<{ serviceA: ..., serviceB: ... }>upsert
Section titled “upsert”delete
Section titled “delete”When containers are updated React is updated too via hooks
Disposing
Section titled “Disposing”You can launch a live playground on Stackblitz
or checkout the playground repo with the playground locally
cd iti-playgroundcat src/examples/0.disposing.tsdisposeAll(): Promise<void>
Section titled “disposeAll(): Promise<void>”Runs cleanup functions on provided (created) dependencies.
Returns a Promise that resolves when all disposers of cached resolutions have resolved.
import { createContainer } from "iti"
container = createContainer() .add({ a: () => "123" }) .addDisposer({ a: () => {} })
container.get("a") === "123" // trueawait container.disposeAll() // Promise<void>Practical example
import { Client } from "pg"import { createContainer } from "iti"
const container = createContainer() .add(() => ({ dbConnection: async () => { const pg = new Client(process.env["DB_CONNECTION_URL"]) await pg.connect() return pg }, })) .addDisposer({ dbConnection: (dbConnection) => dbConnection.end(), })
const db = await container.get("dbConnection")await db.query("...")
// Later..await container.disposeAll()
console.log("All dependencies disposed, you can exit now. :)")dispose(token:): Promise<void>
Section titled “dispose(token:): Promise<void>”Disposes individual container
class A {}class B {}const container = createContainer() .add({ a: () => new A(), b: () => new B(), }) .addDisposer({ a: (a) => console.log("disposing a", a), b: (a) => throw new Error("this should not be called"), })
container.get("a")// will print `disposing a A {}`container.dispose("a")addDisposer(disposer: fn | {}): Promise<void>
Section titled “addDisposer(disposer: fn | {}): Promise<void>”import { createContainer } from "iti"
class A { dispose() {}}class B {}container = createContainer().add({ a: () => new A(), b: () => new B(),})
// Adding disposer for token `a`container.addDisposer({ a: () => {} })
// Accessing `A` instance in disposer for token `a`container.addDisposer({ a: (a: A) => a instanceof A === true,})
// Accessing ctx in disposer for token `a`container.addDisposer((ctx) => ({ a: () => ctx.b instanceof B === true,}))import { createContainer } from "iti"import { Client } from "pg"
class A {}
createContainer() .add({ db: async () => { const pg = new Client(process.env["DB_CONNECTION_URL"]) await pg.connect() return pg }, }) .add((ctx) => ({ a: () => new A(), b: async () => { const db = await ctx.db db.query("SELECT 1") }, })) // ↓ `ctx` to access any other dependency .addDisposer((ctx) => ({ // ↓ `db` is a resolved value of a `DB` db: (db) => { console.log(ctx.a) return db.disconnect() }, }))
class B {}const container = createContainer() .add({ a: () => new A(), b: () => new B(), }) .addDisposer({ a: (a) => console.log("disposing a", a), })
container.dispose("a").on('itemDisposed', ({ key: string }) => void) ✨ New in v0.8.0
Section titled “.on('itemDisposed', ({ key: string }) => void) ✨ New in v0.8.0”import { createContainer } from "iti"
container = createContainer() .add({ a: () => "123" }) .addDisposer({ a: () => {} }) .on("itemDisposed", (payload) => { console.log(payload.key) // prints 'a' })
container.get("a") === "123" // trueawait container.disposeAll()Notes on disposing
Section titled “Notes on disposing”disposal graph
Please note that .dispose('token') doesn’t dispose child elements.
This would be risky to implement due to reasons explained in dispose-graph.ts.api.spec.ts.
In a nutshell, due to an async nature of iti we could potentially dispose unrelated dependencies as well.
But there are a few workarounds:
you can create your custom dispose function that will dispose dependencies you care about
import { createContainer } from "iti"container = createContainer() .add({ a: () => "A", b: () => "B", }) .addDisposer((ctx) => ({ a: () => { console.log("do something to dispose a: ", ctx.a) console.log("do something to dispose b: ", ctx.b) }, }))you can create multiple containers (createContainer) and dispose them separately
import { createContainer } from "iti"class A {}class B {}class C {}class D { constructor(b: B) {}}const cont1 = createContainer() .add({ a: () => new A(), b: () => new B(), }) .addDisposer({ a: (a) => console.log("disposing a", a), b: (b) => console.log("disposing b", b), })
const cont2 = createContainer().add({ c: () => new C(), d: () => new D(cont1.get("b")),})
cont2.get("d") // Creates a new instance of D, which in turn creates a new instance of Bcont1.disposeAll() // Disposes B