Copy-paste recipes for AI development. All TypeScript. All tested patterns.
52 snippets across 9 categories
52 snippets
Basic streaming text generation using the AI SDK. Returns a ReadableStream you can pipe to the client.
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
const result = streamText({
model: openai("gpt-4o"),
system: "You are a helpful assistant.",
prompt: "Explain React Server Components in 3 sentences.",
});
// Pipe to a Response (Route Handler / Server Action)
return result.toDataStreamResponse();Generate type-safe structured data from an LLM using a Zod schema. No parsing or validation needed.
import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
const { object } = await generateObject({
model: openai("gpt-4o"),
schema: z.object({
name: z.string(),
ingredients: z.array(z.string()),
steps: z.array(z.string()),
prepTime: z.number().describe("Prep time in minutes"),
}),
prompt: "Generate a recipe for chocolate chip cookies.",
});
console.log(object.name);
// "Classic Chocolate Chip Cookies"Define tools the model can call. The SDK handles the tool call loop automatically.
import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
const result = await generateText({
model: openai("gpt-4o"),
tools: {
weather: tool({
description: "Get the current weather for a location",
parameters: z.object({
city: z.string().describe("The city name"),
}),
execute: async ({ city }) => {
// Call your weather API here
return { city, temp: 72, condition: "sunny" };
},
}),
},
maxSteps: 5,
prompt: "What's the weather in San Francisco?",
});
console.log(result.text);Full chat UI in a few lines. Handles streaming, message history, loading state, and error handling.
"use client";
import { useChat } from "@ai-sdk/react";
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } =
useChat({ api: "/api/chat" });
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
placeholder="Say something..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
}Send a message to Claude using the Anthropic SDK. Supports system prompts, multi-turn conversations, and streaming.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const message = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: "You are a senior TypeScript developer.",
messages: [
{ role: "user", content: "Write a debounce function with generics." },
],
});
console.log(message.content[0].type === "text" && message.content[0].text);Define tools for Claude to call. Handle the tool_use response and feed results back in a conversation loop.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
tools: [
{
name: "get_stock_price",
description: "Get the current stock price for a ticker symbol",
input_schema: {
type: "object" as const,
properties: {
ticker: { type: "string", description: "Stock ticker symbol" },
},
required: ["ticker"],
},
},
],
messages: [{ role: "user", content: "What's the price of AAPL?" }],
});
// Check if Claude wants to use a tool
for (const block of response.content) {
if (block.type === "tool_use") {
console.log(`Tool: ${block.name}, Input: ${JSON.stringify(block.input)}`);
}
}Send images to Claude for analysis. Supports base64-encoded images and URLs.
import Anthropic from "@anthropic-ai/sdk";
import { readFileSync } from "fs";
const client = new Anthropic();
const imageData = readFileSync("screenshot.png").toString("base64");
const response = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: imageData,
},
},
{
type: "text",
text: "Describe this UI. List any accessibility issues you see.",
},
],
},
],
});
console.log(response.content[0].type === "text" && response.content[0].text);Stream responses token-by-token from Claude. Uses server-sent events under the hood.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const stream = client.messages.stream({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: "Write a haiku about TypeScript." }],
});
for await (const event of stream) {
if (
event.type === "content_block_delta" &&
event.delta.type === "text_delta"
) {
process.stdout.write(event.delta.text);
}
}
const finalMessage = await stream.finalMessage();
console.log("\nTokens used:", finalMessage.usage.input_tokens);Create an MCP server with a custom tool. Uses the official TypeScript SDK with stdio transport.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "my-tools",
version: "1.0.0",
});
server.tool(
"search_docs",
"Search documentation by keyword",
{ query: z.string(), limit: z.number().optional().default(10) },
async ({ query, limit }) => {
const results = await searchIndex(query, limit);
return {
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);Expose data as an MCP resource that AI clients can read. Resources are like GET endpoints for LLMs.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new McpServer({
name: "project-context",
version: "1.0.0",
});
// Static resource
server.resource("readme", "file:///readme", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "text/markdown",
text: "# My Project\nThis is the project README.",
},
],
}));
// Dynamic resource with template
server.resource(
"user-profile",
"users://{userId}/profile",
async (uri, { userId }) => {
const user = await db.getUser(userId);
return {
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(user),
},
],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);Create reusable prompt templates that AI clients can discover and use. Prompts are like stored procedures for LLMs.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "prompts",
version: "1.0.0",
});
server.prompt(
"code-review",
"Review code for bugs, security issues, and style",
{ language: z.string(), code: z.string() },
({ language, code }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: [
`Review this ${language} code.`,
"Check for: bugs, security issues, performance, readability.",
"Format: list each issue with severity (high/medium/low).",
"",
`\`\`\`${language}`,
code,
"\`\`\`",
].join("\n"),
},
},
],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);A POST route handler that streams AI responses. Drop this in app/api/chat/route.ts.
// app/api/chat/route.ts
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
system: "You are a helpful coding assistant.",
messages,
});
return result.toDataStreamResponse();
}Call an LLM from a Server Action and stream the result to the client using createStreamableValue.
// app/actions.ts
"use server";
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { createStreamableValue } from "ai/rsc";
export async function generate(prompt: string) {
const stream = createStreamableValue("");
(async () => {
const result = streamText({
model: openai("gpt-4o"),
prompt,
});
for await (const delta of result.textStream) {
stream.update(delta);
}
stream.done();
})();
return { output: stream.value };
}Render AI-generated content as it streams in. Uses useChat for real-time updates with a loading indicator.
"use client";
import { useChat } from "@ai-sdk/react";
export default function StreamingChat() {
const { messages, input, handleInputChange, handleSubmit, status } =
useChat();
return (
<div className="max-w-2xl mx-auto p-4">
<div className="space-y-4 mb-4">
{messages.map((m) => (
<div
key={m.id}
className={m.role === "user" ? "text-right" : "text-left"}
>
<div
className={`inline-block p-3 rounded-lg ${
m.role === "user"
? "bg-black text-white"
: "bg-gray-100 text-black"
}`}
>
{m.content}
</div>
</div>
))}
{status === "streaming" && (
<div className="text-gray-400 animate-pulse">Thinking...</div>
)}
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
className="flex-1 border rounded-full px-4 py-2"
placeholder="Ask something..."
/>
<button
type="submit"
className="bg-black text-white px-6 py-2 rounded-full"
>
Send
</button>
</form>
</div>
);
}Simple in-memory rate limiter for AI API routes. Prevents abuse and controls costs.
// lib/rate-limit.ts
const rateLimit = new Map<string, { count: number; resetTime: number }>();
export function checkRateLimit(
ip: string,
limit = 10,
windowMs = 60_000
): { allowed: boolean; remaining: number } {
const now = Date.now();
const entry = rateLimit.get(ip);
if (!entry || now > entry.resetTime) {
rateLimit.set(ip, { count: 1, resetTime: now + windowMs });
return { allowed: true, remaining: limit - 1 };
}
if (entry.count >= limit) {
return { allowed: false, remaining: 0 };
}
entry.count++;
return { allowed: true, remaining: limit - entry.count };
}
// Usage in route handler:
// const ip = req.headers.get("x-forwarded-for") ?? "unknown";
// const { allowed } = checkRateLimit(ip);
// if (!allowed) return new Response("Too many requests", { status: 429 });Call an LLM from a Convex action and store the result in the database. Actions can make external API calls.
// convex/ai.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
import Anthropic from "@anthropic-ai/sdk";
export const summarize = action({
args: { text: v.string(), documentId: v.id("documents") },
handler: async (ctx, { text, documentId }) => {
const client = new Anthropic();
const message = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 512,
messages: [
{
role: "user",
content: `Summarize this in 2-3 sentences:\n\n${text}`,
},
],
});
const summary =
message.content[0].type === "text" ? message.content[0].text : "";
// Store the summary back in the database
await ctx.runMutation(api.documents.updateSummary, {
id: documentId,
summary,
});
return summary;
},
});Schedule a recurring AI task using Convex cron jobs. Runs a daily content digest.
// convex/crons.ts
import { cronJobs } from "convex/server";
import { internal } from "./_generated/api";
const crons = cronJobs();
// Run every day at 9am UTC
crons.daily(
"daily-digest",
{ hourUTC: 9, minuteUTC: 0 },
internal.digest.generateDailyDigest
);
export default crons;
// convex/digest.ts
import { internalAction } from "./_generated/server";
import { internal } from "./_generated/api";
export const generateDailyDigest = internalAction({
handler: async (ctx) => {
// Fetch recent items from the database
const items = await ctx.runQuery(internal.items.getRecent, {
since: Date.now() - 86_400_000,
});
// Generate digest with AI
const digest = await generateWithAI(
`Create a brief digest of these ${items.length} items: ${JSON.stringify(items)}`
);
// Store the digest
await ctx.runMutation(internal.digests.store, {
content: digest,
date: new Date().toISOString().slice(0, 10),
});
},
});Semantic search with Convex vector indexes. Embed documents and query by similarity for RAG pipelines.
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
documents: defineTable({
title: v.string(),
content: v.string(),
embedding: v.array(v.float64()),
}).vectorIndex("by_embedding", {
vectorField: "embedding",
dimensions: 1536,
}),
});
// convex/search.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
import { internal } from "./_generated/api";
export const searchSimilar = action({
args: { query: v.string(), limit: v.optional(v.number()) },
handler: async (ctx, { query, limit = 5 }) => {
// Generate embedding for the query
const embedding = await getEmbedding(query);
// Search by vector similarity
const results = await ctx.vectorSearch("documents", "by_embedding", {
vector: embedding,
limit,
});
return results;
},
});Quick token estimation without external dependencies. Good enough for context window management and cost estimates.
function estimateTokens(text: string): number {
// GPT/Claude models average ~4 characters per token for English text.
// This is a rough estimate - use tiktoken for exact counts.
return Math.ceil(text.length / 4);
}
function estimateCost(
inputTokens: number,
outputTokens: number,
model: "gpt-4o" | "claude-sonnet" | "claude-haiku"
): number {
const pricing = {
"gpt-4o": { input: 2.5 / 1_000_000, output: 10 / 1_000_000 },
"claude-sonnet": { input: 3 / 1_000_000, output: 15 / 1_000_000 },
"claude-haiku": { input: 0.25 / 1_000_000, output: 1.25 / 1_000_000 },
};
const p = pricing[model];
return inputTokens * p.input + outputTokens * p.output;
}
// Usage
const text = "Hello, how are you doing today?";
const tokens = estimateTokens(text);
const cost = estimateCost(tokens, 200, "claude-sonnet");
console.log(`~${tokens} input tokens, estimated cost: $${cost.toFixed(4)}`);Build reusable prompt templates with typed variables. Catches missing variables at compile time.
type PromptVars<T extends string> = Record<T, string>;
function createPrompt<T extends string>(
template: string,
vars: PromptVars<T>
): string {
return Object.entries<string>(vars).reduce(
(prompt, [key, value]) =>
prompt.replaceAll(`{{${key}}}`, value),
template
);
}
// Define templates
const templates = {
codeReview: `You are a senior {{language}} developer.
Review this code for bugs, security issues, and performance.
\`\`\`{{language}}
{{code}}
\`\`\`
Focus on: {{focus}}`,
summarize: `Summarize the following {{contentType}} in {{length}} sentences.
Audience: {{audience}}.
{{content}}`,
} as const;
// Usage
const prompt = createPrompt(templates.codeReview, {
language: "TypeScript",
code: "const data = JSON.parse(userInput);",
focus: "security vulnerabilities",
});Wrap any async function with automatic retries and exponential backoff. Essential for flaky AI API calls.
async function withRetry<T>(
fn: () => Promise<T>,
opts: { maxRetries?: number; baseDelay?: number; maxDelay?: number } = {}
): Promise<T> {
const { maxRetries = 3, baseDelay = 1000, maxDelay = 30_000 } = opts;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error: unknown) {
if (attempt === maxRetries) throw error;
// Don't retry client errors (4xx except 429)
if (error instanceof Error && "status" in error) {
const status = (error as { status: number }).status;
if (status >= 400 && status < 500 && status !== 429) throw error;
}
const delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
const jitter = delay * (0.5 + Math.random() * 0.5);
await new Promise((r) => setTimeout(r, jitter));
}
}
throw new Error("Unreachable");
}
// Usage
const response = await withRetry(
() => client.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 1024, messages }),
{ maxRetries: 3, baseDelay: 1000 }
);Comprehensive error handling for AI API calls. Covers rate limits, context length, and network errors.
interface AIError {
type: "rate_limit" | "context_length" | "auth" | "server" | "network" | "unknown";
message: string;
retryable: boolean;
retryAfter?: number;
}
function classifyError(error: unknown): AIError {
if (error instanceof Error && "status" in error) {
const status = (error as { status: number }).status;
const msg = error.message;
if (status === 429) {
return {
type: "rate_limit",
message: "Rate limit exceeded. Slow down requests.",
retryable: true,
retryAfter: 60,
};
}
if (status === 400 && msg.includes("context")) {
return {
type: "context_length",
message: "Input too long. Reduce prompt or conversation history.",
retryable: false,
};
}
if (status === 401 || status === 403) {
return {
type: "auth",
message: "Invalid or expired API key.",
retryable: false,
};
}
if (status >= 500) {
return {
type: "server",
message: "AI provider is having issues. Try again shortly.",
retryable: true,
retryAfter: 5,
};
}
}
if (error instanceof TypeError && (error.message.includes("fetch") || error.message.includes("network"))) {
return { type: "network", message: "Network error.", retryable: true };
}
return { type: "unknown", message: String(error), retryable: false };
}Stream chat completions from OpenAI token-by-token. Works with any OpenAI-compatible API endpoint.
import OpenAI from "openai";
const client = new OpenAI();
const stream = await client.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Explain monads in plain English." },
],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
process.stdout.write(content);
}
}Stream responses from Claude using the Anthropic SDK. Handles content block deltas and final message metadata.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const stream = client.messages.stream({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: "You are a senior TypeScript developer.",
messages: [
{ role: "user", content: "Explain the builder pattern with an example." },
],
});
stream.on("text", (text) => {
process.stdout.write(text);
});
const finalMessage = await stream.finalMessage();
console.log("\nInput tokens:", finalMessage.usage.input_tokens);
console.log("Output tokens:", finalMessage.usage.output_tokens);Define tools with JSON Schema and handle the tool call loop. Works with OpenAI, Anthropic, or any function-calling provider.
import OpenAI from "openai";
const client = new OpenAI();
const tools: OpenAI.ChatCompletionTool[] = [
{
type: "function",
function: {
name: "get_weather",
description: "Get current weather for a city",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name" },
units: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["city"],
},
},
},
];
const handlers: Record<string, (args: Record<string, unknown>) => Promise<string>> = {
get_weather: async ({ city, units }) => {
return JSON.stringify({ city, temp: 22, units: units ?? "celsius" });
},
};
const messages: OpenAI.ChatCompletionMessageParam[] = [
{ role: "user", content: "What's the weather in Tokyo?" },
];
let response = await client.chat.completions.create({
model: "gpt-4o", messages, tools,
});
// Tool call loop
while (response.choices[0].finish_reason === "tool_calls") {
const toolCalls = response.choices[0].message.tool_calls!;
messages.push(response.choices[0].message);
for (const call of toolCalls) {
const result = await handlers[call.function.name](
JSON.parse(call.function.arguments)
);
messages.push({ role: "tool", tool_call_id: call.id, content: result });
}
response = await client.chat.completions.create({
model: "gpt-4o", messages, tools,
});
}
console.log(response.choices[0].message.content);Build a simple RAG pipeline: embed documents, store vectors, and retrieve the most relevant chunks using cosine similarity.
import OpenAI from "openai";
const client = new OpenAI();
async function embed(texts: string[]): Promise<number[][]> {
const res = await client.embeddings.create({
model: "text-embedding-3-small",
input: texts,
});
return res.data.map((d) => d.embedding);
}
function cosineSimilarity(a: number[], b: number[]): number {
let dot = 0, magA = 0, magB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
magA += a[i] * a[i];
magB += b[i] * b[i];
}
return dot / (Math.sqrt(magA) * Math.sqrt(magB));
}
interface Doc { text: string; embedding: number[] }
const store: Doc[] = [];
async function addDocuments(texts: string[]) {
const embeddings = await embed(texts);
texts.forEach((text, i) => store.push({ text, embedding: embeddings[i] }));
}
async function search(query: string, topK = 3): Promise<string[]> {
const [queryEmb] = await embed([query]);
return store
.map((doc) => ({ text: doc.text, score: cosineSimilarity(queryEmb, doc.embedding) }))
.sort((a, b) => b.score - a.score)
.slice(0, topK)
.map((r) => r.text);
}
// Usage
await addDocuments([
"TypeScript supports generics for type-safe reusable code.",
"Next.js App Router uses React Server Components by default.",
"Convex provides real-time reactive queries out of the box.",
]);
const relevant = await search("How do I use generics?");
console.log(relevant);Complete MCP server boilerplate with a tool, resource, and prompt. Ready to extend with your own logic.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "my-mcp-server",
version: "1.0.0",
});
server.tool(
"lookup",
"Look up information by key",
{ key: z.string().describe("The lookup key") },
async ({ key }) => {
const data: Record<string, string> = { version: "2.1.0", status: "healthy" };
const value = data[key] ?? "Not found";
return { content: [{ type: "text", text: value }] };
}
);
server.resource("config", "config://app", async (uri) => ({
contents: [{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({ env: "production", debug: false }),
}],
}));
server.prompt(
"explain",
"Explain a concept simply",
{ topic: z.string() },
({ topic }) => ({
messages: [{
role: "user",
content: {
type: "text",
text: `Explain ${topic} like I'm a junior developer. Use an analogy.`,
},
}],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP server running on stdio");Connect to an MCP server from a client. Discover and call tools, read resources, and use prompts programmatically.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["./my-mcp-server.js"],
});
const client = new Client(
{ name: "my-client", version: "1.0.0" },
{ capabilities: {} }
);
await client.connect(transport);
// List available tools
const { tools } = await client.listTools();
console.log("Available tools:", tools.map((t) => t.name));
// Call a tool
const result = await client.callTool({
name: "lookup",
arguments: { key: "version" },
});
console.log("Tool result:", result.content);
// Read a resource
const { contents } = await client.readResource({ uri: "config://app" });
console.log("Resource:", contents[0].text);
// Use a prompt
const { messages } = await client.getPrompt({
name: "explain",
arguments: { topic: "dependency injection" },
});
console.log("Prompt:", messages[0].content);
await client.close();A basic agent loop that calls tools, handles errors, and retries. Foundation for any agentic workflow.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const toolHandlers: Record<string, (input: Record<string, unknown>) => Promise<string>> = {
read_file: async ({ path }) => {
const { readFileSync } = await import("fs");
return readFileSync(path as string, "utf-8");
},
run_command: async ({ command }) => {
const { execSync } = await import("child_process");
return execSync(command as string, { encoding: "utf-8", timeout: 30_000 });
},
};
async function agentLoop(task: string, maxTurns = 10) {
const messages: Anthropic.MessageParam[] = [{ role: "user", content: task }];
for (let turn = 0; turn < maxTurns; turn++) {
const response = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 4096,
tools: [
{ name: "read_file", description: "Read a file",
input_schema: { type: "object" as const, properties: { path: { type: "string" } }, required: ["path"] } },
{ name: "run_command", description: "Run a shell command",
input_schema: { type: "object" as const, properties: { command: { type: "string" } }, required: ["command"] } },
],
messages,
});
messages.push({ role: "assistant", content: response.content });
if (response.stop_reason === "end_turn") {
const text = response.content.find((b) => b.type === "text");
return text?.type === "text" ? text.text : "Done.";
}
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type === "tool_use") {
try {
const result = await toolHandlers[block.name](block.input as Record<string, unknown>);
toolResults.push({ type: "tool_result", tool_use_id: block.id, content: result });
} catch (err) {
toolResults.push({ type: "tool_result", tool_use_id: block.id, content: String(err), is_error: true });
}
}
}
messages.push({ role: "user", content: toolResults });
}
return "Max turns reached.";
}
const result = await agentLoop("Read package.json and tell me the project name.");
console.log(result);Force an LLM to return data matching a Zod schema. Parse, validate, and get full type inference on the result.
import OpenAI from "openai";
import { z } from "zod";
import { zodResponseFormat } from "openai/helpers/zod";
const client = new OpenAI();
const EventSchema = z.object({
title: z.string(),
date: z.string().describe("ISO 8601 date string"),
location: z.string(),
attendees: z.array(z.object({
name: z.string(),
role: z.enum(["speaker", "organizer", "attendee"]),
})),
isVirtual: z.boolean(),
});
type Event = z.infer<typeof EventSchema>;
const completion = await client.beta.chat.completions.parse({
model: "gpt-4o",
messages: [
{ role: "system", content: "Extract event details from the text." },
{ role: "user", content: "React Conf 2025 is May 15 in Las Vegas. Sarah Chen is speaking. Mike Torres is organizing." },
],
response_format: zodResponseFormat(EventSchema, "event"),
});
const event: Event | null = completion.choices[0].message.parsed;
console.log(event?.title); // "React Conf 2025"
console.log(event?.attendees); // [{ name: "Sarah Chen", role: "speaker" }, ...]Try multiple AI providers in sequence. If the primary model fails or is rate-limited, fall through to the next one.
import Anthropic from "@anthropic-ai/sdk";
import OpenAI from "openai";
interface ModelConfig {
name: string;
call: (prompt: string) => Promise<string>;
}
const models: ModelConfig[] = [
{
name: "claude-sonnet",
call: async (prompt) => {
const client = new Anthropic();
const res = await client.messages.create({
model: "claude-sonnet-4-20250514", max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
});
const block = res.content[0];
return block.type === "text" ? block.text : "";
},
},
{
name: "gpt-4o",
call: async (prompt) => {
const client = new OpenAI();
const res = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: prompt }],
});
return res.choices[0].message.content ?? "";
},
},
{
name: "gpt-4o-mini",
call: async (prompt) => {
const client = new OpenAI();
const res = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
});
return res.choices[0].message.content ?? "";
},
},
];
async function generateWithFallback(
prompt: string
): Promise<{ text: string; model: string }> {
for (const model of models) {
try {
const text = await model.call(prompt);
return { text, model: model.name };
} catch (err) {
console.error(`${model.name} failed: ${err instanceof Error ? err.message : err}`);
}
}
throw new Error("All models failed");
}
const { text, model } = await generateWithFallback("Explain closures in JS.");
console.log(`[${model}] ${text}`);Count tokens accurately using tiktoken for OpenAI models or a character-based estimate for others.
import { encoding_for_model, type TiktokenModel } from "tiktoken";
function countTokensExact(text: string, model: TiktokenModel = "gpt-4o"): number {
const enc = encoding_for_model(model);
const tokens = enc.encode(text);
enc.free();
return tokens.length;
}
function countTokensEstimate(text: string): number {
const codeRatio = (text.match(/[{}();=<>]/g)?.length ?? 0) / text.length;
const charsPerToken = codeRatio > 0.05 ? 3 : 4;
return Math.ceil(text.length / charsPerToken);
}
function countMessageTokens(
messages: { role: string; content: string }[],
model: TiktokenModel = "gpt-4o"
): number {
const enc = encoding_for_model(model);
let total = 0;
for (const msg of messages) {
total += 4; // message overhead
total += enc.encode(msg.role).length;
total += enc.encode(msg.content).length;
}
total += 2; // assistant reply priming
enc.free();
return total;
}
// Usage
const code = "function add(a: number, b: number) { return a + b; }";
console.log("Exact:", countTokensExact(code));
console.log("Estimate:", countTokensEstimate(code));
const msgs = [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is TypeScript?" },
];
console.log("Messages:", countMessageTokens(msgs));A type-safe Server Action with Zod validation, proper error handling, and a client form using useActionState.
// app/actions/contact.ts
"use server";
import { z } from "zod";
const ContactSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.string().email("Invalid email address"),
message: z.string().min(10, "Message must be at least 10 characters"),
});
type ActionState = {
success: boolean;
message: string;
errors?: Record<string, string[]>;
};
export async function submitContact(
_prev: ActionState,
formData: FormData
): Promise<ActionState> {
const raw = Object.fromEntries(formData);
const parsed = ContactSchema.safeParse(raw);
if (!parsed.success) {
return {
success: false,
message: "Validation failed",
errors: parsed.error.flatten().fieldErrors,
};
}
try {
await saveToDatabase(parsed.data);
return { success: true, message: "Message sent!" };
} catch {
return { success: false, message: "Something went wrong." };
}
}
// Client usage:
// const [state, action, pending] = useActionState(submitContact, {
// success: false, message: "",
// });Protect routes with Next.js middleware. Redirects unauthenticated users and handles public paths.
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const PUBLIC = ["/", "/login", "/signup", "/api/auth", "/api/webhook"];
function isPublic(path: string) {
return PUBLIC.some((p) => path === p || path.startsWith(p + "/"));
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
if (isPublic(pathname)) return NextResponse.next();
const token = request.cookies.get("session-token")?.value;
if (!token) {
const url = new URL("/login", request.url);
url.searchParams.set("redirect", pathname);
return NextResponse.redirect(url);
}
try {
const payload = JSON.parse(atob(token.split(".")[1]));
if (payload.exp * 1000 < Date.now()) {
return NextResponse.redirect(new URL("/login", request.url));
}
} catch {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\..*).*)"],
};Generate dynamic Open Graph images at the edge using next/og. Perfect for blog posts and social cards.
// app/api/og/route.tsx
import { ImageResponse } from "next/og";
import type { NextRequest } from "next/server";
export const runtime = "edge";
export async function GET(request: NextRequest) {
const { searchParams } = request.nextUrl;
const title = searchParams.get("title") ?? "Default Title";
const subtitle = searchParams.get("subtitle") ?? "";
return new ImageResponse(
(
<div style={{
height: "100%", width: "100%", display: "flex",
flexDirection: "column", justifyContent: "center",
padding: "60px 80px", backgroundColor: "#F4F4F0",
}}>
<div style={{
display: "flex", flexDirection: "column",
border: "3px solid #000", borderRadius: "24px",
padding: "48px", backgroundColor: "#fff",
}}>
<div style={{ fontSize: 52, fontWeight: 900 }}>{title}</div>
{subtitle && (
<div style={{ fontSize: 28, color: "#666", marginTop: 16 }}>
{subtitle}
</div>
)}
<div style={{ fontSize: 24, fontWeight: 700, marginTop: 32 }}>
developersdigest.tech
</div>
</div>
</div>
),
{ width: 1200, height: 630 }
);
}Incremental Static Regeneration with time-based and on-demand revalidation via API endpoint.
// app/blog/[slug]/page.tsx
import { notFound } from "next/navigation";
export const revalidate = 60; // seconds
export async function generateStaticParams() {
const posts = await getTopPosts(20);
return posts.map((post) => ({ slug: post.slug }));
}
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPostBySlug(slug);
if (!post) notFound();
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
);
}
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from "next/cache";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export async function POST(request: NextRequest) {
const secret = request.headers.get("x-revalidate-secret");
if (secret !== process.env.REVALIDATE_SECRET) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { path, tag } = await request.json();
if (tag) revalidateTag(tag);
else if (path) revalidatePath(path);
else return NextResponse.json({ error: "Missing path or tag" }, { status: 400 });
return NextResponse.json({ revalidated: true, now: Date.now() });
}Fetch multiple data sources in parallel with Promise.all. Avoids waterfall requests in Server Components.
// app/dashboard/page.tsx
async function getUser(id: string) {
const res = await fetch(`https://api.example.com/users/${id}`, {
next: { revalidate: 300 },
});
return res.json() as Promise<{ name: string; email: string }>;
}
async function getStats(id: string) {
const res = await fetch(`https://api.example.com/users/${id}/stats`, {
next: { revalidate: 60 },
});
return res.json() as Promise<{ posts: number; views: number }>;
}
async function getNotifications(id: string) {
const res = await fetch(`https://api.example.com/users/${id}/notifications`, {
cache: "no-store",
});
return res.json() as Promise<{ id: string; message: string }[]>;
}
export default async function Dashboard() {
const userId = "user_123";
// All three fire simultaneously
const [user, stats, notifications] = await Promise.all([
getUser(userId),
getStats(userId),
getNotifications(userId),
]);
return (
<div>
<h1>Welcome, {user.name}</h1>
<p>{stats.posts} posts, {stats.views} views</p>
<h2>Notifications ({notifications.length})</h2>
<ul>
{notifications.map((n) => (
<li key={n.id}>{n.message}</li>
))}
</ul>
</div>
);
}Next.js error boundary with retry functionality. Catches errors at the route segment level.
// app/dashboard/error.tsx
"use client";
import { useEffect } from "react";
export default function DashboardError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("Dashboard error:", error);
}, [error]);
return (
<div className="flex flex-col items-center justify-center min-h-[400px] p-8">
<h2 className="text-2xl font-bold mb-4">Something went wrong</h2>
<p className="text-gray-600 mb-6 text-center max-w-md">
{error.message || "An unexpected error occurred."}
</p>
{error.digest && (
<p className="text-xs text-gray-400 mb-4">Error ID: {error.digest}</p>
)}
<div className="flex gap-3">
<button
onClick={reset}
className="px-6 py-2 bg-black text-white rounded-full font-medium"
>
Try again
</button>
<a
href="/"
className="px-6 py-2 border-2 border-black rounded-full font-medium"
>
Go home
</a>
</div>
</div>
);
}Stream page content progressively using Suspense. The shell renders instantly while slow data loads in.
import { Suspense } from "react";
async function getUser() {
const res = await fetch("https://api.example.com/user", { cache: "no-store" });
return res.json() as Promise<{ name: string }>;
}
async function AnalyticsPanel() {
const res = await fetch("https://api.example.com/analytics", { cache: "no-store" });
const data = (await res.json()) as { views: number; revenue: number };
return (
<div className="border rounded-xl p-6">
<h2 className="font-bold mb-2">Analytics</h2>
<p>{data.views.toLocaleString()} views</p>
</div>
);
}
async function RecommendationsPanel() {
const res = await fetch("https://api.example.com/recs", { cache: "no-store" });
const items = (await res.json()) as { title: string; url: string }[];
return (
<div className="border rounded-xl p-6">
<h2 className="font-bold mb-2">Recommended</h2>
<ul>{items.map((i) => <li key={i.url}><a href={i.url}>{i.title}</a></li>)}</ul>
</div>
);
}
function Skeleton() {
return (
<div className="border rounded-xl p-6 animate-pulse">
<div className="h-6 bg-gray-200 rounded w-1/3 mb-4" />
<div className="h-4 bg-gray-200 rounded w-2/3" />
</div>
);
}
export default async function Dashboard() {
const user = await getUser();
return (
<div className="p-8">
<h1 className="text-3xl font-bold mb-8">Welcome, {user.name}</h1>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Suspense fallback={<Skeleton />}><AnalyticsPanel /></Suspense>
<Suspense fallback={<Skeleton />}><RecommendationsPanel /></Suspense>
</div>
</div>
);
}API route with sliding-window rate limiting. Returns rate limit headers to clients.
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const windows = new Map<string, number[]>();
function rateLimit(key: string, limit: number, windowMs: number) {
const now = Date.now();
const timestamps = (windows.get(key) ?? []).filter((t) => t > now - windowMs);
if (timestamps.length >= limit) {
return { allowed: false, remaining: 0, resetAt: timestamps[0] + windowMs };
}
timestamps.push(now);
windows.set(key, timestamps);
return { allowed: true, remaining: limit - timestamps.length, resetAt: now + windowMs };
}
export async function POST(request: NextRequest) {
const ip = request.headers.get("x-forwarded-for") ?? "anon";
const { allowed, remaining, resetAt } = rateLimit(ip, 20, 60_000);
const headers = {
"X-RateLimit-Limit": "20",
"X-RateLimit-Remaining": String(remaining),
"X-RateLimit-Reset": String(Math.ceil(resetAt / 1000)),
};
if (!allowed) {
return NextResponse.json(
{ error: "Rate limit exceeded" },
{ status: 429, headers: {
...headers,
"Retry-After": String(Math.ceil((resetAt - Date.now()) / 1000)),
}}
);
}
const body = await request.json();
return NextResponse.json({ success: true, data: body }, { headers });
}Subscribe to live data from Convex. Data updates automatically when the database changes.
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
messages: defineTable({
author: v.string(),
body: v.string(),
createdAt: v.number(),
}).index("by_time", ["createdAt"]),
});
// convex/messages.ts
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
export const list = query({
args: { limit: v.optional(v.number()) },
handler: async (ctx, { limit = 50 }) => {
return ctx.db.query("messages")
.withIndex("by_time")
.order("desc")
.take(limit);
},
});
export const send = mutation({
args: { author: v.string(), body: v.string() },
handler: async (ctx, { author, body }) => {
await ctx.db.insert("messages", { author, body, createdAt: Date.now() });
},
});
// Client component:
// "use client";
// import { useQuery, useMutation } from "convex/react";
// import { api } from "@/convex/_generated/api";
//
// const messages = useQuery(api.messages.list, { limit: 50 });
// const send = useMutation(api.messages.send);
// await send({ author: "User", body: "Hello!" });Protect pages and API routes with Clerk. Includes middleware, server-side auth, and API route protection.
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
const isProtected = createRouteMatcher(["/dashboard(.*)", "/settings(.*)", "/api/user(.*)"]);
export default clerkMiddleware(async (auth, request) => {
if (isProtected(request)) await auth.protect();
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\..*).*)"],
};
// app/dashboard/page.tsx
import { auth, currentUser } from "@clerk/nextjs/server";
import { redirect } from "next/navigation";
export default async function DashboardPage() {
const { userId } = await auth();
if (!userId) redirect("/sign-in");
const user = await currentUser();
return (
<div>
<h1>Welcome, {user?.firstName ?? "there"}</h1>
<p>Email: {user?.emailAddresses[0]?.emailAddress}</p>
</div>
);
}
// app/api/user/route.ts
import { auth } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
export async function GET() {
const { userId } = await auth();
if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
const data = await getUserFromDb(userId);
return NextResponse.json(data);
}Boilerplate for a Node.js CLI tool using Commander. Includes subcommands, options, and arguments.
#!/usr/bin/env node
import { Command } from "commander";
import { readFileSync } from "fs";
const pkg = JSON.parse(readFileSync("./package.json", "utf-8"));
const program = new Command()
.name("mycli")
.description("A useful CLI tool")
.version(pkg.version);
program
.command("init")
.description("Initialize a new project")
.option("-t, --template <name>", "template to use", "default")
.option("--no-git", "skip git initialization")
.action(async (opts) => {
console.log(`Template: ${opts.template}, Git: ${opts.git}`);
});
program
.command("build")
.description("Build the project")
.option("-w, --watch", "watch for changes")
.option("-o, --output <dir>", "output directory", "dist")
.action(async (opts) => {
console.log(`Building to ${opts.output}...`);
if (opts.watch) console.log("Watching...");
});
program
.command("deploy")
.description("Deploy to production")
.argument("<environment>", "target environment")
.option("--dry-run", "simulate deployment")
.action(async (env, opts) => {
console.log(`Deploying to ${env}${opts.dryRun ? " (dry run)" : ""}`);
});
program.parse();Build interactive CLI prompts with @inquirer/prompts. Text input, selections, confirmations, and multi-select.
import { input, select, confirm, checkbox } from "@inquirer/prompts";
async function setup() {
const name = await input({
message: "Project name:",
default: "my-app",
validate: (v) => /^[a-z0-9-]+$/.test(v) || "Lowercase, numbers, dashes only",
});
const framework = await select({
message: "Framework:",
choices: [
{ name: "Next.js", value: "nextjs", description: "Full-stack React" },
{ name: "Remix", value: "remix", description: "Web standards" },
{ name: "Astro", value: "astro", description: "Content-first" },
],
});
const features = await checkbox({
message: "Features:",
choices: [
{ name: "TypeScript", value: "ts", checked: true },
{ name: "ESLint", value: "eslint", checked: true },
{ name: "Tailwind", value: "tailwind" },
{ name: "Vitest", value: "vitest" },
{ name: "CI/CD", value: "ci" },
],
});
const ok = await confirm({
message: `Create ${name} with ${framework}?`,
default: true,
});
if (ok) {
console.log("Creating...", { name, framework, features });
}
}
setup();Show a progress bar during long-running CLI operations using cli-progress.
import cliProgress from "cli-progress";
async function processFiles(files: string[]) {
const bar = new cliProgress.SingleBar({
format: " {bar} {percentage}% | {value}/{total} | ETA: {eta}s",
barCompleteChar: "\u2588",
barIncompleteChar: "\u2591",
hideCursor: true,
});
bar.start(files.length, 0);
for (const file of files) {
await processFile(file);
bar.increment();
}
bar.stop();
console.log("Done!");
}
// Multi-bar for parallel work
async function parallelDownload(urls: string[]) {
const multi = new cliProgress.MultiBar({
format: " {name} | {bar} | {percentage}%",
clearOnComplete: false,
});
const tasks = urls.map((url, i) => {
const bar = multi.create(100, 0, { name: `File ${i + 1}` });
return download(url, (pct: number) => bar.update(pct));
});
await Promise.all(tasks);
multi.stop();
}
// Simple spinner
function spinner(msg: string) {
const frames = ["-", "\\", "|", "/"];
let i = 0;
const id = setInterval(() => {
process.stdout.write(`\r${frames[i++ % 4]} ${msg}`);
}, 100);
return {
stop: (final: string) => { clearInterval(id); process.stdout.write(`\r${final}\n`); },
};
}Watch files for changes with debounced callbacks. Prevents duplicate triggers during rapid saves.
import { watch } from "chokidar";
function createWatcher(
patterns: string[],
callback: (paths: string[]) => void,
debounceMs = 300
) {
let timer: NodeJS.Timeout | null = null;
let pending = new Set<string>();
const flush = () => {
const paths = Array.from(pending);
pending = new Set();
if (paths.length > 0) callback(paths);
};
const enqueue = (path: string) => {
pending.add(path);
if (timer) clearTimeout(timer);
timer = setTimeout(flush, debounceMs);
};
const watcher = watch(patterns, {
ignored: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
persistent: true,
ignoreInitial: true,
});
watcher.on("change", enqueue).on("add", enqueue).on("unlink", enqueue);
watcher.on("ready", () => console.log("Watching..."));
return watcher;
}
// Usage
const w = createWatcher(
["src/**/*.ts", "src/**/*.tsx"],
(files) => {
console.log("Changed:", files);
// rebuild, retest, etc.
},
200
);
process.on("SIGINT", () => { w.close(); process.exit(0); });Create a pull request on GitHub using Octokit. Includes branch creation, file commits, and PR creation.
import { Octokit } from "@octokit/rest";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
async function createPR(opts: {
owner: string; repo: string; title: string; body: string;
head: string; base?: string;
files: { path: string; content: string }[];
}) {
const { owner, repo, title, body, head, base = "main", files } = opts;
// Get base SHA
const { data: ref } = await octokit.git.getRef({ owner, repo, ref: `heads/${base}` });
const sha = ref.object.sha;
// Create branch
await octokit.git.createRef({ owner, repo, ref: `refs/heads/${head}`, sha });
// Create file blobs
const blobs = await Promise.all(files.map((f) =>
octokit.git.createBlob({
owner, repo,
content: Buffer.from(f.content).toString("base64"),
encoding: "base64",
}).then((r) => ({ path: f.path, sha: r.data.sha }))
));
// Create tree + commit
const { data: tree } = await octokit.git.createTree({
owner, repo, base_tree: sha,
tree: blobs.map((b) => ({
path: b.path, mode: "100644" as const, type: "blob" as const, sha: b.sha,
})),
});
const { data: commit } = await octokit.git.createCommit({
owner, repo, message: title, tree: tree.sha, parents: [sha],
});
await octokit.git.updateRef({ owner, repo, ref: `heads/${head}`, sha: commit.sha });
const { data: pr } = await octokit.pulls.create({ owner, repo, title, body, head, base });
console.log(`PR: ${pr.html_url}`);
return pr;
}Generate a Docker Compose file for Postgres, Redis, and a Next.js app with health checks.
import { writeFileSync } from "fs";
const compose = `services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpass
POSTGRES_DB: myapp
ports: ["5432:5432"]
volumes: [pgdata:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U devuser -d myapp"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
ports: ["6379:6379"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
retries: 5
app:
build: { context: ., dockerfile: Dockerfile }
restart: unless-stopped
ports: ["3000:3000"]
environment:
DATABASE_URL: postgresql://devuser:devpass@db:5432/myapp
REDIS_URL: redis://redis:6379
NODE_ENV: development
depends_on:
db: { condition: service_healthy }
redis: { condition: service_healthy }
volumes: [./src:/app/src]
volumes:
pgdata:`;
writeFileSync("docker-compose.yml", compose);
console.log("Created docker-compose.yml");
console.log("Run: docker compose up -d");Generate a CI/CD workflow with lint, type check, tests, build, and deploy steps.
import { mkdirSync, writeFileSync } from "fs";
const yml = `name: CI/CD
on:
push: { branches: [main] }
pull_request: { branches: [main] }
concurrency:
group: ci-\${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: npm }
- run: npm ci
- run: npm run lint
- run: npx tsc --noEmit
- run: npm test -- --coverage
- run: npm run build
- uses: actions/upload-artifact@v4
if: always()
with: { name: coverage, path: coverage/ }
deploy:
needs: test
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
run: |
curl -X POST "\${{ secrets.DEPLOY_WEBHOOK }}" \\
-H "Authorization: Bearer \${{ secrets.DEPLOY_TOKEN }}"`;
mkdirSync(".github/workflows", { recursive: true });
writeFileSync(".github/workflows/ci.yml", yml);
console.log("Created .github/workflows/ci.yml");Parse cron expressions into human-readable descriptions. No external dependencies.
function describeCron(expr: string): string {
const [minute, hour, dom, month, dow] = expr.trim().split(/\s+/);
if (!minute || !hour || !dom || !month || !dow) {
throw new Error("Invalid cron: need 5 fields");
}
const parts: string[] = [];
if (minute === "*" && hour === "*") parts.push("Every minute");
else if (minute.startsWith("*/")) parts.push(`Every ${minute.slice(2)} minutes`);
else if (hour === "*") parts.push(`At minute ${minute} of every hour`);
else parts.push(`At ${hour.padStart(2, "0")}:${minute.padStart(2, "0")}`);
if (dow !== "*") {
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const names = dow.split(",").map((d) => {
if (d.includes("-")) {
const [a, b] = d.split("-").map(Number);
return `${days[a]}-${days[b]}`;
}
return days[Number(d)] ?? d;
});
parts.push(`on ${names.join(", ")}`);
}
if (dom !== "*") parts.push(`on day ${dom}`);
if (month !== "*") {
const months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
parts.push(`in ${month.split(",").map((m) => months[Number(m)] ?? m).join(", ")}`);
}
return parts.join(" ");
}
console.log(describeCron("0 9 * * 1-5")); // At 09:00 on Mon-Fri
console.log(describeCron("*/15 * * * *")); // Every 15 minutes
console.log(describeCron("0 0 1 * *")); // At 00:00 on day 1
console.log(describeCron("30 14 * * 0")); // At 14:30 on SunVerify HMAC webhook signatures. Works with GitHub, Stripe, and any HMAC-based provider.
import { createHmac, timingSafeEqual } from "crypto";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
function verifySignature(
payload: string | Buffer,
signature: string,
secret: string,
prefix = "sha256="
): boolean {
const expected = prefix + createHmac("sha256", secret)
.update(payload)
.digest("hex");
try {
return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
} catch {
return false;
}
}
// app/api/webhook/route.ts
export async function POST(request: NextRequest) {
const body = await request.text();
const sig = request.headers.get("x-hub-signature-256");
if (!sig) {
return NextResponse.json({ error: "Missing signature" }, { status: 401 });
}
if (!verifySignature(body, sig, process.env.WEBHOOK_SECRET!)) {
return NextResponse.json({ error: "Invalid signature" }, { status: 403 });
}
const event = JSON.parse(body);
switch (event.action) {
case "opened":
console.log("PR opened:", event.pull_request?.title);
break;
case "closed":
console.log("PR closed:", event.pull_request?.title);
break;
default:
console.log("Event:", event.action);
}
return NextResponse.json({ received: true });
}Validate environment variables at startup with Zod. Fails fast with clear messages.
import { z } from "zod";
const envSchema = z.object({
DATABASE_URL: z.string().url("Must be a valid URL"),
OPENAI_API_KEY: z.string().startsWith("sk-", "Must start with sk-"),
CLERK_SECRET_KEY: z.string().min(1, "Required"),
WEBHOOK_SECRET: z.string().min(16, "Min 16 characters"),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().startsWith("pk_"),
NEXT_PUBLIC_APP_URL: z.string().url().default("http://localhost:3000"),
});
type Env = z.infer<typeof envSchema>;
function validateEnv(): Env {
const result = envSchema.safeParse(process.env);
if (!result.success) {
console.error("\n Environment validation failed:\n");
for (const [key, msgs] of Object.entries(result.error.flatten().fieldErrors)) {
console.error(` ${key}: ${msgs?.join(", ")}`);
}
console.error("\n Check your .env.local file.\n");
process.exit(1);
}
return result.data;
}
export const env = validateEnv();
// Usage: import { env } from "@/lib/env";
// connect(env.DATABASE_URL); // string
// listen(env.PORT); // number
New tutorials, open-source projects, and deep dives on coding agents - delivered weekly.