コンテンツにスキップ

Astro Adapter API

Astroは、SSR(サーバーサイドレンダリング)のために任意のクラウドプロバイダに簡単にデプロイできるように設計されています。この機能は、アダプターによって提供されているインテグレーションの一種です。既存のアダプターの使用方法については、SSRガイドをご覧ください。

アダプターは、サーバーサイドレンダリングのエントリポイントを提供する特別な種類のインテグレーションです。アダプターは以下の2つのことを行います。

  • リクエストを処理するためのホスト固有のAPIを実装します。
  • ホストの規約に従ってビルドを設定します。

アダプターはインテグレーションであり、インテグレーションが行えることは何でも行うことができます。

アダプターは、次のようにしてastro:config:doneフックでsetAdapter APIを呼び出す必要があります。

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
supportedAstroFeatures: {
staticOutput: 'stable'
}
});
},
},
};
}

setAdapterに渡されるオブジェクトは以下のように定義されています。

interface AstroAdapter {
name: string;
serverEntrypoint?: string;
previewEntrypoint?: string;
exports?: string[];
args?: any;
adapterFeatures?: AstroAdapterFeatures;
supportedAstroFeatures?: AstroFeatureMap;
}
export interface AstroAdapterFeatures {
/**
* Astroミドルウェアと通信するエッジ関数を作成します。
*/
edgeMiddleware: boolean;
/**
* SSR専用。各ルートが個別の関数/ファイルになります。
*/
functionPerRoute: boolean;
}
export type SupportsKind = 'unsupported' | 'stable' | 'experimental' | 'deprecated';
export type AstroFeatureMap = {
/**
* アダプターが静的ページを提供できるかどうか
*/
staticOutput?: SupportsKind;
/**
* アダプターが静的またはサーバーレンダリングされたページを提供できるかどうか
*/
hybridOutput?: SupportsKind;
/**
* アダプターがSSRページを提供できるかどうか
*/
serverOutput?: SupportsKind;
/**
* アダプターが静的アセットを生成できるかどうか
*/
assets?: AstroAssetsFeature;
};
export interface AstroAssetsFeature {
supportKind?: SupportsKind;
/**
* このアダプターがライブラリ`sharp`と互換性のある環境でファイルをデプロイするかどうか
*/
isSharpCompatible?: boolean;
/**
* このアダプターがライブラリ`squoosh`と互換性のある環境でファイルをデプロイするかどうか
*/
isSquooshCompatible?: boolean;
}

プロパティの説明は次のとおりです。

  • name: ログに使用されるアダプターのユニークな名前。
  • serverEntrypoint: サーバーサイドレンダリングのエントリポイント。
  • exports: createExports(以下で説明)と併用する場合の名前付きエクスポートの配列。
  • adapterFeatures: アダプターがサポートする必要がある特定の機能を有効にするオブジェクト。これらの機能はビルドされた出力を変更し、アダプターは異なる出力を処理するための適切なロジックを実装する必要があります。
  • supportedAstroFeatures: Astroの組み込み機能のマップ。これにより、Astroはアダプターがサポートできない、またはサポートしない機能を判断し、適切なエラーメッセージを提供できます。

AstroのアダプターAPIは、任意のタイプのホストと連携できるように設計されており、ホストのAPIに適合する柔軟な方法を提供します。

一部のサーバーレスホストでは、handlerのような関数をエクスポートすることが求められます。

export function handler(event, context) {
// ...
}

アダプターAPIを使用すると、serverEntrypointcreateExportsを実装することでこれを実現できます。

import { App } from 'astro/app';
export function createExports(manifest) {
const app = new App(manifest);
const handler = (event, context) => {
// ...
};
return { handler };
}

その後、setAdapterを呼び出すインテグレーションで、この名前をexportsで指定します。

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
exports: ['handler'],
});
},
},
};
}

一部のホスティング環境では、サーバーを自分で起動する必要があります。例えば、特定のポートでリッスンを開始するなどです。このような環境に対応するため、アダプターAPIではstart関数をエクスポートできるようになっています。この関数は、バンドルされたスクリプトが実行されたときに呼び出されます。

import { App } from 'astro/app';
export function start(manifest) {
const app = new App(manifest);
addEventListener('fetch', event => {
// ...
});
}

