Skip to content

api-route-generator

Generate Next.js Route Handlers (API routes) with proper patterns and validation.

From plugin
f5-framework
24104 skills104 agents69 commands
Install
$ npx -y skills add Fujigo-Software/f5-framework-claude --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

Generate Next.js Route Handlers (API routes) with proper patterns and validation.

Agent definition

api-route-generator.md

Next.js API Route Generator Agent

Role

Generate Next.js Route Handlers (API routes) with proper patterns and validation.

Triggers

  • "create api"
  • "api route"
  • "route handler"
  • "nextjs api"

Capabilities

  • Generate route.ts files with HTTP methods
  • Implement request/response handling
  • Add Zod validation
  • Handle authentication
  • Generate typed responses
  • Create OpenAPI documentation

Input Requirements

required:
  - route: string          # API route path
  - methods: array         # HTTP methods (GET, POST, etc.)

optional:
  - entity: string         # Entity name for CRUD
  - auth_required: boolean # Requires authentication
  - validation: object     # Zod schema definition
  - response_type: string  # Response format

Output Structure

app/api/{route}/
├── route.ts              # Route handler
└── types.ts              # Request/Response types (if complex)

Generation Rules

1. Basic Route Handler

// app/api/products/route.ts
import { NextResponse } from 'next/server';
import { z } from 'zod';
import { db } from '@/lib/db';

// GET /api/products
export async function GET(request: Request) {
  try {
    const { searchParams } = new URL(request.url);
    const page = Number(searchParams.get('page')) || 1;
    const limit = Number(searchParams.get('limit')) || 10;
    const skip = (page - 1) * limit;

    const [products, total] = await Promise.all([
      db.product.findMany({
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
      }),
      db.product.count(),
    ]);

    return NextResponse.json({
      data: products,
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
      },
    });
  } catch (error) {
    console.error('GET /api/products error:', error);
    return NextResponse.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    );
  }
}

// POST /api/products
const createProductSchema = z.object({
  name: z.string().min(2).max(100),
  description: z.string().max(1000).optional(),
  price: z.number().min(0),
  categoryId: z.string().uuid(),
});

export async function POST(request: Request) {
  try {
    const body = await request.json();

    const validatedData = createProductSchema.safeParse(body);

    if (!validatedData.success) {
      return NextResponse.json(
        { error: 'Validation failed', details: validatedData.error.flatten() },
        { status: 400 }
      );
    }

    const product = await db.product.create({
      data: validatedData.data,
    });

    return NextResponse.json(product, { status: 201 });
  } catch (error) {
    console.error('POST /api/products error:', error);
    return NextResponse.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    );
  }
}

2. Dynamic Route Handler

// app/api/products/[id]/route.ts
import { NextResponse } from 'next/server';
import { z } from 'zod';
import { db } from '@/lib/db';

interface RouteParams {
  params: { id: string };
}

// GET /api/products/[id]
export async function GET(request: Request, { params }: RouteParams) {
  try {
    const product = await db.product.findUnique({
      where: { id: params.id },
      include: { category: true },
    });

    if (!product) {
      return NextResponse.json(
        { error: 'Product not found' },
        { status: 404 }
      );
    }

    return NextResponse.json(product);
  } catch (error) {
    console.error(`GET /api/products/${params.id} error:`, error);
    return NextResponse.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    );
  }
}

// PATCH /api/products/[id]
const updateProductSchema = z.object({
  name: z.string().min(2).max(100).optional(),
  description: z.string().max(1000).optional(),
  price: z.number().min(0).optional(),
  categoryId: z.string().uuid().optional(),
});

export async function PATCH(request: Request, { params }: RouteParams) {
  try {
    const body = await request.json();

    const validatedData = updateProductSchema.safeParse(body);

    if (!validatedData.success) {
      return NextResponse.json(
        { error: 'Validation failed', details: validatedData.error.flatten() },
        { status: 400 }
      );
    }

    const product = await db.product.update({
      where: { id: params.id },
      data: validatedData.data,
    });

    return NextResponse.json(product);
  } catch (error) {
    if ((error as any).code === 'P2025') {
      return NextResponse.json(
        { error: 'Product not found' },
        { status: 404 }
      );
    }
    console.error(`PATCH /api/products/${params.id} error:`, error);
    return NextResponse.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    );
  }
}

// DELETE /api/products/[id]
export async function DELETE(request: Request, { params }: RouteParams) {
  try {
    await db.product.delete({
      where: { id: params.id },
    });

    return new NextResponse(null, { status: 204 });
  } catch (error) {
    if ((error as any).code === 'P2025') {
      return NextResponse.json(
        { error: 'Product not found' },
        { status: 404 }
      );
    }
    console.error(`DELETE /api/products/${params.id} error:`, error);
    return NextResponse.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    );
  }
}

3. Authenticated Route

// app/api/user/profile/route.ts
import { NextResponse } from 'next/server';
import { auth } from '@/lib/auth';
import { db } from '@/lib/db';

export async function GET() {
  const session = await auth();

  if (!session?.user) {
    return NextResponse.json(
      { error: 'Unauthorized' },
      { status: 401 }
    );
  }

  const user = await db.user.findUnique({
    where: { id: session.user.id },
    select: {
      id: true,
      name: true,
      email: true,
      image: true,
      createdAt: true,
    },
  });

  return NextResponse.json(user);
}

export async function PATCH(reque
Read more
Ships withf5-framework

AI-Powered Development Framework for Claude Code

Get the whole plugin, auto-invoked
Stats
24
Stars
0
Views
8
Forks
Quiet
Maintenance
Python
Language
MIT
License
6mo ago
Last commit
6mo ago
Created

Repo: Fujigo-Software/f5-framework-claude