Endpoints and API Routes

Astro endpoints are .ts or .js files in src/pages/ that return Response objects instead of rendering HTML. They handle RSS feeds, sitemaps, JSON APIs, text files, and any other non-HTML output.

Basic endpoint

// src/pages/api/health.ts
import type { APIRoute } from 'astro';

export const GET: APIRoute = async () => {
  return new Response(JSON.stringify({ status: 'ok' }), {
    headers: { 'Content-Type': 'application/json' },
  });
};

HTTP methods

Export named functions for each method:

// src/pages/api/posts.ts
import type { APIRoute } from 'astro';

export const GET: APIRoute = async ({ request }) => {
  const posts = await fetchPosts();
  return new Response(JSON.stringify(posts), {
    headers: { 'Content-Type': 'application/json' },
  });
};

export const POST: APIRoute = async ({ request }) => {
  const body = await request.json();
  // process body
  return new Response(JSON.stringify({ success: true }), { status: 201 });
};

Supported: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, ALL.

Static vs on-demand endpoints

In output: 'static' mode (default), endpoints run at build time and produce static files:

// src/pages/robots.txt.ts
import type { APIRoute } from 'astro';

export const GET: APIRoute = () => {
  return new Response(`User-agent: *\nAllow: /`, {
    headers: { 'Content-Type': 'text/plain' },
  });
};

This generates a robots.txt file at build time.

For request-time endpoints, opt out of prerendering:

// src/pages/api/search.ts
export const prerender = false;

import type { APIRoute } from 'astro';

export const GET: APIRoute = async ({ url }) => {
  const query = url.searchParams.get('q');
  const results = await search(query);
  return new Response(JSON.stringify(results));
};

Dynamic route endpoints

// src/pages/api/posts/[id].ts
import type { APIRoute } from 'astro';

export const GET: APIRoute = async ({ params }) => {
  const post = await fetchPost(params.id);
  if (!post) {
    return new Response('Not found', { status: 404 });
  }
  return new Response(JSON.stringify(post), {
    headers: { 'Content-Type': 'application/json' },
  });
};

RSS feeds

Astro has an official RSS package:

npm install @astrojs/rss
// src/pages/rss.xml.ts
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';
import type { APIContext } from 'astro';

export async function GET(context: APIContext) {
  const posts = await getCollection('blog');
  return rss({
    title: 'My Blog',
    description: 'Blog feed',
    site: context.site!,
    items: posts.map(post => ({
      title: post.data.title,
      pubDate: post.data.pubDate,
      description: post.data.description,
      link: `/blog/${post.id}/`,
    })),
  });
}

Sitemaps

Use the official integration:

npx astro add sitemap
// astro.config.mjs
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://example.com',
  integrations: [sitemap()],
});

Automatically generates sitemap-index.xml and individual sitemaps at build time.

Text and Markdown endpoints

For endpoints like llms-full.txt:

// src/pages/llms-full.txt.ts
import type { APIRoute } from 'astro';

export const GET: APIRoute = async () => {
  const content = await generateLLMsContent();
  return new Response(content, {
    headers: { 'Content-Type': 'text/plain; charset=utf-8' },
  });
};

Accessing request context

Endpoint handlers receive a context object:

export const GET: APIRoute = async (context) => {
  context.params;       // route parameters
  context.request;      // standard Request object
  context.url;          // parsed URL
  context.site;         // configured site URL
  context.locals;       // data from middleware
  context.redirect();   // redirect helper
  context.cookies;      // cookie helpers
};

Redirects from endpoints

export const GET: APIRoute = async ({ redirect }) => {
  return redirect('/new-location', 301);
};

Comparison with Next.js route handlers

The pattern is almost identical. Key differences:

  • File location: src/pages/ instead of app/
  • Type: APIRoute instead of untyped exports
  • Static endpoints produce files at build time (Next.js route handlers are always dynamic unless explicitly cached)
  • The context object has Astro-specific helpers (Astro.locals, Astro.cookies)