このモジュールは、astro buildコマンドで事前にビルドされたページをレンダリングするために使用します。Astroは標準のRequestオブジェクトとResponseオブジェクトを使用します。独自のリクエスト/レスポンスAPIを持つホスティング環境の場合、アダプター内でこれらの標準オブジェクトに変換する処理を実装する必要があります。

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

このモジュールは以下のメソッドを提供します。

このメソッドは、リクエストに対応するAstroページを呼び出してレンダリングし、Responseオブジェクトを含むPromiseを返します。これはページをレンダリングしないAPIルートでも同様に機能します。

const response = await app.render(request);

app.render()メソッドは、必須のrequest引数と、オプションのRenderOptionsオブジェクトを受け取ります。RenderOptionsにはaddCookieHeaderclientAddresslocalsrouteDataといったオプションが含まれます。

このオプションは、Astro.cookie.set()で設定されたすべてのクッキーを自動的にレスポンスヘッダーに追加するかどうかを指定します。

trueに設定すると、クッキーはレスポンスのSet-Cookieヘッダーにコンマ区切りのキーと値のペアとして追加されます。標準のresponse.headers.getSetCookie() APIを使用して個別に読み取ることができます。 false(デフォルト値)に設定すると、クッキーはApp.getSetCookieFromResponse(response)メソッドを通じてのみ取得できます。

const response = await app.render(request, { addCookieHeader: true });

このオプションで指定したクライアントのIPアドレスは、ページ内ではAstro.clientAddressとして、APIルートとミドルウェア内ではctx.clientAddressとして利用できます。

以下は、x-forwarded-forヘッダーの値を読み取り、それをclientAddressとして設定する例です。この値はAstro.clientAddressとしてユーザーが利用できるようになります。

const clientAddress = request.headers.get("x-forwarded-for");
const response = await app.render(request, { clientAddress });

localsは、リクエストの処理中に情報を保存したりアクセスしたりするためのcontext.localsオブジェクトです。

例えば、次のようなケースで使用できます。x-private-headerというヘッダーの値を読み取り、それをオブジェクトとして解析してlocalsに渡します。こうすることで、その情報を任意のミドルウェア関数で利用できるようになります。

const privateHeader = request.headers.get("x-private-header");
let locals = {};
try {
if (privateHeader) {
locals = JSON.parse(privateHeader);
}
} finally {
const response = await app.render(request, { locals });
}

レンダリングするルートが事前に分かっている場合は、このオプションでrouteDataを指定できます。これにより、ルートを決定するためのapp.matchの内部呼び出しをスキップできます。

const routeData = app.match(request);
if (routeData) {
return app.render(request, { routeData });
} else {
/* アダプター固有の404レスポンス */
return new Response(..., { status: 404 });
}

このメソッドは、受け取ったリクエストがAstroアプリのルーティングルールに合致するかどうかを判断するために使用します。

if(app.match(request)) {
const response = await app.render(request);
}

通常は.matchを使わずに直接app.render(request)を呼び出せます。Astroは404.astroファイルがあれば、自動的に404エラーを処理します。ただし、404エラーを独自の方法で処理したい場合は、app.match(request)を使用してください。

astro addコマンドでのインストールを可能にする

セクションタイトル: astro addコマンドでのインストールを可能にする

astro addコマンドを使うと、ユーザーは簡単にインテグレーション機能やアダプターをプロジェクトに追加できます。自作のアダプターをこのコマンドでインストールできるようにするには、package.jsonファイルのkeywordsフィールドにastro-adapterを追加してください

{
"name": "example",
"keywords": ["astro-adapter"],
}

アダプターをnpmに公開すると、ユーザーはastro add exampleコマンドを実行するだけで、package.jsonに指定されたピア依存関係と共にパッケージをインストールできます。ただし、プロジェクトの設定ファイルの更新は手動で行うよう、ユーザーに指示する必要があります。

追加: astro@3.0.0

Astroの機能は、アダプターがどの機能をサポートできるか、そしてそのサポートレベルをAstroに伝えるための仕組みです。

これらの機能を使用すると、Astroは以下の処理を行います。

  • 特定の検証を実行します
  • 状況に応じたログを出力します

これらの処理は、サポートされている機能、サポートされていない機能、そのサポートレベル、そしてユーザーの設定に基づいて実行されます。

例えば、次の設定は、このアダプターがアセット機能を実験的にサポートしているものの、SharpやSquooshといった組み込みサービスとは互換性がないことをAstroに伝えます。

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
supportedAstroFeatures: {
assets: {
supportKind: "experimental",
isSharpCompatible: false,
isSquooshCompatible: false
}
}
});
},
},
};
}

