Skip to main content
The Adapter API enables server-side rendering (SSR) for Astro by providing host-specific request handling and build configuration. Use this API to deploy Astro to any cloud provider or edge platform.

When to use this API

Use the Adapter API when you need to:
  • Enable SSR deployment to a specific hosting platform
  • Configure build output for serverless functions
  • Handle platform-specific request/response formats
  • Set up preview servers for testing SSR builds
  • Implement host-specific features (edge middleware, image optimization, etc.)

Creating an adapter

An adapter is a special integration that calls setAdapter() in the astro:config:done hook:
export default function createAdapter() {
  return {
    name: '@example/my-adapter',
    hooks: {
      'astro:config:done': ({ setAdapter }) => {
        setAdapter({
          name: '@example/my-adapter',
          serverEntrypoint: '@example/my-adapter/server.js',
          supportedAstroFeatures: {
            staticOutput: 'stable'
          }
        });
      },
    },
  };
}

Adapter configuration

The setAdapter() function accepts an object with the following properties:
name
string
required
Unique name for your adapter, used for logging.
serverEntrypoint
string | URL
required
Path to the server entrypoint file that handles rendering.
serverEntrypoint: '@example/my-adapter/server.js'
supportedAstroFeatures
AstroAdapterFeatureMap
Map of Astro features supported by the adapter. This allows Astro to provide appropriate error messages.Available features:
  • staticOutput - Serve static pages
  • hybridOutput - Mix of static and SSR pages
  • serverOutput - All pages rendered on-demand
  • i18nDomains - i18n domain support
  • envGetSecret - astro:env/server secret support
  • sharpImageService - Sharp image service support
Each feature accepts a support level:
  • 'stable' - Fully supported
  • 'experimental' - Supported but may change
  • 'limited' - Partial support
  • 'deprecated' - Supported but will be removed
  • 'unsupported' - Not supported
supportedAstroFeatures: {
  staticOutput: 'stable',
  hybridOutput: 'experimental',
  serverOutput: 'stable',
  i18nDomains: 'unsupported'
}
adapterFeatures
AstroAdapterFeatures
Adapter-specific features that change build output:
{
  edgeMiddleware?: boolean;
  buildOutput?: 'static' | 'server';
  experimentalStaticHeaders?: boolean;
}
args
any
JSON-serializable value passed to the server entrypoint at runtime. Useful for build-time configuration.
args: {
  assets: config.build.assets,
  serverPath: '/api'
}
exports
string[]
Array of named exports from your server entrypoint’s createExports() function.
exports: ['handler', 'middleware']
previewEntrypoint
string | URL
Path to a module that starts the preview server when running astro preview.
previewEntrypoint: '@example/my-adapter/preview.js'

Building a server entrypoint

The server entrypoint handles rendering at request time. It must export either createExports() or start().

createExports()

Export a function that returns the exports required by your host:
import { App } from 'astro/app';

export function createExports(manifest, args) {
  const app = new App(manifest);
  
  const handler = async (request, context) => {
    return await app.render(request);
  };
  
  return { handler };
}
manifest
SSRManifest
required
The SSR manifest containing routing and build information.
args
any
The args object defined in your adapter configuration.

start()

For hosts that require you to start the server (e.g., listening to a port):
import { App } from 'astro/app';

export function start(manifest, args) {
  const app = new App(manifest);
  
  addEventListener('fetch', event => {
    event.respondWith(app.render(event.request));
  });
}

The App class

Imported from astro/app, the App class provides methods for rendering:
import { App } from 'astro/app';

const app = new App(manifest);

app.render()

Renders a page and returns a Response:
app.render(
  request: Request,
  options?: RenderOptions
): Promise<Response>
request
Request
required
Standard Request object to render.
options
RenderOptions
Optional rendering configuration:
{
  addCookieHeader?: boolean;
  clientAddress?: string;
  locals?: object;
  routeData?: RouteData;
}
Example:
const response = await app.render(request, {
  clientAddress: request.headers.get('x-forwarded-for'),
  locals: { user: authenticatedUser }
});

app.match()

Determines if a request matches Astro’s routing rules:
app.match(
  request: Request,
  allowPrerenderedRoutes?: boolean
): RouteData | undefined
if (app.match(request)) {
  return await app.render(request);
} else {
  return new Response('Not Found', { status: 404 });
}

Static methods

Returns individual cookie header values from a response.
for (const cookie of App.getSetCookieFromResponse(response)) {
  response.headers.append('Set-Cookie', cookie);
}
App.validateForwardedHost
(forwardedHost: string, allowedDomains?: RemotePattern[], protocol?: string) => boolean
Validates a forwarded host against allowed domains.
const forwardedHost = request.headers.get('X-Forwarded-Host');
if (App.validateForwardedHost(forwardedHost, manifest.allowedDomains)) {
  // Process request
}

Node.js adapter features

For Node.js environments, use astro/app/node:
import { NodeApp } from 'astro/app/node';

const nodeApp = new NodeApp(manifest);

NodeApp.createRequest()

Converts Node’s IncomingMessage to a standard Request:
import { createServer } from 'node:http';
import { NodeApp } from 'astro/app/node';

const server = createServer(async (req, res) => {
  const request = NodeApp.createRequest(req);
  const response = await app.render(request);
  await NodeApp.writeResponse(response, res);
});

NodeApp.writeResponse()

Streams a Response to Node’s ServerResponse:
const response = await app.render(request);
await NodeApp.writeResponse(response, res);

Feature support configuration

Define support levels for Astro features:
supportedAstroFeatures: {
  staticOutput: 'stable',
  hybridOutput: 'stable',
  serverOutput: 'stable',
  i18nDomains: {
    support: 'limited',
    message: 'Domain routing requires manual DNS configuration'
  },
  envGetSecret: 'stable',
  sharpImageService: 'experimental'
}
Provide custom messages for non-stable support:
sharpImageService: {
  support: 'experimental',
  message: 'Sharp support is experimental. Some features may not work.'
}

Adapter features

edgeMiddleware

Prevents middleware from being bundled with every page:
adapterFeatures: {
  edgeMiddleware: true
}
Access the middleware entry point in astro:build:ssr:
'astro:build:ssr': ({ middlewareEntryPoint }) => {
  if (middlewareEntryPoint) {
    // Bundle middleware separately
  }
}

buildOutput

Force a specific build output type:
adapterFeatures: {
  buildOutput: 'static' // or 'server'
}

Example adapter

// my-adapter.mjs
export default function createAdapter() {
  return {
    name: '@example/my-adapter',
    hooks: {
      'astro:config:done': ({ config, setAdapter }) => {
        setAdapter({
          name: '@example/my-adapter',
          serverEntrypoint: '@example/my-adapter/server.js',
          supportedAstroFeatures: {
            staticOutput: 'stable',
            serverOutput: 'stable',
            hybridOutput: 'stable'
          },
          args: {
            deploymentId: process.env.DEPLOYMENT_ID
          },
          exports: ['handler']
        });
      },
      'astro:build:done': ({ logger }) => {
        logger.info('Build complete! Deploy to your platform.');
      }
    }
  };
}

// server.js
import { App } from 'astro/app';

export function createExports(manifest, args) {
  const app = new App(manifest);
  
  const handler = async (request) => {
    const clientAddress = request.headers.get('x-forwarded-for');
    const response = await app.render(request, { clientAddress });
    return response;
  };
  
  return { handler };
}