Scrawn.js
Framework Integration
Integration guides for popular JavaScript frameworks
Overview
This guide provides detailed integration examples for popular JavaScript frameworks and runtimes.
Framework Compatibility Note: Most examples use sdkCallEventConsumer 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 scrawn = new 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 = scrawn.sdkCallEventConsumer({
userId: session.user.id,
debitAmount: 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, type EventPayload } from '@scrawn/core';
const app = express();
app.use(express.json());
const scrawn = new 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(scrawn.middlewareEventConsumer({
extractor: (req): EventPayload | null => {
if (!req.user) return null;
return {
userId: req.user.id,
debitAmount: 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 scrawn.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 scrawn: Scrawn;
constructor() {
this.scrawn = new 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) {
// Track in background, don't block request
this.scrawn.sdkCallEventConsumer({
userId: user.id,
debitAmount: 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 scrawn = new 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 = scrawn.sdkCallEventConsumer({
userId: ctx.session.user.id,
debitAmount: 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);