BMAD-METHOD/expansion-packs/bmad-nextjs-fullstack/tasks/create-api-endpoint.md

3.9 KiB

Create API Endpoint

Task Overview

Create a new API endpoint in Next.js with proper TypeScript typing, validation, and error handling.

Prerequisites

  • Next.js project with App Router
  • TypeScript configured
  • Understanding of HTTP methods and status codes

Steps

1. Create API Route File

Create src/app/api/{endpoint}/route.ts:

import { NextRequest, NextResponse } from 'next/server';

// Define request/response types
interface RequestBody {
  // Define your request body structure
}

interface ResponseData {
  // Define your response structure
}

export async function GET(request: NextRequest) {
  try {
    // Handle GET request logic
    const data: ResponseData = {
      // Your response data
    };

    return NextResponse.json(data, { status: 200 });
  } catch (error) {
    console.error('API Error:', error);
    return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
  }
}

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

    // Validate request body
    if (!body) {
      return NextResponse.json({ error: 'Request body is required' }, { status: 400 });
    }

    // Handle POST request logic
    const data: ResponseData = {
      // Your response data
    };

    return NextResponse.json(data, { status: 201 });
  } catch (error) {
    console.error('API Error:', error);
    return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
  }
}

2. Add Request Validation (Optional)

Install and use Zod for validation:

npm install zod
import { z } from 'zod';

const requestSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const validatedData = requestSchema.parse(body);

    // Use validatedData safely
  } catch (error) {
    if (error instanceof z.ZodError) {
      return NextResponse.json(
        { error: 'Invalid request data', details: error.errors },
        { status: 400 },
      );
    }
    // Handle other errors
  }
}

3. Create API Client Helper

Create src/lib/api-client.ts:

class ApiError extends Error {
  constructor(
    public status: number,
    message: string,
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

export async function apiCall<T>(url: string, options?: RequestInit): Promise<T> {
  const response = await fetch(url, {
    headers: {
      'Content-Type': 'application/json',
      ...options?.headers,
    },
    ...options,
  });

  if (!response.ok) {
    throw new ApiError(response.status, `HTTP error! status: ${response.status}`);
  }

  return response.json();
}

4. Use in Components

'use client'

import { useState } from 'react'
import { apiCall } from '@/lib/api-client'

export function ExampleComponent() {
  const [loading, setLoading] = useState(false)
  const [data, setData] = useState(null)

  const handleSubmit = async () => {
    setLoading(true)
    try {
      const result = await apiCall('/api/example', {
        method: 'POST',
        body: JSON.stringify({ /* your data */ }),
      })
      setData(result)
    } catch (error) {
      console.error('Error:', error)
    } finally {
      setLoading(false)
    }
  }

  return (
    // Your component JSX
  )
}

Validation Checklist

  • API route file created in correct location
  • Proper TypeScript types defined
  • Error handling implemented
  • Request validation added (if needed)
  • API tested with different HTTP methods
  • Client-side integration working
  • Error cases handled gracefully

Best Practices

  • Use proper HTTP status codes
  • Implement consistent error response format
  • Add request validation for security
  • Log errors for debugging
  • Consider rate limiting for production
  • Document API endpoints