Skip to main content

Fonctions distantes

Disponible depuis la version 2.27

Les fonctions distantes (remote functions) sont un outil pour communiquer de manière typée entre le client et le serveur. Elles peuvent être appelées depuis n’importe où dans votre application, mais sont toujours exécutées sur le serveur, ce qui signifie qu’elles peuvent accéder de manière sécurisée aux modules réservés au serveur contenant des choses comme des variables d’environnement ou des clients de base de données.

Combinées avec le support expérimental de Svelte pour await, elles vous permettent de charger et de manipuler les données directement au sein de vos composants.

Cette fonctionnalité est actuellement expérimentale, ce qui signifie qu’elle contient très certainement des bugs, et peut être modifiée à tout moment. Vous devez l’activer en ajouter l’option kit.experimental.remoteFunctions dans votre fichier svelte.config.js :

svelte.config
export default {
	
kit: {
    experimental: {
        remoteFunctions: boolean;
    };
}
kit
: {
experimental: {
    remoteFunctions: boolean;
}
experimental
: {
remoteFunctions: booleanremoteFunctions: true } } };

Aperçu

Les fonctions distantes (remote) sont exportées depuis un fichier .remote.js ou .remote.ts, et sont disponibles en quatre versions : query, form, command, et prerender. Sur le client, les fonctions exportées sont transformées en wrappers autour de fetch qui invoquent leur contrepartie sur le serveur en utilisant un endpoint HTTP généré. Les fichiers .remote. doivent être placés dans les dossiers lib ou routes.

query

La fonction query vous permet de lire des données dynamiques depuis le serveur (pour les données statiques, envisagez plutôt l’utilisation de prerender) :

src/routes/blog/data.remote
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
} from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database'; export const const getPosts: RemoteQueryFunction<void, any[]>getPosts = query<any[]>(fn: () => MaybePromise<any[]>): RemoteQueryFunction<void, any[]> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(async () => {
const const posts: any[]posts = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql` SELECT title, slug FROM post ORDER BY published_at DESC `; return const posts: any[]posts; });

Tout au long de cette page, vous verrez des imports depuis des modules fictifs comme $lib/server/database et $lib/server/auth. Ces imports n’existent que pour illustrer les propos — vous pouvez bien sûr utiliser n’importe quel client de base de données et n’importe quel système d’authentification.

La fonction db.sql ci-dessus est une fonction de gabarit étiquété qui échappe toute valeur interpolée.

La query renvoyée par getPosts fonctionne comme une Promise qui résout des posts :

src/routes/blog/+page
<script>
	import { getPosts } from './data.remote';
</script>

<h1>Articles récents</h1>

