乐闻世界logo
搜索文章和话题

How do you create and use API Routes in Astro? How do you handle requests and responses?

2月21日 16:14

Astro's API Routes feature allows you to create server-side API endpoints in your Astro project for handling data requests, authentication, database operations, and more.

Basic Concepts:

API routes are located in the src/pages/api/ directory, where each file corresponds to an API endpoint.

Creating API Routes:

typescript
// src/pages/api/hello.ts export async function GET(context) { return new Response(JSON.stringify({ message: 'Hello, World!' }), { headers: { 'Content-Type': 'application/json', }, }); }

Supported HTTP Methods:

typescript
// src/pages/api/users.ts export async function GET(context) { const users = await fetchUsers(); return new Response(JSON.stringify(users), { headers: { 'Content-Type': 'application/json' }, }); } export async function POST(context) { const body = await context.request.json(); const newUser = await createUser(body); return new Response(JSON.stringify(newUser), { status: 201, headers: { 'Content-Type': 'application/json' }, }); } export async function PUT(context) { const body = await context.request.json(); const updatedUser = await updateUser(body); return new Response(JSON.stringify(updatedUser), { headers: { 'Content-Type': 'application/json' }, }); } export async function DELETE(context) { const { id } = context.params; await deleteUser(id); return new Response(null, { status: 204 }); }

Dynamic Route Parameters:

typescript
// src/pages/api/users/[id].ts export async function GET(context) { const { id } = context.params; const user = await fetchUserById(id); if (!user) { return new Response(JSON.stringify({ error: 'User not found' }), { status: 404, headers: { 'Content-Type': 'application/json' }, }); } return new Response(JSON.stringify(user), { headers: { 'Content-Type': 'application/json' }, }); }

Request and Response Handling:

typescript
// src/pages/api/search.ts export async function POST(context) { try { // Get request body const body = await context.request.json(); const { query } = body; // Get query parameters const url = new URL(context.request.url); const limit = parseInt(url.searchParams.get('limit') || '10'); // Get request headers const authHeader = context.request.headers.get('Authorization'); // Get cookies const sessionCookie = context.cookies.get('session'); // Process business logic const results = await search(query, limit); // Return response return new Response(JSON.stringify({ results }), { status: 200, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=3600', }, }); } catch (error) { return new Response(JSON.stringify({ error: 'Internal Server Error' }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } }

Authentication Example:

typescript
// src/pages/api/protected.ts export async function GET(context) { const token = context.request.headers.get('Authorization'); if (!token) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }); } const user = await verifyToken(token); if (!user) { return new Response(JSON.stringify({ error: 'Invalid token' }), { status: 403, headers: { 'Content-Type': 'application/json' }, }); } const data = await fetchProtectedData(user.id); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }, }); }

File Upload:

typescript
// src/pages/api/upload.ts export async function POST(context) { try { const formData = await context.request.formData(); const file = formData.get('file') as File; if (!file) { return new Response(JSON.stringify({ error: 'No file provided' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } // Validate file type const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']; if (!allowedTypes.includes(file.type)) { return new Response(JSON.stringify({ error: 'Invalid file type' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } // Validate file size (max 5MB) const maxSize = 5 * 1024 * 1024; if (file.size > maxSize) { return new Response(JSON.stringify({ error: 'File too large' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } // Save file const url = await uploadFile(file); return new Response(JSON.stringify({ url }), { status: 201, headers: { 'Content-Type': 'application/json' }, }); } catch (error) { return new Response(JSON.stringify({ error: 'Upload failed' }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } }

Using Database:

typescript
// src/pages/api/posts.ts import { db } from '../../lib/db'; export async function GET(context) { const url = new URL(context.request.url); const page = parseInt(url.searchParams.get('page') || '1'); const limit = parseInt(url.searchParams.get('limit') || '10'); const offset = (page - 1) * limit; const posts = await db.post.findMany({ take: limit, skip: offset, orderBy: { createdAt: 'desc' }, }); const total = await db.post.count(); return new Response(JSON.stringify({ posts, pagination: { page, limit, total, totalPages: Math.ceil(total / limit), }, }), { headers: { 'Content-Type': 'application/json' }, }); } export async function POST(context) { const body = await context.request.json(); const { title, content, authorId } = body; const post = await db.post.create({ data: { title, content, authorId, }, }); return new Response(JSON.stringify(post), { status: 201, headers: { 'Content-Type': 'application/json' }, }); }

Error Handling:

typescript
// src/lib/api-error.ts export class ApiError extends Error { constructor( public statusCode: number, message: string, public code?: string ) { super(message); this.name = 'ApiError'; } } export function handleApiError(error: unknown) { if (error instanceof ApiError) { return new Response( JSON.stringify({ error: error.message, code: error.code, }), { status: error.statusCode, headers: { 'Content-Type': 'application/json' }, } ); } console.error('Unexpected error:', error); return new Response( JSON.stringify({ error: 'Internal Server Error' }), { status: 500, headers: { 'Content-Type': 'application/json' }, } ); }
typescript
// src/pages/api/data.ts import { ApiError, handleApiError } from '../../lib/api-error'; export async function GET(context) { try { const data = await fetchData(); if (!data) { throw new ApiError(404, 'Data not found', 'NOT_FOUND'); } return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }, }); } catch (error) { return handleApiError(error); } }

CORS Configuration:

typescript
// src/pages/api/cors-example.ts export async function OPTIONS(context) { return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Max-Age': '86400', }, }); } export async function GET(context) { const data = await fetchData(); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', }, }); }

Best Practices:

  1. Use appropriate HTTP methods (GET, POST, PUT, DELETE)
  2. Implement proper error handling and status codes
  3. Validate and sanitize input data
  4. Use TypeScript for type safety
  5. Implement authentication and authorization
  6. Add appropriate logging
  7. Use environment variables for sensitive information
  8. Implement rate limiting to prevent abuse
  9. Use appropriate caching strategies
  10. Write tests to ensure API reliability

Astro's API Routes feature provides powerful server-side capabilities for building full-stack applications while maintaining Astro's simplicity and performance benefits.

标签:Astro