この場合、Astroはターミナルに警告を表示します。

[@matthewp/my-adapter] The feature is experimental and subject to issues or changes.

また、アセットに使用するサービスがアダプターと互換性がない場合は、エラーも表示します。

[@matthewp/my-adapter] The currently selected adapter `@matthewp/my-adapter` is not compatible with the service "Sharp". Your project will NOT be able to build.

アダプターの機能は、出力されるファイルの内容を変更する一連の機能です。アダプターがこれらの機能を有効にすると、特定のフック内で追加情報を取得できます。

この機能は、SSR(サーバーサイドレンダリング)を使用する場合にのみ有効になります。デフォルトでは、Astroは単一のentry.mjsファイルを生成し、このファイルが各リクエストに対してレンダリングされたページを出力します。

functionPerRoutetrueに設定すると、Astroはプロジェクトで定義された各ルートに対して個別のファイルを作成します。

生成される各ファイルは1つのページのみをレンダリングします。これらのページファイルはdist/pages/ディレクトリ(またはoutDirで指定されたディレクトリ内の/pages/)に出力され、src/pages/ディレクトリと同じファイル構造を維持します。

例えば、pages/ディレクトリ内のファイル構造は、ビルド後のsrc/pages/のページファイルの構造を反映します。

  • ディレクトリdist/
    • ディレクトリpages/
      • ディレクトリblog/
        • entry._slug_.astro.mjs
        • entry.about.astro.mjs
      • entry.index.astro.mjs

この機能を有効にするには、アダプターにtrueを渡します。

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
functionPerRoute: true
}
});
},
},
};
}

その後、astro:build:ssrフックを使用します。このフックはentryPointsオブジェクトを提供し、ページのルートとビルド後に生成された実際のファイルをマッピングします。

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
functionPerRoute: true
}
});
},
'astro:build:ssr': ({ entryPoints }) => {
for (const [route, entryFile] of entryPoints) {
// routeとentryFileを使用して何かをする
}
}
},
};
}

サーバーレス環境でfunctionPerRoute: trueを設定すると、各ルートに対して個別のJavaScriptファイル(ハンドラー)が作成されます。このハンドラーは、使用するホスティングプラットフォームによってlambda、function、pageなど、さまざまな名前で呼ばれることがあります。

これらの各ルートは、ハンドラーが実行される際にコールドスタートの影響を受ける可能性があり、これにより多少の遅延が生じることがあります。この遅延の程度はさまざまな要因に左右されます。

一方、functionPerRoute: falseに設定した場合、すべてのルートのレンダリングを1つのハンドラーが担当します。このハンドラーが最初に呼び出されるときにはコールドスタートの影響を受けますが、それ以降のルートは遅延なく機能するはずです。ただし、この設定ではfunctionPerRoute: trueで得られるコード分割の利点は失われてしまいます。

この機能は、SSR(サーバーサイドレンダリング)のミドルウェアコードをビルド時にバンドル(まとめて)するかどうかを指定します。

この機能を有効にすると、ビルド中にミドルウェアコードがバンドルされ、すべてのページで個別にインポートされるのを防ぐことができます。

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
edgeMiddleware: true
}
});
},
},
};
}

この機能を使用する場合、astro:build:ssrフックを利用します。このフックはmiddlewareEntryPointを提供します。これはファイルシステム上の実際のファイルへのURLです。

my-adapter.mjs
export default function createIntegration() {
return {
name: '@matthewp/my-adapter',
hooks: {
'astro:config:done': ({ setAdapter }) => {
set
Adapter({
name: '@matthewp/my-adapter',
serverEntrypoint: '@matthewp/my-adapter/server.js',
adapterFeatures: {
edgeMiddleware: true
}
});
},
'astro:build:ssr': ({ middlewareEntryPoint }) => {
// このプロパティが存在するか確認することを忘れないでください。アダプターがこの機能にオプトインしない場合、これは`undefined`になります。
if (middlewareEntryPoint) {
createEdgeMiddleware(middlewareEntryPoint)
}
}
},
};
}
function createEdgeMiddleware(middlewareEntryPoint) {
// バンドラーを使用して新しい物理ファイルを発行する
}
貢献する

どんなことを?

GitHub Issueを作成

チームに素早く問題を報告できます。

コミュニティ