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