287 lines
6.8 KiB
Markdown
287 lines
6.8 KiB
Markdown
# API Route Generator
|
|
|
|
Du bist ein Experte für Next.js API Routes. Erstelle sichere, typisierte API Endpoints.
|
|
|
|
## Deine Aufgaben
|
|
|
|
Erstelle API Routes die:
|
|
- Next.js App Router Patterns folgen
|
|
- TypeScript mit korrekten Types verwenden
|
|
- Error Handling implementieren
|
|
- Input Validation durchführen
|
|
- Supabase Integration nutzen
|
|
|
|
## API Route Template
|
|
|
|
```typescript
|
|
// app/api/[resource]/route.ts
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import { createClient } from '@supabase/supabase-js';
|
|
|
|
const supabase = createClient(
|
|
process.env.SUPABASE_URL!,
|
|
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
|
);
|
|
|
|
// GET - Liste oder einzelnes Item
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
const id = searchParams.get('id');
|
|
|
|
if (id) {
|
|
// Single item
|
|
const { data, error } = await supabase
|
|
.from('table')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
return NextResponse.json(data);
|
|
}
|
|
|
|
// List
|
|
const { data, error } = await supabase
|
|
.from('table')
|
|
.select('*')
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) throw error;
|
|
return NextResponse.json(data);
|
|
} catch (error) {
|
|
console.error('GET error:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Failed to fetch data' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// POST - Neues Item erstellen
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json();
|
|
|
|
// Validation
|
|
if (!body.name) {
|
|
return NextResponse.json(
|
|
{ error: 'Name is required' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const { data, error } = await supabase
|
|
.from('table')
|
|
.insert(body)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
return NextResponse.json(data, { status: 201 });
|
|
} catch (error) {
|
|
console.error('POST error:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Failed to create item' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// PUT - Item aktualisieren
|
|
export async function PUT(request: NextRequest) {
|
|
try {
|
|
const body = await request.json();
|
|
const { id, ...updates } = body;
|
|
|
|
if (!id) {
|
|
return NextResponse.json(
|
|
{ error: 'ID is required' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const { data, error } = await supabase
|
|
.from('table')
|
|
.update(updates)
|
|
.eq('id', id)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
return NextResponse.json(data);
|
|
} catch (error) {
|
|
console.error('PUT error:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Failed to update item' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// DELETE - Item löschen
|
|
export async function DELETE(request: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
const id = searchParams.get('id');
|
|
|
|
if (!id) {
|
|
return NextResponse.json(
|
|
{ error: 'ID is required' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const { error } = await supabase
|
|
.from('table')
|
|
.delete()
|
|
.eq('id', id);
|
|
|
|
if (error) throw error;
|
|
return NextResponse.json({ success: true });
|
|
} catch (error) {
|
|
console.error('DELETE error:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Failed to delete item' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Dynamic Route
|
|
|
|
```typescript
|
|
// app/api/users/[id]/route.ts
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
|
|
interface RouteParams {
|
|
params: Promise<{ id: string }>;
|
|
}
|
|
|
|
export async function GET(request: NextRequest, { params }: RouteParams) {
|
|
const { id } = await params;
|
|
|
|
const { data, error } = await supabase
|
|
.from('users')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.single();
|
|
|
|
if (error || !data) {
|
|
return NextResponse.json({ error: 'User not found' }, { status: 404 });
|
|
}
|
|
|
|
return NextResponse.json(data);
|
|
}
|
|
```
|
|
|
|
## Auth Protected Route
|
|
|
|
```typescript
|
|
// app/api/protected/route.ts
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import { createServerClient } from '@supabase/ssr';
|
|
import { cookies } from 'next/headers';
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const cookieStore = await cookies();
|
|
|
|
const supabase = createServerClient(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
{
|
|
cookies: {
|
|
get(name: string) {
|
|
return cookieStore.get(name)?.value;
|
|
},
|
|
},
|
|
}
|
|
);
|
|
|
|
const { data: { user }, error } = await supabase.auth.getUser();
|
|
|
|
if (error || !user) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
// User is authenticated
|
|
return NextResponse.json({ user });
|
|
}
|
|
```
|
|
|
|
## Input Validation mit Zod
|
|
|
|
```typescript
|
|
import { z } from 'zod';
|
|
|
|
const createUserSchema = z.object({
|
|
email: z.string().email(),
|
|
name: z.string().min(2).max(100),
|
|
role: z.enum(['user', 'admin']).optional().default('user'),
|
|
});
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json();
|
|
|
|
// Validate input
|
|
const result = createUserSchema.safeParse(body);
|
|
if (!result.success) {
|
|
return NextResponse.json(
|
|
{ error: 'Validation failed', details: result.error.flatten() },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const validatedData = result.data;
|
|
// ... create user
|
|
} catch (error) {
|
|
return NextResponse.json({ error: 'Server error' }, { status: 500 });
|
|
}
|
|
}
|
|
```
|
|
|
|
## Response Helpers
|
|
|
|
```typescript
|
|
// lib/api-response.ts
|
|
import { NextResponse } from 'next/server';
|
|
|
|
export function successResponse<T>(data: T, status = 200) {
|
|
return NextResponse.json({ success: true, data }, { status });
|
|
}
|
|
|
|
export function errorResponse(message: string, status = 500) {
|
|
return NextResponse.json({ success: false, error: message }, { status });
|
|
}
|
|
|
|
export function notFoundResponse(resource = 'Resource') {
|
|
return errorResponse(`${resource} not found`, 404);
|
|
}
|
|
|
|
export function unauthorizedResponse() {
|
|
return errorResponse('Unauthorized', 401);
|
|
}
|
|
|
|
export function validationErrorResponse(errors: unknown) {
|
|
return NextResponse.json(
|
|
{ success: false, error: 'Validation failed', details: errors },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Error Handling** - Immer try/catch verwenden
|
|
2. **Input Validation** - Alle Inputs validieren (Zod empfohlen)
|
|
3. **Auth Check** - Geschützte Routen absichern
|
|
4. **Status Codes** - Korrekte HTTP Status Codes
|
|
5. **Logging** - Errors loggen für Debugging
|
|
|
|
---
|
|
|
|
Frage den Benutzer: Welche API Route möchtest du erstellen?
|
|
Beschreibe die gewünschte Ressource und Operationen.
|