<ul>
	{#each await getPosts() as { title, slug }}
		<li><a href="/blog/{slug}">{title}</a></li>
	{/each}
</ul>
<script lang="ts">
	import { getPosts } from './data.remote';
</script>

<h1>Articles récents</h1>

<ul>
	{#each await getPosts() as { title, slug }}
		<li><a href="/blog/{slug}">{title}</a></li>
	{/each}
</ul>

Tant que la promesse n’est pas résolue — et si elle échoue — la <svelte:boundary> la plus proche sera invoquée.

Bien que l’utilisation de await soit recommandée, vous pouvez également utiliser en alternative les propriétés loading, error et current de la query :

src/routes/blog/+page
<script>
	import { getPosts } from './data.remote';

	const query = getPosts();
</script>

{#if query.error}
	<p>oups !</p>
{:else if query.loading}
	<p>chargement...</p>
{:else}
	<ul>
		{#each query.current as { title, slug }}
			<li><a href="/blog/{slug}">{title}</a></li>
		{/each}
	</ul>
{/if}
<script lang="ts">
	import { getPosts } from './data.remote';

	const query = getPosts();
</script>

{#if query.error}
	<p>oups !</p>
{:else if query.loading}
	<p>chargement...</p>
{:else}
	<ul>
		{#each query.current as { title, slug }}
			<li><a href="/blog/{slug}">{title}</a></li>
		{/each}
	</ul>
{/if}

Pour le reste de ce document, nous utiliserons la forme await.

Arguments de query

Les fonctions query peuvent accepter un argument, comme le slug d’un article individuel :

src/routes/blog/[slug]/+page
<script>
	import { getPost } from '../data.remote';

	let { params } = $props();

	const post = $derived(await getPost(params.slug));
</script>

<h1>{post.title}</h1>
<div>{@html post.content}</div>
<script lang="ts">
	import { getPost } from '../data.remote';

	let { params } = $props();

	const post = $derived(await getPost(params.slug));
</script>

<h1>{post.title}</h1>
<div>{@html post.content}</div>

Puisque getPost expose un endpoint HTTP, il est important de valider cet argument pour garantir qu’il est du type attendu. Pour cela, nous pouvons utiliser n’importe quelle librairie de validation de Standard Schema, comme Zod ou Valibot :

src/routes/blog/data.remote
import * as import vv from 'valibot';
import { function error(status: number, body: App.Error): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
} from '@sveltejs/kit';
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
} from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database'; export const const getPosts: RemoteQueryFunction<void, void>getPosts = query<void>(fn: () => MaybePromise<void>): RemoteQueryFunction<void, void> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(async () => { /* ... */ });
export const const getPost: RemoteQueryFunction<string, any>getPost = query<v.StringSchema<undefined>, any>(schema: v.StringSchema<undefined>, fn: (arg: string) => any): RemoteQueryFunction<string, any> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(), async (slug: stringslug) => {
const [const post: anypost] = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql` SELECT * FROM post WHERE slug = ${slug: stringslug} `; if (!const post: anypost)
function error(status: number, body?: {
    message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(404, 'Not found');
return const post: anypost; });

L’argument et la valeur de retour sont tous deux sérialisés avec devalue, qui gère des types comme Date et Map (et également des types personnalisés que vous pouvez définir dans votre transport hook) en plus du JSON.

Mettre les queries à jour

Toute query peut être mise à jour via sa méthode refresh :

<button onclick={() => getPosts().refresh()}>
	Vérifier s'il y a des nouveaux articles
</button>

Les queries sont mises en cache tant qu’elles sont sur la page, ce qui veut dire que getPosts() === getPosts(). Cela signifie que vous n’avez pas besoin d’une référence comme const posts = getPosts() pour mettre à jour la query.

form

La fonction form facilite l’écriture de données sur le serveur. Elle prend un callback qui reçoit le FormData actuel...

src/routes/blog/data.remote
import * as import vv from 'valibot';
import { function error(status: number, body: App.Error): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
, function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

Most common status codes:

  • 303 See Other: redirect as a GET request (often used after a form POST request)
  • 307 Temporary Redirect: redirect will keep the request method
  • 308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page

See all redirect status codes

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
} from '@sveltejs/kit';
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
, function form<T>(fn: (data: FormData) => MaybePromise<T>): RemoteForm<T>

Creates a form object that can be spread onto a &#x3C;form> element.

See Remote functions for full documentation.

form
} from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database'; import * as module "$lib/server/auth"auth from '$lib/server/auth'; export const const getPosts: RemoteQueryFunction<void, void>getPosts = query<void>(fn: () => MaybePromise<void>): RemoteQueryFunction<void, void> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(async () => { /* ... */ });
export const const getPost: RemoteQueryFunction<string, void>getPost = query<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>): RemoteQueryFunction<string, void> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(), async (slug: stringslug) => { /* ... */ });
export const const createPost: RemoteForm<never>createPost = form<never>(fn: (data: FormData) => Promise<never>): RemoteForm<never>

Creates a form object that can be spread onto a &#x3C;form> element.

See Remote functions for full documentation.

form
(async (data: FormDatadata) => {
// Vérifie si l'utilisateur est connecté const const user: auth.User | nulluser = await module "$lib/server/auth"auth.function getUser(): Promise<auth.User | null>

Récupère les informations de l’utilisateur à partir des cookies, en utilisant getRequestEvent

getUser
();
if (!const user: auth.User | nulluser)
function error(status: number, body?: {
    message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(401, 'Unauthorized');
const const title: FormDataEntryValue | nulltitle = data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('title'); const const content: FormDataEntryValue | nullcontent = data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('content'); // Vérifie si les données sont valides if (typeof const title: FormDataEntryValue | nulltitle !== 'string' || typeof const content: FormDataEntryValue | nullcontent !== 'string') {
function error(status: number, body?: {
    message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(400, 'Title and content are required');
} const const slug: stringslug = const title: stringtitle.String.toLowerCase(): string

Converts all the alphabetic characters in a string to lowercase.

toLowerCase
().
String.replace(searchValue: {
    [Symbol.replace](string: string, replaceValue: string): string;
}, replaceValue: string): string (+3 overloads)

Passes a string and {@linkcode replaceValue } to the [Symbol.replace] method on {@linkcode searchValue } . This method is expected to implement its own replacement algorithm.

@paramsearchValue An object that supports searching for and replacing matches within a string.
@paramreplaceValue The replacement text.
replace
(/ /g, '-');
// Insère en base de données await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql` INSERT INTO post (slug, title, content) VALUES (${const slug: stringslug}, ${const title: stringtitle}, ${const content: stringcontent}) `; // Redirige vers la page nouvellement créée function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

Most common status codes:

  • 303 See Other: redirect as a GET request (often used after a form POST request)
  • 307 Temporary Redirect: redirect will keep the request method
  • 308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page

See all redirect status codes

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
(303, `/blog/${const slug: stringslug}`);
});

... et renvoie un objet qui peut être distribué sur un élément <form>. Le callback est appelée à chaque fois que le formulaire est soumis.

src/routes/blog/new/+page
<script>
	import { createPost } from '../data.remote';
</script>

<h1>Créer un nouvel article</h1>

<form {...createPost}>
	<label>
		<h2>Titre</h2>
		<input name="title" />
	</label>

	<label>
		<h2>Écrivez votre article</h2>
		<textarea name="content"></textarea>
	</label>

	<button>Publier !</button>
</form>
<script lang="ts">
	import { createPost } from '../data.remote';
</script>

<h1>Créer un nouvel article</h1>

<form {...createPost}>
	<label>
		<h2>Titre</h2>
		<input name="title" />
	</label>

	<label>
		<h2>Écrivez votre article</h2>
		<textarea name="content"></textarea>
	</label>

	<button>Publier !</button>
</form>

L’objet de formulaire contient les propriétés method et action qui lui permettent de fonctionner sans JavaScript (c-à-d qu’il enverra les données puis qu’il rechargera la page). Il a également un gestionnaire onsubmit qui améliore progressivement le formulaire lorsque JavaScript est disponible, en soumettant les données sans recharger entièrement la page.

Mutations single-flight

Par défaut, toutes les queries utilisées sur la page (en plus de toutes fonction load) sont automatiquement mises à jour à la suite d’une soumission de formulaire. Ceci assure que tout soit à jour, mais est également inefficace : beaucoup de queries seront inchangées, et cela requiert un deuxième aller-retour vers le serveur pour obtenir les données à jour.

À la place, nous pouvons préciser quelles queries devraient être mises à jour en réponse à une soumission particulière de formulaire. On appelle cela une mutation single-flight (mutation sur un seul aller-retour), et il y a deux manières de faire. La première est de rafraîchir la query sur le serveur, dans le gestionnaire de formulaire :

export const const getPosts: RemoteQueryFunction<void, void>getPosts = query<void>(fn: () => MaybePromise<void>): RemoteQueryFunction<void, void> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(async () => { /* ... */ });
export const const getPost: RemoteQueryFunction<string, void>getPost = query<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>): RemoteQueryFunction<string, void> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(), async (slug: stringslug) => { /* ... */ });
export const const createPost: RemoteForm<never>createPost = form<never>(fn: (data: FormData) => Promise<never>): RemoteForm<never>

Creates a form object that can be spread onto a &#x3C;form> element.

See Remote functions for full documentation.

form
(async (data: FormDatadata) => {
// la logique du formulaire se place ici... // Met à jour `getPosts()` sur le serveur, et // renvoie les données au client avec le résultat // de `createPost` await const getPosts: (arg: void) => RemoteQuery<void>getPosts().function refresh(): Promise<void>

On the client, this function will re-fetch the query from the server.

On the server, this can be called in the context of a command or form and the refreshed data will accompany the action response back to the client. This prevents SvelteKit needing to refresh all queries on the page in a second server round-trip.

refresh
();
// Redirige vers la page nouvellement créée function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

Most common status codes:

  • 303 See Other: redirect as a GET request (often used after a form POST request)
  • 307 Temporary Redirect: redirect will keep the request method
  • 308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page

See all redirect status codes

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
(303, `/blog/${const slug: ""slug}`);
});

La seconde est de conduire la mutation single-flight depuis le client, ce que nous verrons dans la section sur enhance.

Renvois et redirections

L’exemple ci-dessus utilise redirect(...), qui redirige l’utilisateur ou l’utilisatrice vers la page nouvellement créée. De manière alternative, le callback pourrait également renvoyer les données, auquel cas elles seraient disponibles en tant que createPost.result :

src/routes/blog/data.remote
export const 
const createPost: RemoteForm<{
    success: boolean;
}>
createPost
=
form<{
    success: boolean;
}>(fn: (data: FormData) => MaybePromise<{
    success: boolean;
}>): RemoteForm<{
    success: boolean;
}>

Creates a form object that can be spread onto a &#x3C;form> element.

See Remote functions for full documentation.

form
(async (data: FormDatadata) => {
// ... return { success: booleansuccess: true }; });
src/routes/blog/new/+page
<script>
	import { createPost } from '../data.remote';
</script>

<h1>Créer un nouvel article</h1>

<form {...createPost}><!-- ... --></form>

{#if createPost.result?.success}
	<p>Publié avec succès !</p>
{/if}
<script lang="ts">
	import { createPost } from '../data.remote';
</script>

<h1>Créer un nouvel article</h1>

<form {...createPost}><!-- ... --></form>

{#if createPost.result?.success}
	<p>Publié avec succès !</p>
{/if}

Cette valeur est éphémère — elle disparaîtra si vous re-soumettez le formulaire, si vous naviguez sur une autre page, ou si vous rechargez la page.

La valeur de result n’indique pas nécessairement un succès — elle peut également contenir des erreurs de validation, ainsi que des données permettant de re-remplir le formulaire en cas de rechargement de la page.

Si une erreur se produit lors de la soumission, la page +error.svelte la plus proche sera affichée.

enhance

Nous pouvons personnaliser ce qui se produit lorsque le formulaire est soumis grâce à la méthode enhance :

src/routes/blog/new/+page
<script>
	import { createPost } from '../data.remote';
	import { showToast } from '$lib/toast';
</script>

<h1>Créer un nouvel article</h1>

<form {...createPost.enhance(async ({ form, data, submit }) => {
	try {
		await submit();
		form.reset();

		showToast('Publié avec succès !');
	} catch (error) {
		showToast('Oh non ! Quelque chose s\'est mal passé !');
	}
})}>
	<input name="title" />
	<textarea name="content"></textarea>
	<button>publier</button>
</form>
<script lang="ts">
	import { createPost } from '../data.remote';
	import { showToast } from '$lib/toast';
</script>

<h1>Créer un nouvel article</h1>

<form {...createPost.enhance(async ({ form, data, submit }) => {
	try {
		await submit();
		form.reset();

		showToast('Publié avec succès !');
	} catch (error) {
		showToast('Oh non ! Quelque chose s\'est mal passé !');
	}
})}>
	<input name="title" />
	<textarea name="content"></textarea>
	<button>publier</button>
</form>

Le callback reçoit l’élément form, la data qu’il contient, et une fonction submit.

Pour activer les mutations single-flight depuis le client, utilisez submit().updates(...}). Par exemple, si la query getPosts() était utilisée sur cette page, nous pourrions la mettre à jour de cette manière :

await 
function submit(): Promise<any> & {
    updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<any>;
}
submit
().function updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<any>updates(function getPosts(): RemoteQuery<Post[]>getPosts());

Nous pouvons aussi surcharger les données courantes pendant que la soumission est en cours de traitement :

await 
function submit(): Promise<any> & {
    updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<any>;
}
submit
().function updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<any>updates(
function getPosts(): RemoteQuery<Post[]>getPosts().function withOverride(update: (current: Post[]) => Post[]): RemoteQueryOverride

Temporarily override the value of a query. This is used with the updates method of a command or enhanced form submission to provide optimistic updates.

&#x3C;script>
  import { getTodos, addTodo } from './todos.remote.js';
  const todos = getTodos();
&#x3C;/script>

&#x3C;form {...addTodo.enhance(async ({ data, submit }) => {
  await submit().updates(
	todos.withOverride((todos) => [...todos, { text: data.get('text') }])
  );
}}>
  &#x3C;input type="text" name="text" />
  &#x3C;button type="submit">Add Todo&#x3C;/button>
&#x3C;/form>
withOverride
((posts: Post[]posts) => [const newPost: PostnewPost, ...posts: Post[]posts])
);

La surcharge sera appliquée immédiatement, et annulée lorsque la soumission se termine (ou échoue).

buttonProps

Par défaut, soumettre un formulaire va envoyer une requête à l’URL indiquée par l’attribut action de l’élément <form>, ce qui dans le cas d’une fonction distante est une propriété sur l’objet de formulaire généré par SvelteKit.

Il est possible pour un <button> au sein d’un <form> d’envoyer la requête à une URL différente, en utilisant l’attribut formaction. Par exemple, vous pourriez avoir un seul formulaire vous permettant de vous connecter ou de vous inscrire, selon le bouton sur lequel vous cliquez.

Cet attribut existe dans la propriété buttonProps d’un objet de formulaire :

src/routes/login/+page
<script>
	import { login, register } from '$lib/auth';
</script>

<form {...login}>
	<label>
		Votre nom d'utilisateur
		<input name="username" />
	</label>

	<label>
		Votre mot de passe
		<input name="password" type="password" />
	</label>

	<button>se connecter</button>
	<button {...register.buttonProps}>s'inscrire</button>
</form>
<script lang="ts">
	import { login, register } from '$lib/auth';
</script>

<form {...login}>
	<label>
		Votre nom d'utilisateur
		<input name="username" />
	</label>

	<label>
		Votre mot de passe
		<input name="password" type="password" />
	</label>

	<button>se connecter</button>
	<button {...register.buttonProps}>s'inscrire</button>
</form>

Comme l’objet de formulaire lui-même, buttonProps possède une méthode enhance permettant de personnaliser la soumission du formulaire.

command

La fonction command, comme form, vous permet d’écrire des données sur le serveur. À la différence de form, elle n’est pas spécifique à un élément et peut être appelée depuis n’importe où.

Privilégiez l’usage de form lorsque c’est possible, puisqu’elle continue de fonctionner lorsque JavaScript est désactivé ou a échoué à se charger.

Comme avec query, si la fonction accepte un argument, celui-ci devrait être validé en fournissant un Standard Schema en tant que premier argument à command.

likes.remote
import * as import vv from 'valibot';
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
, function command<Output>(fn: () => Output): RemoteCommand<void, Output> (+2 overloads)

Creates a remote command. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

command
} from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database'; export const const getLikes: RemoteQueryFunction<string, any>getLikes = query<v.StringSchema<undefined>, any>(schema: v.StringSchema<undefined>, fn: (arg: string) => any): RemoteQueryFunction<string, any> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(), async (id: stringid) => {
const [const row: anyrow] = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql` SELECT likes FROM item WHERE id = ${id: stringid} `; return const row: anyrow.likes; }); export const const addLike: RemoteCommand<string, Promise<void>>addLike = command<v.StringSchema<undefined>, Promise<void>>(validate: v.StringSchema<undefined>, fn: (arg: string) => Promise<void>): RemoteCommand<string, Promise<void>> (+2 overloads)

Creates a remote command. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

command
(import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(), async (id: stringid) => {
await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql` UPDATE item SET likes = likes + 1 WHERE id = ${id: stringid} `; });

Vous pouvez maintenant simplement appeler addLike, depuis (par exemple) un gestionnaire d’évènement :

+page
<script>
	import { getLikes, addLike } from './likes.remote';
	import { showToast } from '$lib/toast';

	let { item } = $props();
</script>

<button
	onclick={async () => {
		try {
			await addLike(item.id);
		} catch (error) {
			showToast('Quelque chose s\'est mal passé !');
		}
	}}
>
	ajouter un like
</button>

<p>likes : {await getLikes(item.id)}</p>
<script lang="ts">
	import { getLikes, addLike } from './likes.remote';
	import { showToast } from '$lib/toast';

	let { item } = $props();
</script>

<button
	onclick={async () => {
		try {
			await addLike(item.id);
		} catch (error) {
			showToast('Quelque chose s\'est mal passé !');
		}
	}}
>
	ajouter un like
</button>

<p>likes : {await getLikes(item.id)}</p>

Les commandes ne peuvent pas être appelées lors du rendu.

Mutations single-flight

Comme avec les formulaires, toute query sur la page (comme getLikes(item.id) dans l’exemple ci-dessus) sera automatiquement mise à jour à la suite d’une commande qui se passe bien. Mais nous pouvons rendre les choses plus efficaces en disant à SvelteKit quelles queries seront affectées par la commande, soit au sein de la commande elle-même...

likes.remote
export const const getLikes: RemoteQueryFunction<string, void>getLikes = query<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>): RemoteQueryFunction<string, void> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(), async (id: stringid) => { /* ... */ });
export const const addLike: RemoteCommand<string, Promise<void>>addLike = command<v.StringSchema<undefined>, Promise<void>>(validate: v.StringSchema<undefined>, fn: (arg: string) => Promise<void>): RemoteCommand<string, Promise<void>> (+2 overloads)

