Points de terminaison

Astro vous laisse la possibilité de créer des points de terminaison (‘endpoints’ en anglais) personnalisés pour servir n’importe quel type de données. Vou pouvez les utiliser pour générer des images, exposer un document RSS, ou en tant que routes d’API afin de construire une API complète pour votre site.

Dans les sites générés statiquement, vos points de terminaison personnalisés sont appelés lors de la compilation afin de produire des fichiers statiques. Si vous optez pour le mode SSR, points de terminaison personnalisés deviendront des points de terminaison “live server” qui seront appelés à la demande. Les points de terminaison Statiques et SSR sont définis de manière similaire, mais les points de terminaison SSR supportent des fonctionnalités additionnelles.

Points de terminaison statiques

Section titled Points de terminaison statiques

Pour créer un point de terminaison, ajoutez un fichier .js ou .ts dans le répertoire /pages. L’extension .js or .ts sera supprimée lors du processus de compilation, pour que le nom de fichier inclus l’extension de données que vous souhaitez créer. Par exemple, src/pages/data.json.ts générera un point de terminaison /data.json.

Les points de terminaison exportent une fonction get (optionellement async) qui reçoit un objet context (EN) avec des propriétés similaires à l’objet Astro global. Elle retourne un objet avec un body, et Astro l’appellera au moment de la compilation et utilisera les contenus du body pour générer le fichier.

src/pages/builtwith.json.ts
// Génère: /builtwith.json
export async function get({params, request}) {
return {
body: JSON.stringify({
name: 'Astro',
url: 'https://astro.build/',
}),
};
}

L’objet retourné peut aussi avoir une propriété encoding. Cela peut être n’importe quel BufferEncoding valide accepté par la method Node.js’ fs.writeFile. Par exemple, pour produire une image binaire png :

src/pages/astro-logo.png.ts
export async function get({ params, request }) {
const response = await fetch("https://astro.build/assets/press/full-logo-light.png");
const buffer = Buffer.from(await response.arrayBuffer());
return {
body: buffer,
encoding: 'binary',
};
}

Vous pouvez également saisir vos fonctions de points de terminaison en utilisant le type APIRoute :

