Astro shows “The endpoint did not return a Response” when a handler in a .ts or .js file under src/pages/ (an endpoint) returns something other than a Response. The message: “An endpoint must return either a Response, or a Promise that resolves with a Response.” Older Astro versions accepted { body: ... } objects; current versions don’t. Return new Response(...) or Response.json(...) from every code path.
Key Takeaways
- Endpoint handlers (
GET,POST, …) must return aResponse.return { body: JSON.stringify(data) }is the old format and fails now.- Use
Response.json(data)for JSON,new Response(text, { headers })for anything else.- Check
ifbranches: a path that returns nothing triggers the error.
Old format, which fails
// src/pages/api/posts.json.ts
export async function GET() {
const posts = await getCollection('blog');
return { body: JSON.stringify(posts.map((p) => p.id)) };
}
Current format
// src/pages/api/posts.json.ts
import type { APIRoute } from 'astro';
import { getCollection } from 'astro:content';
export const GET: APIRoute = async () => {
const posts = await getCollection('blog');
return Response.json(posts.map((p) => ({ id: p.id, title: p.data.title })));
};
For text or XML:
export const GET: APIRoute = () =>
new Response('User-agent: *\nAllow: /\n', {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
Typing the handler as APIRoute makes TypeScript flag a wrong return value before you build.
Branches that return nothing
export const POST: APIRoute = async ({ request }) => {
const data = await request.formData();
if (!data.get('email')) {
return new Response('Email required', { status: 400 });
}
await subscribe(String(data.get('email')));
// missing return
};
Add return new Response(null, { status: 204 }); or a redirect: return Response.redirect(new URL('/thanks/', request.url), 303);.
Static vs on-demand endpoints
In a static build, GET endpoints run once and their output is written as a file, which is how robots.txt and sitemaps are generated. POST and other methods only run on demand, which needs export const prerender = false and an adapter; without one you’ll hit Cannot use server-side rendering without an adapter.
The Astro routing guide covers endpoints with dynamic params, and the endpoints docs list everything a handler receives.
FAQ
Can an endpoint return a stream?
Yes. Pass a ReadableStream as the Response body, on demand.
How do I name the output file of a static endpoint?
By the file name: src/pages/feed.json.ts builds /feed.json.