Serverless Deployment
Karin is architected with a "Serverless-First" mindset. The framework core decouples the Application Logic (Controllers/Services) from the underlying Runtime (HTTP Server).
This decoupling allows Karin to run seamlessly on Edge networks like Cloudflare Workers, Vercel Edge, and Deno Deploy, where traditional long-running Node.js processes are replaced by ephemeral function invocations.
Concept: The Pop-Up Store
A traditional server is a Flagship Store: always open, always staffed, paying rent 24/7. Serverless is a Pop-Up Store: It appears instantly when a customer (request) arrives, serves them, and vanishes. It is extremely cost-effective but requires a different logistics chain (Statelessness).
The Boot Pattern: Inversion of Control
In a standard application, you call app.listen(3000). The application binds to a port and waits. In Serverless, you do not listen. The platform listens for you and invokes your handler. You must export a handler function.
We use KarinFactory.serverless() instead of KarinFactory.create().
// main.ts
import { KarinFactory } from "@project-karin/core";
import { HonoAdapter } from "@project-karin/platform-hono";
import { UserController } from "./user/user.controller";
// 1. Initialize the Adapter manually (to configure CORS, etc.)
const adapter = new HonoAdapter();
adapter.enableCors();
// 2. Create the Handler (Do not await!)
export default KarinFactory.serverless(adapter, {
// CRITICAL: Disable file scanning. Bundlers like esbuild cannot require() dynamic paths.
scan: false,
// You MUST explicitly register all components
controllers: [UserController],
providers: [JwtStrategy, UserService],
plugins: [config, db, auth],
});The Bundler Constraint (No Dynamic Imports)
Serverless environments use aggressive bundlers (esbuild, webpack, vite) to compile your code into a single file. These bundlers analyze your import graph at build time.
Therefore, Karin's scan: true feature (which reads the filesystem at runtime to find .controller.ts files) WILL NOT WORK.
IMPORTANT
You must set { scan: false } and manually import and register every Controller and Provider in main.ts.
The Metadata Constraint (esbuild & Decorators)
TypeScript's emitDecoratorMetadata feature relies on the compiler emitting runtime type information.
- TSC (Standard): Emits full metadata (
design:paramtypes). - esbuild (Cloudflare/Vite): Does NOT emit deep metadata by default.
This breaks Dependency Injection if you rely on implicit typing.
Broken in Cloudflare:
@Post()
create(@Body() dto: CreateUserDto) {
// Error: Karin cannot know that 'dto' is of type 'CreateUserDto'
}Fixed (Explicit Typing):
@Post()
create(@Body(CreateUserDto) dto: CreateUserDto) {
// Success: We explicitly passed the Class Constructor to the decorator
}Warm Starts & Connection Reuse
Even though serverless functions are ephemeral, the provider often reuses the same V8 Isolate for multiple requests (Warm Start).
Karin's architecture takes advantage of this.
- Global Scope: The
appinstance is created in the global scope (outside thefetchhandler). - Singleton Re-hydration: On the first request (Cold Start), Karin boots calls
onPluginInit. - Subsequent Requests: Karin skips the boot phase and routes the request immediately.
This is why MongoosePlugin and DrizzlePlugin have special logic to reuse existing connections instead of opening new ones every 100ms.
platform-specific Examples
Cloudflare Workers (wrangler.toml)
Cloudflare uses the standard Web API (Request / Response).
name = "my-karin-api"
main = "src/main.ts"
compatibility_date = "2024-04-05"
[vars]
LOG_LEVEL = "debug"Vercel / Next.js API Routes
You can wrap the handler for Vercel's proprietary signatures.
// api/[[...route]].ts
import handler from "../src/main"; // Your Karin export
export const GET = handler.fetch;
export const POST = handler.fetch;
// ... export other methods