Skip to content

Using services

Create and use a service:

import type { Context } from 'udic';
interface UUIDService {
uuid: {
create: () => string;
};
}
const main = (c: Context<UUIDService>) => {
console.log('uuid:', c.uuid.create());
};
main({
uuid: {
create: () => crypto.randomUUID(),
},
});

Use multiple services that reference others:

import { impl, implAsync, linkAsync, type Context } from 'udic';
import { SQL, type SQLOptions } from 'bun';
interface Config {
config: {
logLevel: 'DEBUG' | 'WARNING' | 'ERROR';
db: SQLOptions;
};
}
// Auto-infer type from implementations
type Logger = Context<typeof loggerImpl>;
type Database = Context<typeof dbImpl>;
// Logger implementation
const loggerImpl = impl((c: Context<Config>) => ({
log: (...args: any[]) => {
console.log(`[${c.config.logLevel}]`, ...args);
},
}));
// Database implementation
const dbImpl = implAsync(async (c: Context<Config>) => ({
sql: await new SQL(c.config.db).connect(),
}));
{
const query = (c: Context<Database>) => c.sql`SELECT 1`;
// Auto-infer context type from other functions
const main = async (c: Context<typeof query | Logger>) => {
c.log('config:', c.config);
c.log('query result:', await query(c));
};
// linkAsync() can link both async and sync implementations
// use link() to link only sync implementations
const c = await linkAsync(
{
config: {
logLevel: 'DEBUG',
db: {
adapter: 'sqlite',
database: ':memory:',
},
},
},
loggerImpl,
dbImpl,
);
await main(c);
}