Use Advanced Routing

Advanced routing lets you own Astro’s request pipeline from src/fetch.ts.

Use it only when middleware is not enough.

Wrap The Default Pipeline

This is the least invasive pattern. You run code before or after Astro’s built-in pipeline.

import { FetchState, astro } from 'astro/fetch';

export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname.startsWith('/api/external')) {
      return fetch(new URL(url.pathname, 'https://api.example.com'));
    }

    const state = new FetchState(request);
    const response = await astro(state);
    response.headers.set('X-App', 'astro');
    return response;
  },
};

Change The Entrypoint Name

If src/fetch.ts already exists for another purpose, use fetchFile:

import { defineConfig } from 'astro/config';

export default defineConfig({
  fetchFile: 'handler',
});

Astro will look for src/handler.ts instead.

Disable entrypoint detection entirely when you do not need it:

import { defineConfig } from 'astro/config';

export default defineConfig({
  fetchFile: null,
});

Compose With Hono

Use Hono when you already want Hono middleware or route grouping:

import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { actions, middleware, pages, i18n } from 'astro/hono';

const app = new Hono();

app.use(logger());
app.use(actions());
app.use(middleware());
app.use(pages());
app.use(i18n());

export default app;

Keep The Pipeline Complete

When composing individual handlers, you become responsible for order. If you omit sessions(), actions(), i18n(), or cache(), those features do not magically run later.

Gotcha: Advanced routing is stable, but it is also an escape hatch. The more of Astro’s pipeline you recompose manually, the more framework behavior you own.