diff --git a/src/content/docs/en/reference/adapter-reference.mdx b/src/content/docs/en/reference/adapter-reference.mdx
index 0a3fe53407f5e..0fe4a723b68a3 100644
--- a/src/content/docs/en/reference/adapter-reference.mdx
+++ b/src/content/docs/en/reference/adapter-reference.mdx
@@ -4,26 +4,26 @@ sidebar:
label: Adapter API
i18nReady: true
---
+import ReadMore from '~/components/ReadMore.astro';
import Since from '~/components/Since.astro';
import { FileTree } from '@astrojs/starlight/components';
-
Astro is designed to make it easy to deploy to any cloud provider for on-demand rendering, also known as server-side rendering (SSR). This ability is provided by __adapters__, which are [integrations](/en/reference/integrations-reference/). See the [on-demand rendering guide](/en/guides/on-demand-rendering/) to learn how to use an existing adapter.
## What is an adapter?
-An adapter is a special kind of [integration](/en/reference/integrations-reference/) that provides an entrypoint for server rendering at request time. An adapter does two things:
+An adapter is a special kind of [integration](/en/reference/integrations-reference/) that provides an entrypoint for server rendering at request time. An adapter has access to the full Integration API and does two things:
- Implements host-specific APIs for handling requests.
- Configures the build according to host conventions.
-## Building an Adapter
+## Building an adapter
-An adapter is an [integration](/en/reference/integrations-reference/) and can do anything that an integration can do.
+Create an integration and call the `setAdapter()` function in the [`astro:config:done`](/en/reference/integrations-reference/#astroconfigdone) hook. This allows you to define a server entrypoint and the features supported by your adapter.
-An adapter __must__ call the `setAdapter` API in the `astro:config:done` hook like so:
+The following example creates an adapter with a server entrypoint and stable support for Astro static output:
-```js title="my-adapter.mjs"
+```js title="my-adapter.mjs" {5-13} "setAdapter"
export default function createIntegration() {
return {
name: '@example/my-adapter',
@@ -33,7 +33,7 @@ export default function createIntegration() {
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
supportedAstroFeatures: {
- staticOutput: 'stable'
+ staticOutput: 'stable'
}
});
},
@@ -42,106 +42,226 @@ export default function createIntegration() {
}
```
-The object passed into `setAdapter` is defined as:
-
-```ts
-interface AstroAdapter {
- name: string;
- serverEntrypoint?: string;
- previewEntrypoint?: string;
- exports?: string[];
- args?: any;
- adapterFeatures?: AstroAdapterFeatures;
- supportedAstroFeatures: AstroAdapterFeatureMap;
- client?: {
- /**
- * Headers to inject into Astro's internal fetch calls (Actions, View Transitions, Server Islands, Prefetch).
- * Can be an object of headers or a function that returns headers.
- */
- internalFetchHeaders?: Record | (() => Record);
- /**
- * Query parameters to append to all asset URLs (images, stylesheets, scripts, etc.).
- * Useful for adapters that need to track deployment versions or other metadata.
- */
- assetQueryParams?: URLSearchParams;
- };
+The `setAdapter()` function accepts an object containing the following properties:
+
+### `name`
+
+
+
+**Type:** `string`
+
+
+Defines a unique name for your adapter. This will be used for logging.
+
+### `serverEntrypoint`
+
+
+
+**Type:** `string | URL`
+
+
+Defines the entrypoint for on-demand rendering.
+
+Learn more about [building a server entrypoint](#building-a-server-entrypoint).
+
+### `supportedAstroFeatures`
+
+
+
+**Type:** `AstroAdapterFeatureMap`
+
+
+
+A map of Astro's built-in features supported by the adapter. This allows Astro to determine which features an adapter supports, so appropriate error messages can be provided.
+
+Discover the [available Astro features](#astro-features) that an adapter can configure.
+
+### `adapterFeatures`
+
+
+
+**Type:** `AstroAdapterFeatures`
+
+
+
+An object that specifies which [adapter features that change the build output](#adapter-features) are supported by the adapter.
+
+### `args`
+
+
+
+**Type:** `any`
+
+
+A JSON-serializable value that will be passed to the adapter's server entrypoint at runtime. This is useful to pass an object containing build-time configuration (e.g. paths, secrets) to your server runtime code.
+
+The following example defines an `args` object with a property that identifies where assets generated by Astro are located:
+
+```js title="my-adapter.mjs" {9-11}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ config, setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ args: {
+ assets: config.build.assets
+ }
+ });
+ },
+ },
+ };
}
+```
+
+### `client`
+
+
+
+**Type:** `{ internalFetchHeaders?: Record | () => Record; assetQueryParams?: URLSearchParams; }`
+
+
+
+A configuration object for Astro's client-side code.
+
+#### `internalFetchHeaders`
-export interface AstroAdapterFeatures {
- /**
- * Creates an edge function that will communicate with the Astro middleware.
- */
- edgeMiddleware: boolean;
- /**
- * Determine the type of build output the adapter is intended for. Defaults to `server`;
- */
- buildOutput?: 'static' | 'server';
+
+
+**Type:** `Record | () => Record`
+
+
+Defines the headers to inject into Astro's internal fetch calls (e.g. Actions, View Transitions, Server Islands, Prefetch). This can be an object of headers or a function that returns headers.
+
+The following example retrieves a `DEPLOY_ID` from the environment variables and, if provided, returns an object with the header name as key and the deploy id as value:
+
+```js title="my-adapter.mjs" {9-14}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ config, setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ client: {
+ internalFetchHeaders: () => {
+ const deployId = process.env.DEPLOY_ID;
+ return deployId ? { 'Your-Header-ID': deployId } : {};
+ },
+ },
+ });
+ },
+ },
+ };
}
+```
-export type AdapterSupportsKind = 'unsupported' | 'stable' | 'experimental' | 'deprecated' | 'limited';
+#### `assetQueryParams`
-export type AdapterSupportWithMessage = {
- support: Exclude;
- message: string;
- suppress?: 'default' | 'all';
-};
+
-export type AdapterSupport = AdapterSupportsKind | AdapterSupportWithMessage;
-
-export type AstroAdapterFeatureMap = {
- /**
- * The adapter is able to serve static pages
- */
- staticOutput?: AdapterSupport;
- /**
- * The adapter is able to serve pages that are static or rendered via server
- */
- hybridOutput?: AdapterSupport;
- /**
- * The adapter is able to serve pages rendered on demand
- */
- serverOutput?: AdapterSupport;
- /**
- * The adapter is able to support i18n domains
- */
- i18nDomains?: AdapterSupport;
- /**
- * The adapter is able to support `getSecret` exported from `astro:env/server`
- */
- envGetSecret?: AdapterSupport;
- /**
- * The adapter supports the Sharp image service
- */
- sharpImageService?: AdapterSupport;
-};
+**Type:** `URLSearchParams`
+
+
+Define the query parameters to append to all asset URLs (e.g. images, stylesheets, scripts, etc.). This is useful for adapters that need to track deployment versions or other metadata.
+
+The following example retrieves a `DEPLOY_ID` from the environment variables and, if provided, returns an object with a custom search parameter name as key and the deploy id as value:
+
+```js title="my-adapter.mjs" {9-13}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ config, setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ client: {
+ assetQueryParams: process.env.DEPLOY_ID
+ ? new URLSearchParams({ yourParam: process.env.DEPLOY_ID })
+ : undefined,
+ },
+ });
+ },
+ },
+ };
+}
```
-The properties are:
+### `exports`
+
+
-* __name__: A unique name for your adapter, used for logging.
-* __serverEntrypoint__: The entrypoint for on-demand server rendering.
-* __exports__: An array of named exports when used in conjunction with `createExports` (explained below).
-* __adapterFeatures__: An object that enables specific features that must be supported by the adapter.
- These features will change the built output, and the adapter must implement the proper logic to handle the different output.
-* __supportedAstroFeatures__: A map of Astro built-in features. This allows Astro to determine which features an adapter is unable or unwilling to support so appropriate error messages can be provided.
+**Type:** `string[]`
+
-### Server Entrypoint
+Defines an array of named exports to use in conjunction with the [`createExports()` function](#createexports) of your server entrypoint.
-Astro's adapter API attempts to work with any type of host, and gives a flexible way to conform to the host APIs.
+The following example assumes that `createExports()` provides an export named `handler`:
-#### Exports
+```js title="my-adapter.mjs" {9}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ config, setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ exports: ['handler']
+ });
+ },
+ },
+ };
+}
+```
-Some serverless hosts expect you to export a function, such as `handler`:
+### `previewEntrypoint`
-```js
-export function handler(event, context) {
- // ...
+
+
+**Type:** `string | URL`
+
+
+
+Defines the path or ID of a module in the adapter's package that is responsible for starting up the built server when `astro preview` is run.
+
+```js title="my-adapter.mjs" {9}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ config, setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ previewEntrypoint: '@example/my-adapter/preview.js',
+ });
+ },
+ },
+ };
}
```
-With the adapter API you achieve this by implementing `createExports` in your `serverEntrypoint`:
+## Building a server entrypoint
-```js
+You will need to create a file that executes during server-side requests to enable on-demand rendering with your particular host. Astro's adapter API attempts to work with any type of host and gives a flexible way to conform to the host APIs.
+
+### `createExports()`
+
+
+
+Type: `(manifest: SSRManifest, options: any) => Record`
+
+
+An exported function that takes an SSR manifest as its first argument and an object containing your adapter [args](#args) as its second argument. This should provide the exports required by your host.
+
+For example, some serverless hosts expect you to export an `handler()` function. With the adapter API, you achieve this by implementing `createExports()` in your server entrypoint:
+
+```js title="my-adapter/server.js"
import { App } from 'astro/app';
export function createExports(manifest) {
@@ -155,7 +275,7 @@ export function createExports(manifest) {
}
```
-And then in your integration, where you call `setAdapter`, provide this name in `exports`:
+And then in your integration, where you call `setAdapter()`, provide this name in [`exports`](#exports):
```js title="my-adapter.mjs" ins={9}
export default function createIntegration() {
@@ -174,11 +294,34 @@ export default function createIntegration() {
}
```
-#### Start
+You can access the [`args`](#args) defined by your adapter through the second argument of `createExports()`. This can be useful when you need to access build-time configuration in your server entrypoint. For example, your server might need to identify where assets generated by Astro are located:
+
+```js title="my-adapter/server.js" {4} "args"
+import { App } from 'astro/app';
-Some hosts expect you to *start* the server yourselves, for example by listening to a port. For these types of hosts, the adapter API allows you to export a `start` function which will be called when the bundle script is run.
+export function createExports(manifest, args) {
+ const assetsPath = args.assets;
-```js
+ const handler = (event, context) => {
+ // ...
+ };
+
+ return { handler };
+}
+```
+
+### `start()`
+
+
+
+Type: `(manifest: SSRManifest, options: any) => Record`
+
+
+An exported function that takes an SSR manifest as its first argument and an object containing your adapter [args](#args) as its second argument.
+
+Some hosts expect you to *start* the server yourselves, for example, by listening to a port. For these types of hosts, the adapter API allows you to export a `start` function, which will be called when the bundle script is run.
+
+```js title="my-adapter/server.js"
import { App } from 'astro/app';
export function start(manifest) {
@@ -190,10 +333,12 @@ export function start(manifest) {
}
```
-#### `astro/app`
+### `astro/app`
This module is used for rendering pages that have been prebuilt through `astro build`. Astro uses the standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. Hosts that have a different API for request/response should convert to these types in their adapter.
+The `App` constructor accepts a required SSR manifest argument, and optionally an argument to enable or disable streaming, defaulting to `true`.
+
```js
import { App } from 'astro/app';
import http from 'http';
@@ -211,14 +356,14 @@ export function start(manifest) {
The following methods are provided:
-##### `app.render()`
+#### `app.render()`
**Type:** `(request: Request, options?: RenderOptions) => Promise`
-This method calls the Astro page that matches the request, renders it, and returns a Promise to a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. This also works for API routes that do not render pages.
+A method that accepts a required `request` argument and an optional `RenderOptions` object. This calls the Astro page that matches the request, renders it, and returns a promise to a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. This also works for API routes that do not render pages.
```js
const response = await app.render(request);
@@ -231,7 +376,7 @@ const response = await app.render(request);
**Type:** `{addCookieHeader?: boolean; clientAddress?: string; locals?: object; prerenderedErrorPageFetch?: (url: ErrorPagePath) => Promise; routeData?: RouteData;}`
-The `app.render()` method accepts a mandatory `request` argument, and an optional `RenderOptions` object for [`addCookieHeader`](#addcookieheader), [`clientAddress`](#clientaddress), [`locals`](#locals), [`prerenderedErrorPageFetch`](#prerenderederrorpagefetch), and [`routeData`](#routedata).
+An object that controls the rendering and contains the following properties:
###### `addCookieHeader`
@@ -243,8 +388,8 @@ The `app.render()` method accepts a mandatory `request` argument, and an optiona
Whether or not to automatically add all cookies written by `Astro.cookie.set()` to the response headers.
-When set to `true`, they will be added to the `Set-Cookie` header of the response as comma separated key-value pairs. You can use the standard `response.headers.getSetCookie()` API to read them individually.
-When set to `false`(default), the cookies will only be available from `App.getSetCookieFromResponse(response)`.
+When set to `true`, they will be added to the `Set-Cookie` header of the response as comma-separated key-value pairs. You can use the standard `response.headers.getSetCookie()` API to read them individually.
+When set to `false`(default), the cookies will only be available from [`App.getSetCookieFromResponse(response)`](#appgetsetcookiefromresponse).
```js
const response = await app.render(request, { addCookieHeader: true });
@@ -290,7 +435,7 @@ try {
}
```
-###### `prerenderedErrorPageFetch`
+###### `prerenderedErrorPageFetch()`
@@ -346,14 +491,14 @@ if (routeData) {
}
```
-##### `app.match()`
+#### `app.match()`
-**Type:** `(request: Request) => RouteData | undefined`
+**Type:** `(request: Request, allowPrerenderedRoutes = false) => RouteData | undefined`
-This method is used to determine if a request is matched by the Astro app's routing rules.
+Determines if a request is matched by the Astro app's routing rules.
```js
if(app.match(request)) {
@@ -363,158 +508,292 @@ if(app.match(request)) {
You can usually call `app.render(request)` without using `.match` because Astro handles 404s if you provide a `404.astro` file. Use `app.match(request)` if you want to handle 404s in a different way.
-## Allow installation via `astro add`
+By default, prerendered routes aren't returned, even if they are matched. You can change this behavior by using `true` as the second argument.
-[The `astro add` command](/en/reference/cli-reference/#astro-add) allows users to easily add integrations and adapters to their project. If you want _your_ adapter to be installable with this tool, **add `astro-adapter` to the `keywords` field in your `package.json`**:
+#### `app.getAdapterLogger()`
-```json
-{
- "name": "example",
- "keywords": ["astro-adapter"],
+
+
+**Type:** `() => AstroIntegrationLogger`
+
+
+
+Returns an [instance of the Astro logger](/en/reference/integrations-reference/#astrointegrationlogger) available to the adapter's runtime environment.
+
+```js "logger"
+const logger = app.getAdapterLogger();
+try {
+ /* Some logic that can throw */
+} catch {
+ logger.error("Your custom error message using Astro logger.");
}
```
-Once you [publish your adapter to npm](https://docs.npmjs.com/cli/v8/commands/npm-publish), running `astro add example` will install your package with any peer dependencies specified in your `package.json`. We will also instruct users to update their project config manually.
+#### `app.getAllowedDomains()`
-## Astro features
+
-
+**Type:** `() => Partial[] | undefined`
+
+
-Astro features are a way for an adapter to tell Astro whether they are able to support a feature, and also the adapter's level of support.
+Returns a list of permitted host patterns for incoming requests when using on-demand rendering [as defined in the user configuration](/en/reference/configuration-reference/#securityalloweddomains).
-When using these properties, Astro will:
-- run specific validation;
-- emit contextual information to the logs;
+#### `app.removeBase()`
-These operations are run based on the features supported or not supported, their level of support, the [desired amount of logging](#suppress), and the user's own configuration.
+
-The following configuration tells Astro that this adapter has experimental support for the Sharp-powered built-in image service:
+**Type:** `(pathname: string) => string`
+
+
-```js title="my-adapter.mjs" ins={9-11}
-export default function createIntegration() {
- return {
- name: '@example/my-adapter',
- hooks: {
- 'astro:config:done': ({ setAdapter }) => {
- setAdapter({
- name: '@example/my-adapter',
- serverEntrypoint: '@example/my-adapter/server.js',
- supportedAstroFeatures: {
- sharpImageService: 'experimental'
- }
- });
- },
- },
- };
+Removes the base from the given path. This is useful when you need to look up assets from the filesystem.
+
+#### `app.setCookieHeaders()`
+
+
+
+**Type:** `(response: Response) => Generator`
+
+
+
+Returns a generator that yields individual cookie header values from a `Response` object. This is used to properly handle multiple cookies that may have been set during request processing.
+
+The following example appends a `Set-Cookie` header for each header obtained from a response:
+
+```js
+for (const setCookieHeader of app.setCookieHeaders(response)) {
+ response.headers.append('Set-Cookie', setCookieHeader);
}
```
-If the Sharp image service is used, Astro will log a warning and error to the terminal based on your adapter's support:
+#### `App.getSetCookieFromResponse()`
-```
-[@example/my-adapter] The feature is experimental and subject to issues or changes.
+
-[@example/my-adapter] The currently selected adapter `@example/my-adapter` is not compatible with the service "Sharp". Your project will NOT be able to build.
-```
+**Type:** `(response: Response) => Generator`
+
+
-A message can additionally be provided to give more context to the user:
+Returns a generator that yields individual cookie header values from a `Response` object. This works in the same way as [`app.setCookieHeaders()`](#appsetcookieheaders), but can be used at any time as it is a static method.
-```js title="my-adapter.mjs" ins={9-14}
-export default function createIntegration() {
- return {
- name: '@example/my-adapter',
- hooks: {
- 'astro:config:done': ({ setAdapter }) => {
- setAdapter({
- name: '@example/my-adapter',
- serverEntrypoint: '@example/my-adapter/server.js',
- supportedAstroFeatures: {
- sharpImageService: {
- support: 'limited',
- message: 'This adapter has limited support for Sharp. Certain features may not work as expected.'
- }
- }
- });
- },
- },
- };
+The following example appends a `Set-Cookie` header for each header obtained from a response:
+
+```js
+for (const cookie of App.getSetCookieFromResponse(response)) {
+ response.headers.append('Set-Cookie', cookie);
}
```
-### `suppress`
+#### `App.validateForwardedHost()`
- **Type:** `'default' | 'all'`
-
+**Type:** `(forwardedHost: string, allowedDomains?: Partial[], protocol?: string = 'https') => boolean`
+
-An option to prevent showing some or all logged messages about an adapter's support for a feature.
+Checks whether a `forwardedHost` matches any of the given [`allowedDomains`](/en/reference/configuration-reference/#securityalloweddomains). This static method accepts a third argument that allows you to override the host protocol, defaulting to `https`.
-If Astro's default log message is redundant, or confusing to the user in combination with your custom `message`, you can use `suppress: "default"` to suppress the default message and only log your message:
+The following example retrieves the `forwardedHost` from the headers and checks if the host matches an allowed domain:
-```js title="my-adapter.mjs" ins={13}
-export default function createIntegration() {
- return {
- name: '@example/my-adapter',
- hooks: {
- 'astro:config:done': ({ setAdapter }) => {
- setAdapter({
- name: '@example/my-adapter',
- serverEntrypoint: '@example/my-adapter/server.js',
- supportedAstroFeatures: {
- sharpImageService: {
- support: 'limited',
- message: 'The adapter has limited support for Sharp. It will be used for images during build time, but will not work at runtime.',
- suppress: 'default' // custom message is more detailed than the default
- }
- }
- });
- },
- },
- };
+```js {4-6}
+export function start(manifest) {
+ addEventListener('fetch', (event) => {
+ const forwardedHost = event.request.headers.get('X-Forwarded-Host');
+ if (App.validateForwardedHost(forwardedHost, manifest.allowedDomains)) {
+ /* do something */
+ }
+ });
}
```
-You can also use `suppress: "all"` to suppress all messages about support for the feature. This is useful when these messages are unhelpful to users in a specific context, such as when they have a configuration setting that means they are not using that feature. For example, you can choose to prevent logging any messages about Sharp support from your adapter:
+#### `App.sanitizeHost()`
-```js title="my-adapter.mjs" ins={13}
-export default function createIntegration() {
- return {
- name: '@example/my-adapter',
- hooks: {
- 'astro:config:done': ({ setAdapter }) => {
- setAdapter({
- name: '@example/my-adapter',
- serverEntrypoint: '@example/my-adapter/server.js',
- supportedAstroFeatures: {
- sharpImageService: {
- support: 'limited',
- message: 'This adapter has limited support for Sharp. Certain features may not work as expected.',
- suppress: 'all'
- }
- }
- });
- },
- },
- };
+
+
+**Type:** `(hostname: string | undefined) => string | undefined`
+
+
+
+Validates a hostname by rejecting any name containing path separators. When a hostname is invalid, this static method will return `undefined`.
+
+The following example retrieves the `forwardedHost` from the headers and sanitizes it:
+
+```js {4}
+export function start(manifest) {
+ addEventListener('fetch', (event) => {
+ const forwardedHost = event.request.headers.get('X-Forwarded-Host');
+ const sanitized = App.sanitizeHost(forwardedHost);
+ });
}
```
-## Adapter features
+#### `App.validateForwardedHeaders()`
-A set of features that changes the output of the emitted files. When an adapter opts in to these features, they will get additional information inside specific hooks.
+
-### `edgeMiddleware`
+**Type:** `(forwardedProtocol?: string, forwardedHost?: string, forwardedPort?: string, allowedDomains?: Partial[]) => { protocol?: string; host?: string; port?: string }`
+
+
+
+Validates the forwarded protocol, host, and port against the `allowedDomains`. This static method returns validated values or `undefined` for rejected headers.
+
+The following example validates the forwarded headers against the authorized domains defined in the received manifest:
+
+```js {3-8}
+export function start(manifest) {
+ addEventListener('fetch', (event) => {
+ const validated = App.validateForwardedHeaders(
+ request.headers.get('X-Forwarded-Proto') ?? undefined,
+ request.headers.get('X-Forwarded-Host') ?? undefined,
+ request.headers.get('X-Forwarded-Port') ?? undefined,
+ manifest.allowedDomains,
+ );
+ });
+}
+```
+
+### `astro/app/node`
+
+Just like [`astro/app`](#astroapp), this module is used for rendering pages that have been prebuilt through `astro build`. This allows you to create a `NodeApp` providing all the methods available from `App` and additional methods useful for Node environments.
+
+The `NodeApp` constructor accepts a required SSR manifest argument, and optionally an argument to enable or disable streaming, defaulting to `true`.
+
+```js
+import { NodeApp } from 'astro/app/node';
+import http from 'http';
+
+export function start(manifest) {
+ const nodeApp = new NodeApp(manifest);
+
+ addEventListener('fetch', event => {
+ event.respondWith(
+ nodeApp.render(event.request)
+ );
+ });
+}
+```
+
+The following additional methods are provided:
+
+#### `nodeApp.render()`
-**Type:** `boolean`
+**Type:** `(request: NodeRequest | Request, options?: RenderOptions) => Promise`
+
-Defines whether any on-demand rendering middleware code will be bundled when built.
+Extends [`app.render()`](#apprender) to also accept [Node.js `IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage) objects in addition to standard `Request` objects as the first argument. The second argument is an optional object allowing you to [control the rendering](#renderoptions).
-When enabled, this prevents middleware code from being bundled and imported by all pages during the build:
+```js
+const response = await nodeApp.render(request);
+```
+
+#### `nodeApp.match()`
+
+
+
+**Type:** `(req: NodeRequest | Request, allowPrerenderedRoutes?: boolean) => RouteData | undefined`
+
+
+Extends [`app.match()`](#appmatch) to also accept [Node.js `IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage) objects in addition to standard `Request` objects.
+
+```js
+if(nodeApp.match(request)) {
+ const response = await nodeApp.render(request);
+}
+```
+
+#### `nodeApp.headersMap`
+
+
+
+**Type:** `NodeAppHeadersJson | undefined`
+**Default:** `undefined`
+
+
+
+An array containing the headers configuration. Each entry maps a pathname to a list of headers that should be applied for that route. This is useful for applying headers such as CSP directives to prerendered routes.
+
+#### `nodeApp.setHeadersMap()`
+
+
+
+**Type:** `(headers: NodeAppHeadersJson) => void`
+
+
+
+Loads [headers configuration](#nodeappheadersmap) into the `NodeApp` instance.
+
+```js
+nodeApp.setHeadersMap([
+ {
+ pathname: "/blog",
+ headers: [
+ { key: "Content-Security-Policy", value: "default-src 'self'" },
+ ]
+ }
+]);
+```
+
+#### `NodeApp.createRequest()`
+
+
+
+**Type:** `(req: NodeRequest, options?: { skipBody?: boolean; allowedDomains?: Partial[]; }) => Request`
+
+
+
+Converts a NodeJS `IncomingMessage` into a standard `Request` object. This static method accepts an optional object as the second argument, allowing you to define if the body should be ignored, defaulting to `false`, and the [`allowedDomains`](/en/reference/configuration-reference/#securityalloweddomains).
+
+The following example creates a `Request` and passes it to `app.render`:
+
+```js {5}
+import { NodeApp } from 'astro/app/node';
+import { createServer } from 'node:http';
+
+const server = createServer(async (req, res) => {
+ const request = NodeApp.createRequest(req);
+ const response = await app.render(request);
+})
+```
+
+#### `NodeApp.writeResponse()`
+
+
+
+**Type:** `(source: Response, destination: ServerResponse) => Promise | undefined>`
+
+
+
+Streams a web-standard `Response` into a NodeJS server response. This static method takes a `Response` object and the initial `ServerResponse` before returning a promise of a `ServerResponse` object.
+
+The following example creates a `Request`, passes it to `app.render`, and writes the response:
+
+```js {7}
+import { NodeApp } from 'astro/app/node';
+import { createServer } from 'node:http';
+
+const server = createServer(async (req, res) => {
+ const request = NodeApp.createRequest(req);
+ const response = await app.render(request);
+ await NodeApp.writeResponse(response, res);
+})
+```
+
+## Astro features
+
+Astro features are a way for an adapter to tell Astro whether they are able to support a feature, and also the adapter’s level of support.
+
+When using these properties, Astro will:
+- run specific validation;
+- emit contextual information to the logs;
+
+These operations are run based on the features supported or not supported, their level of support, the [desired amount of logging](#suppress), and the user's own configuration.
+
+The following configuration tells Astro that this adapter has experimental support for the Sharp-powered built-in image service:
```js title="my-adapter.mjs" ins={9-11}
export default function createIntegration() {
@@ -525,8 +804,8 @@ export default function createIntegration() {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
- adapterFeatures: {
- edgeMiddleware: true
+ supportedAstroFeatures: {
+ sharpImageService: 'experimental'
}
});
},
@@ -535,9 +814,17 @@ export default function createIntegration() {
}
```
-Then, consume the hook [`astro:build:ssr`](/en/reference/integrations-reference/#astrobuildssr), which will give you a `middlewareEntryPoint`, an `URL` to the physical file on the file system.
+If the Sharp image service is used, Astro will log a warning and error to the terminal based on your adapter's support:
-```js title="my-adapter.mjs" ins={15-20}
+```
+[@example/my-adapter] The feature is experimental and subject to issues or changes.
+
+[@example/my-adapter] The currently selected adapter `@example/my-adapter` is not compatible with the service "Sharp". Your project will NOT be able to build.
+```
+
+A message can additionally be provided to give more context to the user:
+
+```js title="my-adapter.mjs" ins={9-14}
export default function createIntegration() {
return {
name: '@example/my-adapter',
@@ -546,37 +833,69 @@ export default function createIntegration() {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
- adapterFeatures: {
- edgeMiddleware: true
+ supportedAstroFeatures: {
+ sharpImageService: {
+ support: 'limited',
+ message: 'This adapter has limited support for Sharp. Certain features may not work as expected.'
+ }
}
});
},
-
- 'astro:build:ssr': ({ middlewareEntryPoint }) => {
- // remember to check if this property exits, it will be `undefined` if the adapter doesn't opt in to the feature
- if (middlewareEntryPoint) {
- createEdgeMiddleware(middlewareEntryPoint)
- }
- }
},
};
}
-
-function createEdgeMiddleware(middlewareEntryPoint) {
- // emit a new physical file using your bundler
-}
```
-### envGetSecret
+This object contains the following configurable features:
+
+### `staticOutput`
+
+
+
+**Type:** [`AdapterSupport`](#adaptersupport)
+
+
+Defines whether the adapter is able to serve static pages.
+
+### `hybridOutput`
+
+
+
+**Type:** [`AdapterSupport`](#adaptersupport)
+
+
+Defines whether the adapter is able to serve sites that include a mix of static and on-demand rendered pages.
+
+### `serverOutput`
+
+
+
+**Type:** [`AdapterSupport`](#adaptersupport)
+
+
+Defines whether the adapter is able to serve on-demand rendered pages.
+
+### `i18nDomains`
+
+
+
+**Type:** [`AdapterSupport`](#adaptersupport)
+
+
+
+Defines whether the adapter is able to support i18n domains.
+
+### `envGetSecret`
-**Type:** `AdapterSupportsKind`
+**Type:** [`AdapterSupport`](#adaptersupport)
+
-This is a feature to allow your adapter to retrieve secrets configured by users in `env.schema`.
+Defines whether the adapter is able to support `getSecret()` exported from [`astro:env/server`](/en/reference/modules/astro-env/). When enabled, this feature allows your adapter to retrieve secrets configured by users in `env.schema`.
-Enable the feature by passing any valid `AdapterSupportsKind` value to the adapter:
+The following example enables the feature by passing [a valid `AdapterSupportsKind` value](#adaptersupportskind) to the adapter:
```js title="my-adapter.mjs" ins={9-11}
export default function createIntegration() {
@@ -587,7 +906,7 @@ export default function createIntegration() {
setAdapter({
name: '@example/my-adapter',
serverEntrypoint: '@example/my-adapter/server.js',
- adapterFeatures: {
+ supportedAstroFeatures: {
envGetSecret: 'stable'
}
});
@@ -597,7 +916,7 @@ export default function createIntegration() {
}
```
-The `astro/env/setup` module allows you to provide an implementation for `getSecret()`. In your server entrypoint, call `setGetEnv()` as soon as possible:
+The `astro/env/setup` module allows you to provide an implementation for `getSecret()`. In [your server entrypoint](#building-a-server-entrypoint), call `setGetEnv()` as soon as possible:
```js ins={2,4}
import { App } from 'astro/app';
@@ -616,7 +935,7 @@ export function createExports(manifest) {
}
```
-If you support secrets, be sure to call `setGetEnv()` before `getSecret()` when your environment variables are tied to the request:
+If the adapter supports secrets, be sure to call `setGetEnv()` before `getSecret()` when environment variables are tied to the request:
```js ins={3,14}
import type { SSRManifest } from 'astro';
@@ -643,15 +962,92 @@ export function createExports(manifest: SSRManifest) {
}
```
-### buildOutput
+### `sharpImageService`
-**Type:** `'static' | 'server'`
+**Type:** [`AdapterSupport`](#adaptersupport)
-This property allows you to force a specific output shape for the build. This can be useful for adapters that only work with a specific output type, for instance, your adapter might expect a static website, but uses an adapter to create host-specific files. Defaults to `server` if not specified.
+Defines whether the adapter supports image transformation using the built-in Sharp image service.
+
+## Adapter features
+
+A set of features that changes the output of the emitted files. When an adapter opts in to these features, they will get additional information inside specific hooks and must implement the proper logic to handle the different output.
+
+### `edgeMiddleware`
+
+
+
+**Type:** `boolean`
+
+
+Defines whether any on-demand rendering middleware code will be bundled when built.
+
+When enabled, this prevents middleware code from being bundled and imported by all pages during the build:
+
+```js title="my-adapter.mjs" ins={9-11}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ adapterFeatures: {
+ edgeMiddleware: true
+ }
+ });
+ },
+ },
+ };
+}
+```
+
+Then, consume the hook [`astro:build:ssr`](/en/reference/integrations-reference/#astrobuildssr), which will give you a `middlewareEntryPoint`, an `URL` to the physical file on the file system.
+
+```js title="my-adapter.mjs" ins={15-20}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ adapterFeatures: {
+ edgeMiddleware: true
+ }
+ });
+ },
+
+ 'astro:build:ssr': ({ middlewareEntryPoint }) => {
+ // remember to check if this property exits, it will be `undefined` if the adapter doesn't opt in to the feature
+ if (middlewareEntryPoint) {
+ createEdgeMiddleware(middlewareEntryPoint)
+ }
+ }
+ },
+ };
+}
+
+function createEdgeMiddleware(middlewareEntryPoint) {
+ // emit a new physical file using your bundler
+}
+```
+
+### `buildOutput`
+
+
+
+**Type:** `"static" | "server"`
+**Default:** `"server"`
+
+
+
+Allows you to force a specific output shape for the build. This can be useful for adapters that only work with a specific output type. For example, your adapter might expect a static website, but uses an adapter to create host-specific files. Defaults to `server` if not specified.
```js title="my-adapter.mjs" ins={9-11}
export default function createIntegration() {
@@ -672,19 +1068,15 @@ export default function createIntegration() {
}
```
-### experimentalStaticHeaders
+### `experimentalStaticHeaders`
-**Type:** `true | false`
+**Type:** `boolean`
-When this feature is enabled, Astro will return a map of the `Headers` emitted by the static pages. This map `experimentalRouteToHeaders` is available in the `astro:build:generated` hook.
-
-The value of the headers might change based on the features enabled/used by the application.
-
-For example, if CSP enabled, the `` element is not added to the static page. Instead its `content` is available in the `experimentalRouteToHeaders` map.
+Whether or not the adapter provides experimental support for setting response headers for static pages. When this feature is enabled, Astro will return a map of the `Headers` emitted by the static pages. This map `experimentalRouteToHeaders` is available in the [`astro:build:generated` hook](/en/reference/integrations-reference/#astrobuildgenerated) for generating files such as a `_headers` that allows you to make changes to the default HTTP header.
```js title="my-adapter.mjs" ins={9-11}
export default function createIntegration() {
@@ -708,3 +1100,131 @@ export default function createIntegration() {
};
}
```
+
+The value of the headers might change based on the features enabled/used by the application. For example, if [CSP is enabled](/en/reference/experimental-flags/csp/), the `` element is not added to the static page. Instead, its `content` is available in the `experimentalRouteToHeaders` map.
+
+## Adapter types reference
+
+### `AdapterSupport`
+
+
+
+**Type:** AdapterSupportsKind | AdapterSupportWithMessage
+
+
+
+A union of valid formats to describe the support level for a feature.
+
+### `AdapterSupportsKind`
+
+
+
+**Type:** `"deprecated" | "experimental" | "limited" | "stable" | "unsupported"`
+
+
+Defines the level of support for a feature by your adapter:
+* Use `"deprecated"` when your adapter deprecates support for a feature before removing it completely in a future version.
+* Use `"experimental"` when your adapter adds support for a feature, but issues or breaking changes are expected.
+* Use `"limited"` when your adapter only supports a subset of the full feature.
+* Use `"stable"` when the feature is fully supported by your adapter.
+* Use `"unsupported"` to warn users that they may encounter build issues in their project, as this feature is not supported by your adapter.
+
+### `AdapterSupportWithMessage`
+
+
+
+
+
+
+An object that allows you to define a support level for a feature and a message to be logged in the user console. This object contains the following properties:
+
+#### `support`
+
+
+
+**Type:** Exclude\<AdapterSupportsKind, "stable"\>
+
+
+Defines the level of support for a feature by your adapter.
+
+#### `message`
+
+
+
+**Type:** `string`
+
+
+Defines a custom message to log regarding the support of a feature by your adapter.
+
+#### `suppress`
+
+
+
+**Type:** `"default" | "all"`
+
+
+
+An option to prevent showing some or all logged messages about an adapter's support for a feature.
+
+If Astro's default log message is redundant, or confusing to the user in combination with your [custom `message`](#message), you can use `suppress: "default"` to suppress the default message and only log your message:
+
+```js title="my-adapter.mjs" ins={13}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ supportedAstroFeatures: {
+ sharpImageService: {
+ support: 'limited',
+ message: 'The adapter has limited support for Sharp. It will be used for images during build time, but will not work at runtime.',
+ suppress: 'default' // custom message is more detailed than the default
+ }
+ }
+ });
+ },
+ },
+ };
+}
+```
+
+You can also use `suppress: "all"` to suppress all messages about support for the feature. This is useful when these messages are unhelpful to users in a specific context, such as when they have a configuration setting that means they are not using that feature. For example, you can choose to prevent logging any messages about Sharp support from your adapter:
+
+```js title="my-adapter.mjs" ins={13}
+export default function createIntegration() {
+ return {
+ name: '@example/my-adapter',
+ hooks: {
+ 'astro:config:done': ({ setAdapter }) => {
+ setAdapter({
+ name: '@example/my-adapter',
+ serverEntrypoint: '@example/my-adapter/server.js',
+ supportedAstroFeatures: {
+ sharpImageService: {
+ support: 'limited',
+ message: 'This adapter has limited support for Sharp. Certain features may not work as expected.',
+ suppress: 'all'
+ }
+ }
+ });
+ },
+ },
+ };
+}
+```
+
+## Allow installation via `astro add`
+
+[The `astro add` command](/en/reference/cli-reference/#astro-add) allows users to easily add integrations and adapters to their project. To allow your adapter to be installed with this command, **add `astro-adapter` to the `keywords` field in your `package.json`**:
+
+```json
+{
+ "name": "example",
+ "keywords": ["astro-adapter"],
+}
+```
+
+Once you [publish your adapter to npm](https://docs.npmjs.com/cli/v8/commands/npm-publish), running `astro add example` will install your package with any peer dependencies specified in your `package.json` and instruct users to update their project config manually.