ein freies Lehrmittel auf der Basis von eduskript

API Routes

Add new endpoints to the backend.

Route Structure

src/app/api/
├── auth/[...nextauth]/route.ts   # Auth (don't touch)
├── collections/
│   ├── route.ts                  # GET, POST /api/collections
│   └── [id]/route.ts             # GET, PUT, DELETE /api/collections/:id
├── skripts/
│   └── route.ts
├── pages/
│   └── route.ts
└── your-feature/
    └── route.ts                  # Your new endpoint

Basic Route

// src/app/api/stats/route.ts
import { NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { prisma } from '@/lib/prisma'

export async function GET() {
  const session = await getServerSession(authOptions)

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

  const stats = await prisma.page.count({
    where: {
      authors: { some: { userId: session.user.id } }
    }
  })

  return NextResponse.json({ pageCount: stats })
}

With Parameters

// src/app/api/pages/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function GET(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params

  const page = await prisma.page.findUnique({
    where: { id }
  })

  if (!page) {
    return NextResponse.json({ error: 'Not found' }, { status: 404 })
  }

  return NextResponse.json(page)
}

With Request Body

export async function POST(request: NextRequest) {
  const session = await getServerSession(authOptions)
  if (!session?.user?.id) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const body = await request.json()
  const { title, content } = body

  if (!title) {
    return NextResponse.json({ error: 'Title required' }, { status: 400 })
  }

  const page = await prisma.page.create({
    data: {
      title,
      content,
      slug: slugify(title),
      // ... other fields
    }
  })

  return NextResponse.json(page, { status: 201 })
}

Permission Checks

Use the permission helpers:

import { canEditSkript } from '@/lib/permissions'

export async function PUT(request: NextRequest, { params }) {
  const { id } = await params
  const session = await getServerSession(authOptions)

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

  const skript = await prisma.skript.findUnique({ where: { id } })

  if (!await canEditSkript(session.user.id, skript)) {
    return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
  }

  // Proceed with update...
}

Testing Routes

// tests/api/stats.test.ts
import { GET } from '@/app/api/stats/route'
import { createMockSession } from '@tests/helpers/api-helpers'

vi.mock('@/lib/prisma')

it('returns page count for authenticated user', async () => {
  createMockSession({ userId: 'user-1' })
  prisma.page.count.mockResolvedValue(5)

  const response = await GET()
  const data = await response.json()

  expect(response.status).toBe(200)
  expect(data.pageCount).toBe(5)
})

it('returns 401 for unauthenticated', async () => {
  createMockSession(null)

  const response = await GET()

  expect(response.status).toBe(401)
})

Patterns

PatternUse
Return early on auth failureKeep logic flat
Validate input before database callsFail fast
Use permission helpersDon't reinvent
Return appropriate status codes200, 201, 400, 401, 403, 404