Adapters
Overview
Infrastructure adapters allow swapping implementations without changing application code.
Cache
import { CacheAdapter } from "@rune/cache";
interface CacheAdapter { get<T>(key: string): Promise<T | null>; set(key: string, value: unknown, ttl?: number): Promise<void>; delete(key: string): Promise<void>; clear(): Promise<void>;}
// Built-inimport { MemoryCache } from "@rune/cache";
// External implementationsimport { RedisCache } from "@rune/cache-redis";import { CloudflareKVCache } from "@rune/cache-kv";import { UpstashCache } from "@rune/cache-upstash";Logger
import { LoggerAdapter } from "@rune/logger";
interface LoggerAdapter { info(...args: unknown[]): void; warn(...args: unknown[]): void; error(...args: unknown[]): void; debug(...args: unknown[]): void;}
// Built-inimport { ConsoleLogger } from "@rune/logger";
// External implementationsimport { PinoLogger } from "@rune/logger-pino";import { WinstonLogger } from "@rune/logger-winston";Events
import { EventAdapter } from "@rune/events";
interface EventAdapter { emit(event: string, payload: unknown): Promise<void>; on(event: string, handler: Function): Promise<void>;}
// Built-inimport { MemoryEventBus } from "@rune/events";
// External implementationsimport { RedisEventBus } from "@rune/events-redis";import { KafkaEventBus } from "@rune/events-kafka";import { NATSEventBus } from "@rune/events-nats";Queue
import { QueueAdapter } from "@rune/queue";
interface QueueAdapter { publish(queue: string, payload: unknown): Promise<void>; consume(queue: string, handler: Function): Promise<void>;}
// External implementationsimport { BullMQQueue } from "@rune/queue-bullmq";import { SQSQueue } from "@rune/queue-sqs";import { CloudflareQueue } from "@rune/queue-cf";Database
import { DatabaseAdapter } from "@rune/database";
interface DatabaseAdapter { connect(): Promise<void>; disconnect(): Promise<void>;}Built-in adapters
The @rune/database umbrella package re-exports from sub-packages. You can use the umbrella or import directly:
import { DrizzleAdapter } from "@rune/database";// Or directly:import { DrizzleAdapter } from "@rune/database-drizzle";import { PrismaAdapter } from "@rune/database-prisma";import type { DatabaseAdapter } from "@rune/database-core";Built-in ORM adapters:
DrizzleAdapter
import { drizzle } from "drizzle-orm/better-sqlite3";import Database from "better-sqlite3";import { DrizzleAdapter } from "@rune/database";
const sqlite = new Database("app.db");const db = drizzle(sqlite);const adapter = new DrizzleAdapter(db);
// Lifecycle (auto-wired via RuneApp)await adapter.connect();// ... use adapter.client for native Drizzle queriesawait adapter.disconnect();PrismaAdapter
import { PrismaClient } from "@prisma/client";import { PrismaAdapter } from "@rune/database";
const prisma = new PrismaClient();const adapter = new PrismaAdapter(prisma);
// Lifecycle (auto-wired via RuneApp)await adapter.connect(); // calls prisma.$connect()await adapter.disconnect(); // calls prisma.$disconnect()
// Query using native Prisma APIconst users = await adapter.client.user.findMany();Lifecycle hooks
Adapters implement OnAppInit / OnAppDestroy for automatic lifecycle wiring:
const app = createApp();
// Register lifecycle hooksapp.onInit(() => adapter.onAppInit());app.onDestroy(() => adapter.onAppDestroy());
await app.init(); // calls adapter.connect()await app.destroy(); // calls adapter.disconnect()Example: Module integration
import { DrizzleAdapter } from "@rune/database";
@Deps(DrizzleAdapter)class PostService { constructor(private db: DrizzleAdapter<any>) {}
async getPosts() { return this.db.client.select().from(postsTable).all(); }}
@Module({ controllers: [PostController], providers: [PostService, DrizzleAdapter],})class BlogModule {}import { MailAdapter, MailMessage } from "@rune/mail";
interface MailAdapter { send(message: MailMessage): Promise<void>;}
// External implementationsimport { ResendAdapter } from "@rune/mail-resend";import { SESAdapter } from "@rune/mail-ses";import { SendGridAdapter } from "@rune/mail-sendgrid";Socket
import { SocketAdapter, SocketHandler } from "@rune/socket";
interface SocketAdapter { handleUpgrade(request: Request): Promise<Response> | Response; broadcast(data: string): void; readonly connections: number; close(): void; on(event: "connection", handler: (conn: SocketConnection) => void): void; on(event: "message", handler: (conn: SocketConnection, msg: SocketMessage) => void): void; on(event: "close", handler: (conn: SocketConnection) => void): void; on(event: "error", handler: (conn: SocketConnection, error: Error) => void): void;}
// Built-inimport { SocketHandler } from "@rune/socket";
// External implementationsimport { WsAdapter } from "@rune/socket-ws";import { SocketIOAdapter } from "@rune/socket-io";GraphQL
import { GraphQLAdapter, GraphQLHandler } from "@rune/graphql";
interface GraphQLAdapter { execute(query: string, variables?: Record<string, unknown>): Promise<ExecutionResult>; handleRequest(request: Request): Promise<Response>;}
// Built-inimport { GraphQLHandler } from "@rune/graphql";
// External implementationsimport { YogaAdapter } from "@rune/graphql-yoga";import { ApolloAdapter } from "@rune/graphql-apollo";Telemetry
import { TelemetryAdapter } from "@rune/telemetry";
interface TelemetryAdapter { startSpan(name: string): Span; recordException(error: Error): void;}
// Built-inimport { NoopTelemetry } from "@rune/telemetry";
// External implementationsimport { OTelAdapter } from "@rune/otel";import { DatadogAdapter } from "@rune/datadog";Custom Adapter
Implement any interface:
import { CacheAdapter } from "@rune/cache";
export class MyCustomCache implements CacheAdapter { async get<T>(key: string): Promise<T | null> { // Your implementation } async set(key: string, value: unknown, ttl?: number): Promise<void> { // Your implementation } async delete(key: string): Promise<void> {} async clear(): Promise<void> {}}