Database
camelon does not ship a database. It injects the client you pass. The db arg is whatever you hand createHandler, and it arrives on the args bag in every get and post handler.
A tiny in-memory client, injected and read off db:
import type { GetFunction } from 'camelon';
import type { Fruit, FruitDb } from '../../db';
declare module 'camelon' {
interface Register {
db: FruitDb;
}
}
export type GetOutput = { fruits: Fruit[] };
export const get: GetFunction = ({ db }) => {
return { fruits: db.fruits() };
};
export default function Db({ fruits }: GetOutput) {
return (
<div class="d-card">
<strong>Fruit store</strong>
<ul class="d-list">
{fruits.map((f) => (
<li>
{f.emoji} {f.name} — {f.price}¢
</li>
))}
</ul>
</div>
);
}A real adapter (Prisma via the same db arg) looks identical from the route's side:
import type { GetFunction } from 'camelon';
import type { PrismaClient, User } from '@prisma/client';
declare module 'camelon' {
interface Register {
db: PrismaClient;
}
}
export type GetOutput = { users: User[] };
export const get: GetFunction = async ({ db }) => {
return { users: await db.user.findMany() };
};import { createHandler } from 'camelon';
import { PrismaClient } from '@prisma/client';
import { manifest } from './gen/manifest.generated';
await createHandler({ routes: manifest, db: new PrismaClient() });Type db by declaring it on Register, the one augmentable interface where an app types itself. db then carries your client's type everywhere — no cast:
declare module 'camelon' {
interface Register {
db: PrismaClient;
}
}
export type GetOutput = { users: User[] };
export const get: GetFunction = async ({ db }) => {
return { users: await db.user.findMany() }; // db is PrismaClient
};
Until you declare Register['db'], touching db is a type error that tells you to declare it — never unknown or any. At runtime, using db without configuring one throws a clear error. Apps that never touch db pay nothing.