Урок 03

Ответы и статусы

JSON

return Response.json({ data });
return NextResponse.json({ data });

return Response.json({ data }, { status: 201 });

NextResponse расширяет Response методами для редиректов и cookies.

HTTP-статусы

return Response.json({ error: 'Bad request' }, { status: 400 });
return Response.json({ error: 'Unauthorized' }, { status: 401 });
return Response.json({ error: 'Forbidden' }, { status: 403 });
return Response.json({ error: 'Not found' }, { status: 404 });
return Response.json({ error: 'Method not allowed' }, { status: 405 });
return Response.json({ error: 'Conflict' }, { status: 409 });
return Response.json({ errors }, { status: 422 });
return Response.json({ error: 'Too many requests' }, { status: 429 });
return Response.json({ error: 'Server error' }, { status: 500 });

Пустой ответ

return new Response(null, { status: 204 });

Текст

return new Response('Hello', {
    headers: { 'Content-Type': 'text/plain' },
});

HTML

return new Response('<h1>Hello</h1>', {
    headers: { 'Content-Type': 'text/html' },
});

Файл

const file = await fs.readFile('./public/document.pdf');

return new Response(file, {
    headers: {
        'Content-Type': 'application/pdf',
        'Content-Disposition': 'attachment; filename="document.pdf"',
    },
});

Stream

const stream = new ReadableStream({
    async start(controller) {
        for (let i = 0; i < 10; i++) {
            controller.enqueue(`chunk ${i}\n`);
            await new Promise((r) => setTimeout(r, 100));
        }
        controller.close();
    },
});

return new Response(stream, {
    headers: { 'Content-Type': 'text/plain' },
});

Редирект

return NextResponse.redirect(new URL('/login', request.url));
return NextResponse.redirect('https://example.com', 301);

Статусы: 301 (permanent), 302 (temporary), 307, 308.

Rewrite

return NextResponse.rewrite(new URL('/proxy', request.url));

Меняет URL без редиректа на клиенте.

Заголовки

return Response.json(data, {
    headers: {
        'X-Custom-Header': 'value',
        'Cache-Control': 'public, max-age=3600',
        'ETag': '"abc123"',
    },
});

CORS

const corsHeaders = {
    '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 OPTIONS() {
    return new Response(null, { status: 204, headers: corsHeaders });
}

export async function GET() {
    const data = await db.post.findMany();
    return Response.json(data, { headers: corsHeaders });
}

Множественные Set-Cookie

const response = Response.json({ ok: true });
response.headers.append('Set-Cookie', 'a=1; HttpOnly');
response.headers.append('Set-Cookie', 'b=2; HttpOnly');
return response;

Кеш

return Response.json(data, {
    headers: {
        'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
    },
});

Итоги

  • Response.json для JSON
  • Статусы через { status }
  • 204 для пустого
  • Стриминг через ReadableStream
  • NextResponse.redirect, rewrite
  • CORS через заголовки