Aiki
Guides

Dependency Injection

Inject dependencies that are created once at startup and shared by every execution — database connections, API clients, services — into tasks and workflows with higher-order functions. For values created fresh per execution, like trace IDs, use Context instead.

Higher-Order Functions (Startup Dependencies)

Use this pattern for dependencies like database connections, API clients, or services that should be created once at startup and reused across all executions.

Tasks

Wrap your task definition in a function that accepts dependencies:

import { task } from "@aikirun/workflow";

interface EmailService {
	send(to: string, subject: string, body: string): Promise<void>;
}

const createNotifyCustomer = (emailService: EmailService) =>
	task({
		name: "notify-customer",
		async handler(input: { email: string; message: string }) {
			await emailService.send(input.email, "Order Update", input.message);
		},
	});

// At startup, inject your real service:
const emailService = new SendGridEmailService(process.env.SENDGRID_API_KEY);
export const notifyCustomer = createNotifyCustomer(emailService);

Workflows

The same pattern works for workflows:

import { workflow } from "@aikirun/workflow";

interface Database {
	orders: {
		findById(id: string): Promise<Order>;
		update(id: string, data: Partial<Order>): Promise<void>;
	};
}

const createOrderWorkflow = (db: Database) => {
	const orderWorkflow = workflow({ name: "order-processing" });

	return orderWorkflow.v("1.0.0", {
		async handler(run, input: { orderId: string }) {
			const order = await db.orders.findById(input.orderId);

			// Process order...

			await db.orders.update(input.orderId, { status: "completed" });
			return { success: true };
		},
	});
};

// At startup:
const db = createDatabaseConnection(process.env.DATABASE_URL);
export const orderWorkflowV1 = createOrderWorkflow(db);

When to Use Which

PatternUse CaseLifetime
Higher-order functionsDatabase connections, API clients, servicesCreated once at startup
ContextTrace IDs, request metadata, user contextCreated per execution

Higher-order functions are best for:

  • Dependencies that are expensive to create (DB connections, HTTP clients)
  • Stateful services that should be shared
  • External service clients with connection pooling

Context is best for:

  • Per-request tracing and observability
  • User-specific context that varies per execution
  • Lightweight metadata that doesn't need connection management

Next Steps

On this page