Creates a remote command. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

command
(import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(), async (id: stringid) => {
await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql` UPDATE item SET likes = likes + 1 WHERE id = ${id: stringid} `; const getLikes: (arg: string) => RemoteQuery<void>getLikes(id: stringid).function refresh(): Promise<void>

On the client, this function will re-fetch the query from the server.

On the server, this can be called in the context of a command or form and the refreshed data will accompany the action response back to the client. This prevents SvelteKit needing to refresh all queries on the page in a second server round-trip.

refresh
();
});

... soit lorsque nous l’exécutons :

try {
	await 
const addLike: (arg: string) => Promise<void> & {
    updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<...>;
}
addLike
(const item: Itemitem.Item.id: stringid).function updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<void>updates(const getLikes: (arg: string) => RemoteQuery<number>getLikes(const item: Itemitem.Item.id: stringid));
} catch (var error: unknownerror) { function showToast(message: string): voidshowToast('Quelque chose s\'est mal passé !'); }

Comme précédemment, nous pouvons utiliser withOverride pour faire des mises à jour optimistes :

try {
	await 
const addLike: (arg: string) => Promise<void> & {
    updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<...>;
}
addLike
(const item: Itemitem.Item.id: stringid).function updates(...queries: Array<RemoteQuery<any> | RemoteQueryOverride>): Promise<void>updates(
const getLikes: (arg: string) => RemoteQuery<number>getLikes(const item: Itemitem.Item.id: stringid).function withOverride(update: (current: number) => number): RemoteQueryOverride

Temporarily override the value of a query. This is used with the updates method of a command or enhanced form submission to provide optimistic updates.

&#x3C;script>
  import { getTodos, addTodo } from './todos.remote.js';
  const todos = getTodos();
&#x3C;/script>

&#x3C;form {...addTodo.enhance(async ({ data, submit }) => {
  await submit().updates(
	todos.withOverride((todos) => [...todos, { text: data.get('text') }])
  );
}}>
  &#x3C;input type="text" name="text" />
  &#x3C;button type="submit">Add Todo&#x3C;/button>