import type { APIRoute } from 'astro';
export const get: APIRoute = async function get ({params, request}) {
...

Les points de terminaison supportent les même fonctionnalités de routes dynamiques que les pages. Nommez votre fichier avec un nom de paramètre entre crochets et exportez une fonction getStaticPaths() (EN). Ensuite, vous pouvez accéder au paramètre en utilisant la propriété params passé à la fonction du point de terminaison :

src/pages/[id].json.ts
import type { APIRoute } from 'astro';
const usernames = ["Sarah", "Chris", "Dan"]
export const get: APIRoute = ({ params, request }) => {
const id = params.id;
return {
body: JSON.stringify({
name: usernames[id]
})
}
};
export function getStaticPaths () {
return [
{ params: { id: "0"} },
{ params: { id: "1"} },
{ params: { id: "2"} },
]
}

Ceci générera trois points de terminaison JSON au moment de la compilation : /api/1.json, /api/2.json, /api/3.json. Le routage dynamique avec les points de terminaison fonctionne de la même façon qu’avec les pages, mais comme le point de terminaison est une fonction et pas un composant, les props (EN) ne sont pas supportées.

Tous les points de terminaison reçoivent une propriété request, mais en mode statique, vous avez uniquement accès à request.url. Ceci retourne l’URL complète du point de terminaison en cours et fonctionne de la même façon que Astro.request.url (EN) le fait pour les pages.

src/pages/request-path.json.ts
import type { APIRoute } from 'astro';
export const get: APIRoute = ({ params, request }) => {
return {
body: JSON.stringify({
path: new URL(request.url).pathname
})
};
}

Points de terminaison Serveur(Routes d’API)

Section titled Points de terminaison Serveur(Routes d’API)

Tout ce qui est décrit dans la section des points de terminaison statiques peut également être utilisé en mode SSR : Les fichiers peuvent exporter une fonction get qui reçoit un objet context (EN) avec des propriétés similaires à l’objet Astro global.

Mais, à la différence du mode static, lorsque vous configurez le mode server, les points de terminaison seront construits à la demande. Ceci déverrouille de nouvelles fonctionnalités qui ne sont pas disponibles au moment de la compilation, et vous permet de construire des routes d’API qui écoutent les demandes et exécutent le code de manière sécurisée sur le serveur au moment de l’exécution.

Les points de terminaison serveur peuvent accéder aux params sans avoir à exporter getStaticPaths, et ils peuvent retourner un objet Response, permettant d’affecter des codes de statut et des en-têtes :

src/pages/[id].json.js
import { getProduct } from '../db';
export async function get({ params }) {
const id = params.id;
const product = await getProduct(id);
if (!product) {
return new Response(null, {
status: 404,
statusText: 'Not found'
});
}
return new Response(JSON.stringify(product), {
status: 200,
headers: {
"Content-Type": "application/json"
}
});
}

Ceci répondra à n’importe quelle requête qui correspond à la route dynamique. Par exemple, si nous naviguons vers /helmet.json, params.id sera affecté à helmet. Si helmet existe dans la base de données product d’exemple, le point de terminaison utilisera un objet Response pour renvoyer du JSON et retourner un code de statut HTTP 200. Sinon, il utilisera un objet Response pour renvoyer une 404.

En plus de la fonction get, vous pouvez exporter une fonction avec le nom de n’importe quelle méthode HTTP. Lorsqu’une requête arrive, Astro vérifiera la méthode et appellera la fonction correspondante.

Vous pouvez aussi exporter une fonction all pour faire correspondre n’importe quelle méthode qui n’a pas de fonction exportée correspondante. S’il y a une demande avec une méthode qui ne correspond pas, elle sera redirigée vers la page 404.

src/pages/methods.json.ts
export const get: APIRoute = ({ params, request }) => {
return {
body: JSON.stringify({
message: "Ceci est un GET !"
})
}
};
export const post: APIRoute = ({ request }) => {
return {
body: JSON.stringify({
message: "Ceci est un POST !"
})
}
}
export const del: APIRoute = ({ request }) => {
return {
body: JSON.stringify({
message: "Ceci est un DELETE !"
})
}
}
export const all: APIRoute = ({ request }) => {
return {
body: JSON.stringify({
message: `Ceci est un ${request.method} !`
})
}
}

En mode SSR, la propriété request retourne un objet Request compètement utilisable qui fait référence à la requête en cours. Cela vous permettra d’accepter les données et vérifier les en-têtes :

src/pages/test-post.json.ts
export const post: APIRoute = async ({ request }) => {
if (request.headers.get("Content-Type") === "application/json") {
const body = await request.json();
const name = body.name;
return new Response(JSON.stringify({
message: "Your name was: " + name
}), {
status: 200
})
}
return new Response(null, { status: 400 });
}

Le contexte du point de terminaison exporte une méthode redirect() utilitaire similaire à Astro.redirect:

src/pages/links/[id].js
import { getLinkUrl } from '../db';
export async function get({ params, redirect }) {
const { id } = params;
const link = await getLinkUrl(id);
if (!link) {
return new Response(null, {
status: 404,
statusText: 'Not found'
});
}
return redirect(link, 307);
}

Les points de terminaison serveur peuvent être utilisés en tant que points de terminaison d’API REST pour exécuter des fonctions telles que des authentifications, de l’accès à une base de données, et des vérifications sans exposer de données sensibles au client.

Dans cet exemple ci-dessous, une route d’API est utilisée pour vérifier un reCAPTCHA Google v3 sans exposer le secret aux clients.

Sur le serveur, nous définissons une méthode post qui accepte les données du recaptcha, puis le vérifie avec l’API reCAPTCHA. Ici, nous pouvons définir les valeurs secrètes ou lire les variables d’environnement de manière sécurisée.

src/pages/recaptcha.js
export async function post({ request }) {
const data = await request.json();
const recaptchaURL = 'https://www.google.com/recaptcha/api/siteverify';
const requestBody = {
secret: "YOUR_SITE_SECRET_KEY", // Ceci peut être une variable d'environnement
response: data.recaptcha // Le token passé par le client
};
const response = await fetch(recaptchaURL, {
method: "POST",
body: JSON.stringify(requestBody)
});
const responseData = await response.json();
return new Response(JSON.stringify(responseData), { status: 200 });
}

Ensuite, nous accédons à notre point de terminaison en utilisant fetch depuis un script client :

src/pages/index.astro
<html>
<head>
<script src="https://www.google.com/recaptcha/api.js"></script>
</head>
<body>
<button class="g-recaptcha"
data-sitekey="PUBLIC_SITE_KEY"
data-callback="onSubmit"
data-action="submit"> Cliquez pour vérifier le captcha ! </button>
<script is:inline>
function onSubmit(token) {
fetch("/recaptcha", {
method: "POST",
body: JSON.stringify({ recaptcha: token })
})
.then((response) => response.json())
.then((gResponse) => {
if (gResponse.success) {
// La vérification du Captcha est un succès
} else {
// La vérification du Captcha a échoué
}
})
}
</script>
</body>
</html>