nextjs-api-guidelines
This repository contains a complete **Cursor (Claude AI)** infrastructure that provides **auto-activating skills**, **specialized agents**, and **powerful commands** to help you build better applications faster.
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- Updated: 2025-11-22
Download the latest version (v1.0.0) from this page.
SKILL.md
# Next.js API Routes Guidelines
**Skill Version:** 1.0.0
**Stack:** Next.js 14+ App Router, TypeScript
**Purpose:** Guidelines for building Next.js API routes and server actions
## Overview
This skill provides comprehensive guidelines for creating API routes and server actions in Next.js using the App Router.
## When to Use This Skill
Load this skill when working on:
- API route handlers
- Server actions
- Middleware
- Backend logic
- Database operations via API
- Authentication flows
## Core Guidelines
### 1. API Route Structure
#### Basic Route Handler
```typescript
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
try {
// Your logic here
const users = []; // fetch from database
return NextResponse.json({ data: users }, { status: 200 });
} catch (error) {
console.error('Error fetching users:', error);
return NextResponse.json(
{ error: 'Failed to fetch users' },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Validate input
if (!body.name || !body.email) {
return NextResponse.json(
{ error: 'Name and email are required' },
{ status: 400 }
);
}
// Your logic here
const newUser = {}; // create in database
return NextResponse.json({ data: newUser }, { status: 201 });
} catch (error) {
console.error('Error creating user:', error);
return NextResponse.json(
{ error: 'Failed to create user' },
{ status: 500 }
);
}
}
```
#### Dynamic Route Handler
```typescript
// app/api/users/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';
interface RouteParams {
params: { id: string };
}
export async function GET(
request: NextRequest,
{ params }: RouteParams
) {
try {
const { id } = params;
// Fetch user from database
const user = {}; // await getUser(id);
if (!user) {
return NextResponse.json(
{ error: 'User not found' },
{ status: 404 }
);
}
return NextResponse.json({ data: user });
} catch (error) {
console.error('Error fetching user:', error);
return NextResponse.json(
{ error: 'Failed to fetch user' },
{ status: 500 }
);
}
}
export async function PUT(
request: NextRequest,
{ params }: RouteParams
) {
try {
const { id } = params;
const body = await request.json();
// Update user in database
const updatedUser = {}; // await updateUser(id, body);
return NextResponse.json({ data: updatedUser });
} catch (error) {
console.error('Error updating user:', error);
return NextResponse.json(
{ error: 'Failed to update user' },
{ status: 500 }
);
}
}
export async function DELETE(
request: NextRequest,
{ params }: RouteParams
) {
try {
const { id } = params;
// Delete user from database
// await deleteUser(id);
return NextResponse.json({ message: 'User deleted' });
} catch (error) {
console.error('Error deleting user:', error);
return NextResponse.json(
{ error: 'Failed to delete user' },
{ status: 500 }
);
}
}
```
### 2. Server Actions
```typescript
// app/actions/users.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createUser(formData: FormData) {
try {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
// Validate
if (!name || !email) {
return { error: 'Name and email are required' };
}
// Create user in database
const user = {}; // await createUserInDb({ name, email });
// Revalidate the users page
revalidatePath('/users');
return { success: true, data: user };
} catch (error) {
console.error('Error creating user:', error);
return { error: 'Failed to create user' };
}
}
export async function updateUser(id: string, formData: FormData) {
try {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
// Update user in database
const user = {}; // await updateUserInDb(id, { name, email });
revalidatePath('/users');
revalidatePath(`/users/${id}`);
return { success: true, data: user };
} catch (error) {
console.error('Error updating user:', error);
return { error: 'Failed to update user' };
}
}
export async function deleteUser(id: string) {
try {
// Delete user from database
// await deleteUserFromDb(id);
revalidatePath('/users');
return { success: true };
} catch (error) {
console.error('Error deleting user:', error);
return { error: 'Failed to delete user' };
}
}
```
#### Using Server Actions in Components
```typescript
'use client';
import { useFormStatus } from 'react-dom';
import { createUser } from '@/app/actions/users';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Creating...' : 'Create User'}
</button>
);
}
export function CreateUserForm() {
return (
<form action={createUser}>
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<SubmitButton />
</form>
);
}
```
### 3. Authentication & Authorization
```typescript
// lib/auth.ts
import { NextRequest } from 'next/server';
import { adminAuth } from '@/lib/firebase/admin';
export async function verifyAuth(request: NextRequest): Promise<string | null> {
try {
const token = request.headers.get('Authorization')?.split('Bearer ')[1];
if (!token) {
return null;
}
const decodedToken = await adminAuth.verifyIdToken(token);
return decodedToken.uid;
} catch (error) {
console.error('Error verifying auth:', error);
return null;
}
}
// app/api/protected/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyAuth } from '@/lib/auth';
export async function GET(request: NextRequest) {
const userId = await verifyAuth(request);
if (!userId) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
// Your protected logic here
return NextResponse.json({ data: 'Protected data', userId });
}
```
### 4. Error Handling
```typescript
// lib/api-error.ts
export class APIError extends Error {
constructor(
message: string,
public statusCode: number = 500,
public code?: string
) {
super(message);
this.name = 'APIError';
}
}
// lib/api-handler.ts
import { NextRequest, NextResponse } from 'next/server';
import { APIError } from './api-error';
type Handler = (
request: NextRequest,
context?: any
) => Promise<NextResponse>;
export function withErrorHandler(handler: Handler): Handler {
return async (request: NextRequest, context?: any) => {
try {
return await handler(request, context);
} catch (error) {
console.error('API Error:', error);
if (error instanceof APIError) {
return NextResponse.json(
{ error: error.message, code: error.code },
{ status: error.statusCode }
);
}
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
};
}
// Usage
// app/api/example/route.ts
import { withErrorHandler } from '@/lib/api-handler';
import { APIError } from '@/lib/api-error';
export const GET = withErrorHandler(async (request) => {
// Your logic
throw new APIError('Not found', 404, 'USER_NOT_FOUND');
});
```
### 5. Request Validation
```typescript
// lib/validation.ts
import { z } from 'zod';
import { NextRequest } from 'next/server';
export async function validateRequest<T>(
request: NextRequest,
schema: z.ZodSchema<T>
): Promise<T> {
const body = await request.json();
return schema.parse(body);
}
// app/api/users/route.ts
import { z } from 'zod';
import { validateRequest } from '@/lib/validation';
import { APIError } from '@/lib/api-error';
const createUserSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
age: z.number().min(18).optional(),
});
export async function POST(request: NextRequest) {
try {
const data = await validateRequest(request, createUserSchema);
// Create user with validated data
const user = {}; // await createUser(data);
return NextResponse.json({ data: user }, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: 'Validation failed', details: error.errors },
{ status: 400 }
);
}
throw error;
}
}
```
### 6. CORS Configuration
```typescript
// lib/cors.ts
import { NextResponse } from 'next/server';
export function corsHeaders() {
return {
'Access-Control-Allow-Origin': process.env.ALLOWED_ORIGIN || '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
}
// app/api/users/route.ts
import { corsHeaders } from '@/lib/cors';
export async function OPTIONS() {
return NextResponse.json({}, { headers: corsHeaders() });
}
export async function GET(request: NextRequest) {
const data = {}; // your logic
return NextResponse.json(
{ data },
{ headers: corsHeaders() }
);
}
```
### 7. Rate Limiting
```typescript
// lib/rate-limit.ts
import { NextRequest } from 'next/server';
const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
export function checkRateLimit(
request: NextRequest,
maxRequests: number = 10,
windowMs: number = 60000
): boolean {
const ip = request.ip || 'unknown';
const now = Date.now();
const record = rateLimitMap.get(ip);
if (!record || now > record.resetTime) {
rateLimitMap.set(ip, { count: 1, resetTime: now + windowMs });
return true;
}
if (record.count >= maxRequests) {
return false;
}
record.count++;
return true;
}
// app/api/limited/route.ts
import { checkRateLimit } from '@/lib/rate-limit';
export async function GET(request: NextRequest) {
if (!checkRateLimit(request, 10, 60000)) {
return NextResponse.json(
{ error: 'Too many requests' },
{ status: 429 }
);
}
// Your logic
return NextResponse.json({ data: 'success' });
}
```
## Best Practices
### 1. Response Format
Always use consistent response format:
```typescript
// Success
{ data: any, message?: string }
// Error
{ error: string, code?: string, details?: any }
```
### 2. Status Codes
- 200: Success (GET, PUT, PATCH)
- 201: Created (POST)
- 204: No Content (DELETE)
- 400: Bad Request
- 401: Unauthorized
- 403: Forbidden
- 404: Not Found
- 429: Too Many Requests
- 500: Internal Server Error
### 3. Security
- ✅ Always validate input
- ✅ Use environment variables for secrets
- ✅ Implement authentication for protected routes
- ✅ Use HTTPS in production
- ✅ Sanitize user input
- ✅ Implement rate limiting
### 4. Performance
- ✅ Use database indexes
- ✅ Implement caching where appropriate
- ✅ Use connection pooling
- ✅ Optimize database queries
- ✅ Enable compression
---
**Last Updated:** 2025-01-10
**Maintained By:** Development Team