&#x3C;/form>
withOverride
((n: numbern) => n: numbern + 1)
); } catch (var error: unknownerror) { function showToast(message: string): voidshowToast('Quelque chose s\'est mal passé !'); }

prerender

La fonction prerender est similaire à query, sauf qu’elle sera exécutée lors de la compilation pour pré-rendre le résultat. Utilisez cette fonction pour des données qui changent au plus une fois par déploiement.

src/routes/blog/data.remote
import { 
function prerender<Output>(fn: () => MaybePromise<Output>, options?: {
    inputs?: RemotePrerenderInputsGenerator<void>;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<void, Output> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
} from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database'; export const const getPosts: RemotePrerenderFunction<void, any[]>getPosts =
prerender<any[]>(fn: () => MaybePromise<any[]>, options?: {
    inputs?: RemotePrerenderInputsGenerator<void>;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
(async () => {
const const posts: any[]posts = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql` SELECT title, slug FROM post ORDER BY published_at DESC `; return const posts: any[]posts; });

Vous pouvez utiliser des fonctions prerender sur des pages par ailleurs dynamiques, permettant le pré-rendu partiel de vos données. Ceci permet une navigation très rapide, puisque les données prérendues peuvent être stockées sur un CDN à côté de vos autres assets statiques.

Dans le navigateur, les données pré-rendues sont sauvegardées en utilisant l’API Cache. Ce cache survit aux rechargements de page, et sera nettoyé lorsque l’utilisateur ou l’utilisatrice visitera pour la première fois une nouveau déploiement de votre application.

Lorsque la page entière a export const prerender = true, vous ne pouvez pas utiliser de queries, puisqu’elles sont dynamiques.

Arguments de prerender

Comme avec les queries, les fonctions prerender peuvent accepter un argument, qui devrait être validé avec un Standard Schema :

src/routes/blog/data.remote
import * as import vv from 'valibot';
import { function error(status: number, body: App.Error): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
} from '@sveltejs/kit';
import {
function prerender<Output>(fn: () => MaybePromise<Output>, options?: {
    inputs?: RemotePrerenderInputsGenerator<void>;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<void, Output> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
} from '$app/server';
import * as module "$lib/server/database"db from '$lib/server/database'; export const const getPosts: RemotePrerenderFunction<void, void>getPosts =
prerender<void>(fn: () => MaybePromise<void>, options?: {
    inputs?: RemotePrerenderInputsGenerator<void>;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
(async () => { /* ... */ });
export const const getPost: RemotePrerenderFunction<string, any>getPost =
prerender<v.StringSchema<undefined>, any>(schema: v.StringSchema<undefined>, fn: (arg: string) => any, options?: {
    inputs?: RemotePrerenderInputsGenerator<string> | undefined;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
(import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(), async (slug: stringslug) => {
const [const post: anypost] = await module "$lib/server/database"db.function sql(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>sql` SELECT * FROM post WHERE slug = ${slug: stringslug} `; if (!const post: anypost)
function error(status: number, body?: {
    message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(404, 'Not found');
return const post: anypost; });

Tout appel à getPost(...) identifié par le crawler de SvelteKit lors du pré-rendu des pages sera automatiquement enregistré, mais vous pouvez également préciser avec quelles valeurs chaque appel devrait être fait en utilisant l’option inputs :

src/routes/blog/data.remote

export const const getPost: RemotePrerenderFunction<string, void>getPost = 
prerender<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>, options?: {
    inputs?: RemotePrerenderInputsGenerator<string> | undefined;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
(
import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(),
async (slug: stringslug) => { /* ... */ }, { inputs?: RemotePrerenderInputsGenerator<string> | undefinedinputs: () => [ 'premier-article', 'deuxième-article', 'troisième-article' ] } );
export const const getPost: RemotePrerenderFunction<string, void>getPost = 
prerender<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>, options?: {
    inputs?: RemotePrerenderInputsGenerator<string> | undefined;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
(
import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(),
async (slug: stringslug) => { /* ... */ }, { inputs?: RemotePrerenderInputsGenerator<string> | undefinedinputs: () => [ 'premier-article', 'deuxième-article', 'troisième-article' ] } );

Svelte ne supporte pas encore le rendu asynchrone côté serveur, et il est donc probable que vous n’appeliez les fonctions distantes que depuis le serveur, plutôt que lors du pré-rendu. À cause de cela, vous aurez besoin d’utiliser inputs, pour le moment. Nous travaillons activement sur point bloquant.

Par défaut, les fonctions de pré-rendu sont exclues de votre bundle de compilation, ce qui signifie que vous ne pouvez pas les exécuter avec des arguments qui n’ont pas été pré-rendus. Vous pouvez définir dynamic: true pour changer ce comportement :

src/routes/blog/data.remote

export const const getPost: RemotePrerenderFunction<string, void>getPost = 
prerender<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>, options?: {
    inputs?: RemotePrerenderInputsGenerator<string> | undefined;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
(
import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(),
async (slug: stringslug) => { /* ... */ }, { dynamic?: boolean | undefineddynamic: true, inputs?: RemotePrerenderInputsGenerator<string> | undefinedinputs: () => [ 'premier-article', 'deuxième-article', 'troisième-article' ] } );
export const const getPost: RemotePrerenderFunction<string, void>getPost = 
prerender<v.StringSchema<undefined>, void>(schema: v.StringSchema<undefined>, fn: (arg: string) => MaybePromise<void>, options?: {
    inputs?: RemotePrerenderInputsGenerator<string> | undefined;
    dynamic?: boolean;
} | undefined): RemotePrerenderFunction<...> (+2 overloads)

Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

prerender
(
import vv.
function string(): v.StringSchema<undefined> (+1 overload)
export string

Creates a string schema.

@returnsA string schema.
string
(),
async (slug: stringslug) => { /* ... */ }, { dynamic?: boolean | undefineddynamic: true, inputs?: RemotePrerenderInputsGenerator<string> | undefinedinputs: () => [ 'premier-article', 'deuxième-article', 'troisième-article' ] } );

Gestion des erreurs de validation

Tant que vous ne passez pas de données invalides à vos fonctions distantes, il n’y a que deux raisons pour lesquelles l’argument passé à une command, une query ou une prerender échouerait à la validation :

  • la signature de la fonction a changé entre deux déploiements, et certains utilisateurs ou utilisatrices sont actuellement sur une version ancienne de votre application
  • quelqu’un est en train d’essayer d’attaquer votre site en testant les endpoints que vous exposez avec des données malformées

Dans le second cas, nous ne voulons pas donner à l’attaquant d’indices, c’est pourquoi SvelteKit va générer une réponse générique 400 Bad Request. Vous pouvez contrôler le message en implémentant le hook serveur handleValidationError, qui, comme handleError, doit renvoyer un objet App.Error (qui est défini par défaut à { message: string }) :

src/hooks.server
/** @type {import('@sveltejs/kit').HandleValidationError} */
export function 
function handleValidationError({ event, issues }: {
    event: any;
    issues: any;
}): {
    message: string;
}
@type{import('@sveltejs/kit').HandleValidationError}
handleValidationError
({ event: anyevent, issues: anyissues }) {
return { message: stringmessage: 'Nice try, hacker!' }; }
import type { 
type HandleValidationError<Issue extends StandardSchemaV1.Issue = StandardSchemaV1.Issue> = (input: {
    issues: Issue[];
    event: RequestEvent;
}) => MaybePromise<App.Error>

The handleValidationError hook runs when the argument to a remote function fails validation.

It will be called with the validation issues and the event, and must return an object shape that matches App.Error.

HandleValidationError
} from '@sveltejs/kit';
export const const handleValidationError: HandleValidationErrorhandleValidationError:
type HandleValidationError<Issue extends StandardSchemaV1.Issue = StandardSchemaV1.Issue> = (input: {
    issues: Issue[];
    event: RequestEvent;
}) => MaybePromise<App.Error>

The handleValidationError hook runs when the argument to a remote function fails validation.

It will be called with the validation issues and the event, and must return an object shape that matches App.Error.

HandleValidationError
= ({ event: RequestEvent<AppLayoutParams<"/">, any>event, issues: StandardSchemaV1.Issue[]issues }) => {
return { App.Error.message: stringmessage: 'Nice try, hacker!' }; };

Si vous savez ce que vous faites et souhaitez vous passer de validation, vous pouvez passer la chaîne de caractères 'unchecked' à la place du schéma :

data.remote
import { function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
} from '$app/server';
export const
const getStuff: RemoteQueryFunction<{
    id: string;
}, void>
getStuff
=
query<{
    id: string;
}, void>(validate: "unchecked", fn: (arg: {
    id: string;
}) => MaybePromise<void>): RemoteQueryFunction<{
    id: string;
}, void> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
('unchecked', async ({ id: stringid }: { id: stringid: string }) => {
// la forme peut ne pas être ce que pense TypeScript // puisque des personnes mal intentionnées pourraient // appeler cette fonction avec d'autres arguments });

form n’accepte pas de schéma puisque vous recevrez toujours un objet FormData. Vous êtes libres de parser et valider ces données comme bon vous semble.

Utiliser getRequestEvent

Dans une fonction query, form ou command, vous pouvez utiliser getRequestEvent pour obtenir l’objet RequestEvent courant. Ceci permet de construire simplement des abstractions pour interagir avec des cookies, par exemple :

user.remote
import { function getRequestEvent(): RequestEvent<findUser<"/">, any>

Returns the current RequestEvent. Can be used inside server hooks, server load functions, actions, and endpoints (and functions called by them).

In environments without AsyncLocalStorage, this must be called synchronously (i.e. not after an await).

@since2.20.0
getRequestEvent
, function query<Output>(fn: () => MaybePromise<Output>): RemoteQueryFunction<void, Output> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
} from '$app/server';
import { import findUserfindUser } from '$lib/server/database'; export const
const getProfile: RemoteQueryFunction<void, {
    name: any;
    avatar: any;
}>
getProfile
=
query<{
    name: any;
    avatar: any;
}>(fn: () => MaybePromise<{
    name: any;
    avatar: any;
}>): RemoteQueryFunction<void, {
    name: any;
    avatar: any;
}> (+2 overloads)

Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.

See Remote functions for full documentation.

query
(async () => {
const const user: anyuser = await function getUser(): anygetUser(); return { name: anyname: const user: anyuser.name, avatar: anyavatar: const user: anyuser.avatar }; }); // cette fonction pourrait être appelée depuis plusieurs endroits function function getUser(): anygetUser() { const { const cookies: Cookies

Get or set cookies related to the current request

cookies
, const locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
} = function getRequestEvent(): RequestEvent<findUser<"/">, any>

Returns the current RequestEvent. Can be used inside server hooks, server load functions, actions, and endpoints (and functions called by them).

In environments without AsyncLocalStorage, this must be called synchronously (i.e. not after an await).

@since2.20.0
getRequestEvent
();
const locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.userPromise ??= import findUserfindUser(const cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get: (name: string, opts?: CookieParseOptions) => string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('session_id'));
return await const locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.userPromise;
}

Notez que certaines propriétés de RequestEvent sont différentes au sein des fonctions distantes. Il n’y a pas de params ou de route.id, et vous ne pouvez pas définir d’en-têtes (si ce n’est écrire dans des cookies, et uniquement dans des fonctions form et command), et url.pathname vaut toujours / (puisque le chemin qui est vraiment requêté par le client est essentiellement un détail d’implémentation).

Redirections

Au sein d’une fonction query, form ou prerender, il est possible d’utiliser la fonction redirect(...). Ce n’est pas possible au sein d’une fonction command, car vous devriez éviter de rediriger à cet endroit. (Si vous avez absolument besoin de le faire, vous pouvez toujours renvoyer un objet { redirect: location } et gérer la redirection côté client.)

Modifier cette page sur Github llms.txt