SDK ReferenceReference & Guides
Framework Recipes
Quick integration patterns for popular JavaScript frameworks
Overview
This guide provides detailed integration examples for popular JavaScript frameworks and runtimes.
Framework Compatibility Note: Most examples use basicUsageEventConsumer directly in route handlers rather than the middlewareEventConsumer due to framework-specific middleware patterns. The middlewareEventConsumer is designed for Express-like frameworks with (req, res, next) signatures.
Next.js
import { scrawn } from "@scrawn/core";
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
const biller = scrawn({
apiKey: process.env.SCRAWN_KEY as `scrn_${string}`,
baseURL: process.env.SCRAWN_BASE_URL || "http://localhost:8069",
});
export async function POST(req: NextRequest) {
const session = await auth();
if (!session?.user?.id) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Start tracking (don't await yet)
const trackingPromise = biller.basicUsageEventConsumer({
userId: session.user.id,
debit: 100,
});
// Process request
const result = await generateContent(await req.json());
// Wait for tracking to complete
await trackingPromise;
return NextResponse.json(result);
}Express.js
import express from "express";
import { scrawn } from "@scrawn/core";
const app = express();
app.use(express.json());
const biller = scrawn({
apiKey: process.env.SCRAWN_KEY as `scrn_${string}`,
baseURL: process.env.SCRAWN_BASE_URL || "http://localhost:8069",
});
// Use middleware to automatically track all API calls
app.use(biller.middlewareEventConsumer({
extractor: (req) => {
if (!req.user) return null;
return {
userId: req.user.id,
debit: req.body?.cost || 10,
};
},
blacklist: ["/health", "/api/collect-payment"],
}));
app.post("/api/generate", async (req, res) => {
const result = await generateContent(req.body);
res.json(result);
});
app.post("/api/collect-payment", async (req, res) => {
const checkoutLink = await biller.collectPayment(req.body.userId);
res.redirect(checkoutLink);
});
app.listen(3000);NestJS
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { scrawn } from "@scrawn/core";
@Injectable()
export class ScrawnInterceptor implements NestInterceptor {
private biller = scrawn({
apiKey: process.env.SCRAWN_KEY as `scrn_${string}`,
baseURL: process.env.SCRAWN_BASE_URL || "http://localhost:8069",
});
async intercept(
context: ExecutionContext,
next: CallHandler,
): Promise<Observable<any>> {
const request = context.switchToHttp().getRequest();
const user = request.user;
if (user) {
this.biller.basicUsageEventConsumer({
userId: user.id,
debit: 10,
}).catch(err => {
console.error("Failed to track event:", err);
});
}
return next.handle();
}
}import { Controller, Post, UseInterceptors } from "@nestjs/common";
import { ScrawnInterceptor } from "./scrawn.interceptor";
@Controller("api")
@UseInterceptors(ScrawnInterceptor)
export class AppController {
@Post("generate")
async generate() {
return { message: "Content generated" };
}
}tRPC
import { TRPCError } from "@trpc/server";
import { scrawn } from "@scrawn/core";
import { t } from "./trpc";
const biller = scrawn({
apiKey: process.env.SCRAWN_KEY as `scrn_${string}`,
baseURL: process.env.SCRAWN_BASE_URL || "http://localhost:8069",
});
export const trackUsage = t.middleware(async ({ ctx, next }) => {
if (!ctx.session?.user?.id) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
// Start tracking (don't await)
const trackingPromise = biller.basicUsageEventConsumer({
userId: ctx.session.user.id,
debit: 10,
});
// Execute procedure
const result = await next();
// Wait for tracking
await trackingPromise.catch(err => {
console.error("Failed to track event:", err);
});
return result;
});
// Use in procedures
export const protectedProcedure = t.procedure.use(trackUsage);