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 ajoutant
les options compilerOptions.experimental.async et kit.experimental.remoteFunctions dans votre
fichier svelte.config.js.
optionnelle en ajoutant l’option kit.experimental.remoteFunctions dans votre svelte.config.js :
The options to be passed to the Svelte compiler. A few options are set by default,
including dev and css. However, some options are non-configurable, like
filename, format, generate, and cssHash (in dev).
Allow await keyword in deriveds, template expressions, and the top level of components
5.36
:true}}};exportdefaultconfigconstconfig:Config
{import('@sveltejs/kit').Config}
;
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. peuvent être placés
n’importe où dans votre dossier src (sauf dans le dossier src/lib/server), et des librairies
externes peuvent également en fournir.
query
La fonction query vous permet de lire des données dynamiques depuis le serveur.
Pour des données statiques, envisagez plutôt les fonctions prerender.
Les queries ne peuvent pas être utilisées lorsque la page entière est pré-rendue (ce qui veut dire
que export const prerender = true est appliquée à la page ou à un
layout parent), comme lorsque vous utilisez adapter-static.
reference(async()=>{constpostsconstposts:any[]=awaitdb(alias)module"$lib/server/database"importdb.sqlfunctionsql(strings:TemplateStringsArray,...values:any[]):Promise<any[]>`
SELECT title, slug
FROM post
ORDER BY published_at
DESC
`;returnpostsconstposts:any[];});
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.
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 :
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.
(),async(slug(parameter)slug:string)=>{const[postconstpost:any]=awaitdb(alias)module"$lib/server/database"importdb.sqlfunctionsql(strings:TemplateStringsArray,...values:any[]):Promise<any[]>`
SELECT * FROM post
WHERE slug =${slug(parameter)slug:string}`;if(!postconstpost:any)error(alias)error(status:number,body?:{message:string;}extendsApp.Error?App.Error|string|undefined:never):never(+1overload)importerror
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.
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.
Pour les arguments de query et prerender (mais pas leurs valeurs de retour), les
objets, maps et sets sont triés de sorte que les occurrences ayant les mêmes éléments soient
associées à une même clé de cache. Par exemple, getPosts({ limit: 10, offset: 10 }) et
getPosts({ offset: 10, limit: 10 }) seront associés à la même clé. Si l’ordre a de l’importance
pour vous, vous devrez utiliser un tableau.
Déduplication
Lorsque vous appelez une fonction query, SvelteKit sérialise l’argument avec lequel vous l’appelez
et s’en sert en tant que clé de cache. Sur le serveur, cette clé est utilisée pour créer un cache
scopé à la requête pour que plusieurs invocations de la même requête ne provoquent qu’une seule fois
le travail à faire. Sur le client, SvelteKit fait quelque chose de similaire : plusieurs invocations
identiques d’une même query pointent toutes vers la même instance.
Vous pouvez await une query dans n’importe quel contexte — composants, gestionnaires d’évènements,
fonctions load universelles, callbacks asynchrones — et SvelteKit va dédupliquer en se servant de
tout ce qui consomme la même query.
Par exemple:
<script>import{getData}from'./data.remote.js';// attendue au sein du template de composants — remplit le cacheconstdata=getData();</script><p>{awaitdata}</p><!-- ceci déduplique en se servant de l'usage "dans le composant" juste au-dessus ; aucune requête supplémentaire n'est effectuée --><buttononclick={async()=>console.log(awaitgetData())}>cliquez-moi dessus !</button>
Le cache est partagé tant que la query est activement utilisée — rendue dans un composant,
actuellement attendue, ou autrement référencée. Dès que plus rien ne se sert de la query, la valeur
cachée est libérée.
Mettre les queries à jour
Toute query peut être rafraîchie via sa méthode refresh, qui récupère la valeur la plus récente
depuis le serveur :
<buttononclick={()=>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.
query.batch
query.batch fonctionne comme query à l’exception du fait qu’il regroupe les requêtes se
produisant lors de la même micro-tâche. Ceci résout le bien nommé problème n+1 : plutôt que chaque
requête résulte en un appel séparé à la base de données (par exemple), les requêtes simultanées sont
regroupées.
Sur le serveur, le callback reçoit un tableau des arguments avec lesquels la fonction a été appelée.
Il doit renvoyer une fonction de signature (input: Input, index: number) => Output. SvelteKit
exécutera alors cette fonction avec chacun des arguments d’entrée pour résoudre les appels
individuels et leurs résultats.
(),async(cityIds(parameter)cityIds:string[])=>{constweatherconstweather:any[]=awaitdb(alias)module"$lib/server/database"importdb.sqlfunctionsql(strings:TemplateStringsArray,...values:any[]):Promise<any[]>`
SELECT * FROM weather
WHERE city_id = ANY(${cityIds(parameter)cityIds:string[]})
`;constlookupconstlookup:Map<any,any>=newMapvarMap:MapConstructornew<any,any>(iterable?:Iterable<readonly[any,any]>|null|undefined)=>Map<any,any>(+3overloads)(weatherconstweather:any[].map(method)Array<any>.map<[any,any]>(callbackfn:(value:any,index:number,array:any[])=>[any,any],thisArg?:any):[any,any][]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
callbackfn
A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
thisArg
An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
Returns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map.
Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned.
query.live sert à accéder à des données en temps réel depuis le serveur. Elle fonctionne de la
même manière que query, mais son callback — typiquement une fonction
génératrice
asynchrone — renvoie un AsyncIterable :
A callback used to initialize the promise. This callback is passed two arguments:
a resolve callback used to resolve the promise with a value or the result of another promise,
and a reject callback used to reject the promise with a provided reason or error.
Lors du rendu côté serveur, await getTime() renvoie la première valeur fournie par le générateur
puis ferme l’itérateur. La valeur initiale est sérialisée et réutilisée lors de l’hydratation.
Sur le client, la query restera connectée tant qu’elle sera activement utilisée dans un composant.
Plusieurs instances partagent une même connexion. Lorsqu’il n’y a plus d’utilisations actives, le
flux se déconnecte et l’itération côté serveur s’arrête.
Les queries temps réel (live queries) exposent une propriété connected et une méthode
reconnect() :
Si la connexion s’arrête, connected devient false. SvelteKit essaiera de se reconnecter
passivement, avec un délai exponentiel, et activement si navigator.onLine passe de false à
true.
À la différence de query, les live queries ne possèdent pas de méthode .refresh(), car elles se
mettent à jour toutes seules.
Si vous avez besoin d’un accès direct et impératif au flux sous-jacent de valeurs (plutôt qu’à la
propriété réactive current), les instances de query live sont elles-mêmes itérables de manière
asynchrone.
Vous pouvez utilisez for await directement sur l’instance :
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');// Prints: hello world, to stdoutconsole.log('hello %s','world');// Prints: hello world, to stdoutconsole.error(newError('Whoops, something bad happened'));// Prints error message and stack trace to stderr:// Error: Whoops, something bad happened// at [eval]:5:15// at Script.runInThisContext (node:vm:132:18)// at Object.runInThisContext (node:vm:309:38)// at node:internal/process/execution:77:19// at [eval]-wrapper:6:22// at evalScript (node:internal/process/execution:76:60)// at node:internal/main/eval_string:23:3constname='Will Robinson';console.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();consterr=getStreamSomehow();constmyConsole=newconsole.Console(out,err);myConsole.log('hello world');// Prints: hello world, to outmyConsole.log('hello %s','world');// Prints: hello world, to outmyConsole.error(newError('Whoops, something bad happened'));// Prints: [Error: Whoops, something bad happened], to errconstname='Will Robinson';myConsole.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
constcount=5;console.log('count: %d',count);// Prints: count: 5, to stdoutconsole.log('count:',count);// Prints: count: 5, to stdout
Plusieurs consommateurs de la même query live (qu’il soit réactif — via await ou current — ou
que ce soit une boucle for await impérative) partagent une même connexion sous-jacente unique. La
première valeur fournie à un itérateur for await est la valeur la plus récemment reçue, si une
telle valeur est disponible, correspondant à la sémantique d’attendre une ressource directement. Les
envois suivants sont déclenchés lorsqu’une nouvelle valeur arrive du serveur. Si des valeurs
arrivent plus vite que le consommateur ne draine l’itérateur, seule la valeur en attente la plus
récente est gardée — les flux live ne sont pas des logs d’évènements.
De la même manière, sur le serveur, for await partage une itération requête par requête du
générator sous-jacent, de sorte que les consommateurs concurrents au sein de la même requête
n’exécutent pas le générateur défini par l’utilisateur ou utilisatrice plusieurs fois.
Il est essentiel que vous ne cachiez pas les réponses des live queries dans un service
worker, puisque la réponse clonée continuerait à être streamée bien après la fermeture de la page.
Assurez-vous que votre logique de cache exclue toute réponse avec une en-tête Cache-Control
incluant no-store.
form
La fonction form facilite l’écriture de données sur le serveur. Elle prend un callback qui reçoit
l’objet data construit à partir du
FormData soumis...
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.
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
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
())}),async({title(parameter)title:string,content(parameter)content:string})=>{// Vérifie si l'utilisateur est loggué
constuserconstuser:auth.User|null=awaitauth(alias)module"$lib/server/auth"importauth.getUserfunctiongetUser():Promise<auth.User|null>
Récupère les informations de l'utilisateur à partir des cookies, en utilisant getRequestEvent
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.
Passes a string and
{@linkcode
replaceValue
}
to the [Symbol.replace] method on
{@linkcode
searchValue
}
. This method is expected to implement its own replacement algorithm.
searchValue
An object that supports searching for and replacing matches within a string.
replaceValue
The replacement text.
(/ /g,'-');// Insère dans la base de donnéesawaitdb(alias)module"$lib/server/database"importdb.sqlfunctionsql(strings:TemplateStringsArray,...values:any[]):Promise<any[]>`
INSERT INTO post (slug, title, content)
VALUES (${slugconstslug:string},${title(parameter)title:string},${content(parameter)content:string})
`;// Redirige vers la page nouvellement crééeredirect(alias)redirect(status:300|301|302|303|304|305|306|307|308|({}&number),location:string|URL):neverimportredirect
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
... 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}><!-- le contenu du formulaire est défini ici --><button>Publier !</button></form>
<scriptlang="ts">import{createPost}from'../data.remote';</script><h1>Créer un nouvel article</h1><form{...createPost}><!-- le contenu du formulaire est défini ici --><button>Publier !</button></form>
L’objet de formulaire contient les propriétés method et action qui lui permettent de fonctionner
sans JavaScriptt (c-à-d il soumet les données et recharge la page). Il possède également un
attachement qui améliore progressivement le formulaire lorsque JavaScript
est disponible, lui permettant de soumettre les données sans recharger entièrement la page.
Comme avec query, si le callback utilise les data soumises, celles-ci doivent être
validées en passant un Standard Schema en
tant que premier argument au form.
Champs
Un formulaire est composé d’un ensemble de champs, qui sont définis par le schéma. Dans le cas
d’un createPost, nous avons deux champs, title et content, qui sont tous deux des chaînes de
caractères. Pour récupérer les attributs d’un champ, exécutez sa méthode as(...), en précisant
quel type
d’input utiliser. Pour la
plupart des types d’inputs, vous pouvez également passer un deuxième argument — .as(type, value) —
pour contrôler la valeur renvoyée :
Ces attributs permettent à SvelteKit de définit le type correct d’input, de définir un name
utilisé pour construire les data passées au gestionnaire, de remplir la value du formulaire (par
exemple à la suite de l’échec d’une soumission, permettant de sauvegarder l’utilisateur ou
l’utilisatrice sans avoir besoin de tout ré-écrire), et de définir l’état
aria-invalid
Fournir un deuxième argument à .as(...) est utile lorsque vous créez un formulaire à partir de
données existantes, comme un formulaire d’édition, ou plusieurs instances du formulaire avec
for(...). De même qu’il définit la valeur de l’élément
lorsque celui-ci est rendu, il contrôle également la valeur de l’élément lorsque le formulaire est
réinitialisé. Les inputs radio, submit, et hidden ont
toujours besoin de cette valeur, et les inputs checkbox en ont besoin lorsqu’ils représentent une
option parmi d’autres. Les inputs file ne peuvent pas être remplis de cette manière.
L’attribut généré name utilise la notation d’objet JS (par ex. nested.array[0].value).
Les clés exprimés en chaînes de caractères qui nécessitent des guillemets tels que
object['nested-array'][0].value ne sont pas supportées. Sous le capot, les noms de champs de
type nombre et de type booléen (pour les checkbox) sont préfixés par b: et n:, respectivement,
pour signaler à SvelteKit qu’il doit transformer les valeurs depuis des chaînes de caractères
avant leur validation.
Les champs peuvent être imbriqués dans des objets et des tableaux, et leurs valeurs peuvent être des
chaînes de caractères, des nombres, des booléens ou des objets File. Par exemple, si votre schéma
ressemble à ceci...
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
... votre formulaire pourrait avoir cette forme là :
<script>import{createProfile}from'./data.remote';const{name,photo,info,attributes}=createProfile.fields;</script><form{...createProfile}enctype="multipart/form-data"><label><input{...name.as('text')}/> Nom</label><label><input{...photo.as('file')}/> Photo</label><label><input{...info.height.as('number')}/> Hauteur (cm)</label><label><input{...info.likesDogs.as('checkbox')}/> J'aime les chiens</label><h2>Mes meilleures compétences</h2><input{...attributes[0].as('text')}/><input{...attributes[1].as('text')}/><input{...attributes[2].as('text')}/><button>envoyer</button></form>
Puisque notre formulaire contient un input file, nous avons ajouté un attribut
enctype="multipart/form-data". Les valeurs de info.height et info.likesDogs sont transformées
en nombre et booléen respectivement.
Si un input checkbox est non coché, sa valeur n’est pas incluses dans l’objet
FormData à partir duquelSvelteKit
construit les données. Ainsi, nous devons rendre la valeur optionnelle dans votre schéma. Avec
Valibot cela signifie l’utilisation de v.optional(v.boolean(), false) plutôt que juste
v.boolean(), tandis qu’avec Zod, cela signifie l’utilisation de z.coerce.boolean<boolean>().
Dans le cas d’inputs radio et checkbox qui appartiennent tous au même champ, la value doit
être précisée en tant que second argument de .as(...) :
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
<form{...survey}><h2>Quel système d'exploitation utilisez-vous ?</h2>{#eachoperatingSystemsasos}<label><input{...survey.fields.operatingSystem.as('radio',os)}>{os}</label>{/each}<h2>Dans quels langages écrivez-vous du code ?</h2>{#eachlanguagesaslanguage}<label><input{...survey.fields.languages.as('checkbox',language)}>{language}</label>{/each}<button>envoyer</button></form>
De manière alternative, vous pouvez également utiliser select et select multiple :
<form{...survey}><h2>Quel système d'exploitation utilisez-vous ?</h2><select{...survey.fields.operatingSystem.as('select')}>{#eachoperatingSystemsasos}<option>{os}</option>{/each}</select><h2>Dans quels langages écrivez-vous du code ?</h2><select{...survey.fields.languages.as('select multiple')}>{#eachlanguagesaslanguage}<option>{language}</option>{/each}</select><button>envoyer</button></form>
Comme pour les inputs checkbox non cochés, si aucune sélection n’est faite, alors les
données seront undefined. Pour cette raison, le champ languages utilise
v.optional(v.array(...), []), plutôt que simplement v.array(...).
Validation programmatique
En plus de la validation de schéma déclarative, vous pouvez programmatiquement marquer des champs
comme invalides au sein du gestionnaire de formulaire en utilisant la fonction invalid importé
depuis @sveltejs/kit. Cela sert dans les cas où vous ne pouvez savoir si quelque chose est valide
avant d’avoir effectué certaines actions.
Elle jette une exception tout comme redirect ou error
Elle accepte plusieurs arguments qui peuvent être des chaînes de caractères (pour les problèmes
concernant le formulaire en tant que tel – ces problèmes n’appaîtront que dans fields.allIssues())
ou des erreurs respectant le format du schéma standard (pour les erreurs relatives à des champs
spécifiques). Utilisez le paramètre issue pour créer de telles erreurs de manière typée :
Use this to throw a validation error to imperatively fail form validation.
Can be used in combination with issue passed to form actions to create field-specific issues.
import{invalid}from'@sveltejs/kit';import{form}from'$app/server';import{tryLogin}from'$lib/server/auth';import*asvfrom'valibot';exportconstlogin=form(v.object({name:v.string(),_password:v.string()}),async({name,_password})=>{constsuccess=tryLogin(name,_password);if(!success){invalid('Incorrect username or password');}// ...});
reference}from'$app/server';import*asdb(alias)module"$lib/server/database"importdbfrom'$lib/server/database';exportconstbuyHotcakesconstbuyHotcakes:RemoteForm<{qty:number;},void>=form(alias)form<v.ObjectSchema<{readonlyqty:v.SchemaWithPipe<readonly[v.NumberSchema<undefined>,v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">]>;},undefined>,void>(validate:v.ObjectSchema<{readonlyqty:v.SchemaWithPipe<readonly[v.NumberSchema<undefined>,v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">]>;},undefined>,fn:(data:{qty:number;},issue:{qty:(message:string)=>StandardSchemaV1<Input=unknown,Output=Input>.Issue;}&((message:string)=>StandardSchemaV1<Input=unknown,Output=Input>.Issue))=>MaybePromise<...>):RemoteForm<...>(+2overloads)importform
Creates a form object that can be spread onto a <form> element.
reference(vimportv.object(alias)object<{readonlyqty:v.SchemaWithPipe<readonly[v.NumberSchema<undefined>,v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">]>;}>(entries:{readonlyqty:v.SchemaWithPipe<readonly[v.NumberSchema<undefined>,v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">]>;}):v.ObjectSchema<{readonlyqty:v.SchemaWithPipe<readonly[v.NumberSchema<undefined>,v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">]>;},undefined>(+1overload)exportobject
Creates an object schema.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
entries
The entries schema.
An object schema.
({qty(property)qty:v.SchemaWithPipe<readonly[v.NumberSchema<undefined>,v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">]>:vimportv.pipe(alias)pipe<v.NumberSchema<undefined>,v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">>(schema:v.NumberSchema<undefined>,item1:v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">|v.PipeAction<number,number,v.MinValueIssue<number,1>>):v.SchemaWithPipe<readonly[v.NumberSchema<undefined>,v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">]>(+20overloads)exportpipe
Adds a pipeline to a schema, that can validate and transform its input.
(),vimportv.minValue(alias)minValue<number,1,"vous devez acheter au moins un gâteau">(requirement:1,message:"vous devez acheter au moins un gâteau"):v.MinValueAction<number,1,"vous devez acheter au moins un gâteau">(+1overload)exportminValue
Creates a min value validation action.
requirement
The minimum value.
message
The error message.
A min value action.
(1,'vous devez acheter au moins un gâteau'))}),async(data(parameter)data:{qty:number;},issue(parameter)issue:{qty:(message:string)=>StandardSchemaV1.Issue;}&((message:string)=>StandardSchemaV1.Issue))=>{try{awaitdb(alias)module"$lib/server/database"importdb.buyfunctionbuy(qty:number):Promise<void>(data(parameter)data:{qty:number;}.qty(property)qty:number);}catch(e(localvar)e:unknown){if(e(localvar)e:unknown.codeany==='OUT_OF_STOCK'){invalid(alias)invalid(...issues:(StandardSchemaV1<Input=unknown,Output=Input>.Issue|string)[]):neverimportinvalid
Use this to throw a validation error to imperatively fail form validation.
Can be used in combination with issue passed to form actions to create field-specific issues.
import{invalid}from'@sveltejs/kit';import{form}from'$app/server';import{tryLogin}from'$lib/server/auth';import*asvfrom'valibot';exportconstlogin=form(v.object({name:v.string(),_password:v.string()}),async({name,_password})=>{constsuccess=tryLogin(name,_password);if(!success){invalid('Incorrect username or password');}// ...});
2.47.3
reference(issue(parameter)issue:{qty:(message:string)=>StandardSchemaV1.Issue;}&((message:string)=>StandardSchemaV1.Issue).qty(property)qty:(message:string)=>StandardSchemaV1<Input=unknown,Output=Input>.Issue(`nous n'avons pas assez de gâteaux`));}}});
Validation
Si les données soumises ne correspondent pas au schéma, le callback ne sera pas exécuté. À la place,
chaque méthode issues() d’un champ invalide renverra un tableau d’objets { message: string }, et
l’attribut aria-invalid (renvoyé depuis as(...)) sera défini comme true :
Si le title est valide, ou n’a pas encore été validé, createPost.fields.title.issues() renverra
undefined.
Vous n’avez pas besoin d’attendre la fin de la soumission du formulaire pour valider les données —
vous pouvez appeler validate() programmatiquement, par exemple dans un callback oninput (qui va
valider les données à chaque touche du clavier) ou dans un callback onchange :
Par défaut, les problèmes sont ignorés s’ils concernent des contrôles de formulaires avec lesquels
il n’y a pas encore eu d’interaction. Pour valider tous les inputs, appelez validate({ includeUntouched: true }).
Pour les validations côté client, vous pouvez préciser un schéma preflight qui va remplir les
issues() et empêcher que les données soient envoyées au serveur si celles-ci ne sont pas validées
:
<script>import*asvfrom'valibot';import{createPost}from'../data.remote';constschema=v.object({title:v.pipe(v.string(),v.nonEmpty()),content:v.pipe(v.string(),v.nonEmpty())});</script><h1>Créer un nouvel article</h1><form{...createPost.preflight(schema)}><!-- --></form>
Le schéma preflight peut être le même objet que votre schéma utilisé sur le serveur, si
cela est pertinent, même s’il ne sera pas capable de faire des validations devant être faites côté
serveur comme “cette valeur existe déjà dans la base de données” par exemple. Notez que vous ne
pouvez pas exporter un schéma depuis un fichier .remote.ts ou .remote.js, le schéma doit alors
être exporté depuis un module partagé, ou depuis le block <script module> du composant contenant
le <form>.
Pour obtenir une liste de tous les problèmes, plutôt qu’uniquement ceux appartenant à un champ
particulier, vous pouvez utiliser la méthode fields.allIssues() :
Comme avec les champs individuels, createPost.fields.allIssues() renverra undefined si le
formulaire est entièrement valide (ou n’a pas encore été validé).
Récupérer/définir des inputs
Chaque champ a une méthode value() qui reflète sa valeur actuelle. Lorsque quelqu’un interagit
avec le formulaire, la valeur est automatiquement mise à jour :
De plus, createPost.fields renvoie un objet { title, content }.
La value() d’un champ ne reflète pas les valeurs par défaut fournies en second argument à as
(comme dans fields.title.as('text', '...')) avant que le champ n’ait été modifié ou soumis. Vous
pouvez mettre à jour programmatiquement un champ (ou une collection de champs) via la méthode
set(...) :
<script>import{createPost}from'../data.remote';// ceci...createPost.fields.set({title:'Mon nouvel article de blog',content:'Lorem ipsum dolor sit amet...'});// ...est équivalent à ceci :createPost.fields.title.set('Mon nouvel article de blog');createPost.fields.content.set('Lorem ipsum dolor sit amet');</script>
Gérer les données sensibles
Dans le cas d’une soumission de formulaire non-améliorée-progressivement (c-à-d lorsque JavaScript
n’est pas disponible, quelqu’en soit la raison), value() est également rempli si les données
soumises sont invalides, afin que l’utilisateur ou l’utilisatrice n’ait pas besoin de re-remplir
entièrement le formulaire.
Vous pouvez empêcher que des données sensibles (comme des mots de passe ou des numéros de carte
bancaire) soient renvoyés au client en utilisant un nom commençant par un tiret-bas :
<form{...register}><label>Nom<input{...register.fields.username.as('text')}/></label><label>Mot de passe<input{...register.fields._password.as('password')}/></label><button>S'inscrire !</button></form>
Dans cet exemple, si les données ne sont pas validées, seul le premier <input> sera rempli lorsque
la page sera rechargée.
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 :
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
<script>import{createPost}from'../data.remote';</script><h1>Créer un nouvel article</h1><form{...createPost}><!-- --></form>{#ifcreatePost.result?.success}<p>Publié avec succès !</p>{/if}
<scriptlang="ts">import{createPost}from'../data.remote';</script><h1>Créer un nouvel article</h1><form{...createPost}><!-- --></form>{#ifcreatePost.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 :
Lorsque vous utilisez enhance, le <form> n’est pas automatiquement réinitialisé — vous devez
appeler form.element.reset() si vous souhaitez nettoyer les inputs.
Le callback reçoit une copie de l’instance de formulaire. Elle possède les mêmes propriétés et
méthodes à l’exception de enhance, et form.submit() effectue la soumission directement sans
ré-exécuter le callback enhance. Au sein du callback, form.element est toujours définie.
Instances multiples de formulaire
Certains formulaires peuvent être répétés au sein d’une liste. Dans ce cas, vous pouvez créer des
instances séparées d’une fonction de formulaire via for(id) pour les garder en isolation.
Lorsque chaque instance devrait afficher des valeurs différentes, passez-les en tant que deuxième
argument à .as(...) :
src/routes/todos/+page
<script>import{getTodos,modifyTodo}from'../data.remote';</script><h1>À faire</h1>{#eachawaitgetTodos()astodo}{@constmodify=modifyTodo.for(todo.id)}<form{...modify}><input{...modify.fields.description.as('text',todo.description)}/><buttondisabled={!!modify.pending}>enregistrer les changements</button></form>{/each}
<scriptlang="ts">import{getTodos,modifyTodo}from'../data.remote';</script><h1>À faire</h1>{#eachawaitgetTodos()astodo}{@constmodify=modifyTodo.for(todo.id)}<form{...modify}><input{...modify.fields.description.as('text',todo.description)}/><buttondisabled={!!modify.pending}>enregistrer les changements</button></form>{/each}
Plusieurs boutons de soumission
Un <form> peut avoir plusieurs boutons de soumission. Par exemple, vous pourriez avoir un unique
formulaire vous permettant de vous connecter ou de vous inscrire selon le bouton sur lequel vous
cliquez.
Pour faire cela, ajoutez un champ à votre schéma correspondant à la valeur du bouton, et utilisez
as('submit', value) pour le lier :
src/routes/login/+page
<script>import{loginOrRegister}from'$lib/auth';</script><form{...loginOrRegister}><label>Votre nom d'utilisateur<input{...loginOrRegister.fields.username.as('text')}/></label><label>Votre mot de passeYour password<input{...loginOrRegister.fields._password.as('password')}/></label><button{...loginOrRegister.fields.action.as('submit','login')}>se connecter</button><button{...loginOrRegister.fields.action.as('submit','register')}>s'inscrire</button></form>
<scriptlang="ts">import{loginOrRegister}from'$lib/auth';</script><form{...loginOrRegister}><label>Votre nom d'utilisateur<input{...loginOrRegister.fields.username.as('text')}/></label><label>Votre mot de passeYour password<input{...loginOrRegister.fields._password.as('password')}/></label><button{...loginOrRegister.fields.action.as('submit','login')}>se connecter</button><button{...loginOrRegister.fields.action.as('submit','register')}>s'inscrire</button></form>
Dans votre gestionnaire de formulaire, vous pouvez vérifier quel bouton a été cliqué :
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
(['login','register'])}),async({username(parameter)username:string,_password(parameter)_password:string,action(parameter)action:"login"|"register"})=>{if(action(parameter)action:"login"|"register"==='login'){ // gestion de la connexion
}else{ // gestion de l'inscription
}});
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 et form, si la fonction accepte un argument, celui-ci devrait être
validé en fournissant un Standard Schema en
tant que premier argument à command.
(),async(id(parameter)id:string)=>{const[rowconstrow:any]=awaitdb(alias)module"$lib/server/database"importdb.sqlfunctionsql(strings:TemplateStringsArray,...values:any[]):Promise<any[]>`
SELECT likes
FROM item
WHERE id =${id(parameter)id:string}`;returnrowconstrow:any.likesany;});exportconstaddLikeconstaddLike:RemoteCommand<string,void>=command(alias)command<v.StringSchema<undefined>,void>(validate:v.StringSchema<undefined>,fn:(arg:string)=>MaybePromise<void>):RemoteCommand<string,void>(+2overloads)importcommand
Creates a remote command. When called from the browser, the function will be invoked on the server via a fetch call.
(),async(id(parameter)id:string)=>{awaitdb(alias)module"$lib/server/database"importdb.sqlfunctionsql(strings:TemplateStringsArray,...values:any[]):Promise<any[]>`
UPDATE item
SET likes = likes + 1
WHERE id =${id(parameter)id:string}`;});
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><buttononclick={async()=>{try{awaitaddLike(item.id);}catch(error){showToast('Quelque chose s\'est mal passé !');}}}>ajouter un like</button><p>likes : {awaitgetLikes(item.id)}</p>
<scriptlang="ts">import{getLikes,addLike}from'./likes.remote';import{showToast}from'$lib/toast';let{item}=$props();</script><buttononclick={async()=>{try{awaitaddLike(item.id);}catch(error){showToast('Quelque chose s\'est mal passé !');}}}>ajouter un like</button><p>likes : {awaitgetLikes(item.id)}</p>
Les commandes ne peuvent pas être appelées lors du rendu.
Mutations single-flight
L’objectif des fonctions distantes form et command est de muter des
données. Dans plusieurs cas, la mutation de données invalide d’autres données. Par défaut, form
gère ceci en invalidant automatiquement toutes les queries et fonctions load à la suite d’une
soumission de formulaire réussie, pour simuler ce qu’il se passerait avec le rechargement classique
de la page. command, de son côté, ne fait rien de particulier. Typiquement, aucune de ces options
n’est idéale — tout invalider est probablement surdimensionné, car il est peu probable qu’une
soumission de formulaire ait changé tout ce qui est visible sur votre page. Pour ce qui est de
command, ne rien faire n’est pas beaucoup plus utile, car cela laisse des données périmées à
l’écran. Dans les deux cas, il est courant d’avoir à effectuer deux aller-retours vers le serveur :
un pour réaliser la mutation, et un autre dans la foulée permettant de re-requêter les données des
queries qui ont besoin d’être mises à jour.
SvelteKit solutionne ces deux problèmes avec les mutations single-flight : votre soumission de
form ou votre invocation de command peuvent rafraîchir des queries et fournir leurs résultats en
retour au client au cours d’une seule requête.
Mises à jour pilotées depuis le serveur
Dans la plupart des situations, le gestionnaire sur le serveur sait quelles sont les données du
client ayant besoin d’être mises à jour, en fonction de ses arguments :
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
entries
The entries schema.
An object schema.
({/* ... */}),async(data(parameter)data:{})=>{ // la logique de formulaire se trouve ici...
// Met à jour `getPosts()` sur le serveur, et renvoie// les données avec le résultats de `createPost`// La promise de `refresh` peut ne pas être gérée// car le framework l'attend pour nous avant de servir la réponsevoidgetPostsconstgetPosts:(arg:void)=>RemoteQuery<void>().refresh(method)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.
();// Redirige vers la page nouvellement crééeredirect(alias)redirect(status:300|301|302|303|304|305|306|307|308|({}&number),location:string|URL):neverimportredirect
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
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
()}),async(post(parameter)post:{id:string;})=>{ // la logique de formulaire se trouve ici...
constresultconstresult:any=externalApiconstexternalApi:any
{any}
.updateany(post(parameter)post:{id:string;});// L'API nous donne déjà l'article mis à jour,// il n'y a pas besoin de le mettre à jour,// nous pouvons le définir directementgetPostconstgetPost:(arg:string)=>RemoteQuery<void>(post(parameter)post:{id:string;}.id(property)id:string).set(method)set(value:void):void
On the client, this function will update the value of the query without re-fetching it.
On the server, this can be called in the context of a command or form and the specified 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.
(resultconstresult:any);});
Puisque les queries sont associées à des clés en fonction de leurs arguments,
getPost(post.id).set(result) sur le serveur sait retrouver le getPost(id) correspondant sur le
client pour le mettre à jour. C’est le même fonctionnement pour getPosts().refresh() — il sait
retrouver getPosts() sans argument sur le client.
Reconnecter les queries live dans les mutations
Les mutations “single-flight” peuvent également se reconnecter aux instances de query.live. Dans
un gestionnaire form / command, appelez .reconnect() sur la ressource de la query live à
laquelle vous souhaitez vous reconnecter :
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
()}),async({userId(parameter)userId:string})=>{ // logique de mutation...
getNotificationsconstgetNotifications:(arg:string)=>RemoteLiveQuery<any>(userId(parameter)userId:string).reconnect(method)reconnect():Promise<void>
Reconnects the live stream immediately.
();});
Ceci prévoit une reconnection des instances client actives correspondantes et les applique en tant
que partie de la réponse à la mutation (c-à-d dans le même trajet que le résultat du formulaire/de
la commande). Vous pourriez avoir besoin de ceci si, par exemple, la commande modifie un cookie que
la query live a besoin de réinitialiser pour le capturer.
Mises à jour pilotées par le client
Malheureusement, la vie n’est pas toujours aussi simple que l’exemple précédent. Le serveur connaît
toujours quelles fonctions de type “query” mettre à jour, mais il ne sait pas quelles instances
spécifiques mettre à jour. Par exemple, si getPosts({ filter: 'author:santa' }) est affiché sur le
client, appeler getPosts().refresh() sur le serveur ne la mettra pas à jour. Vous devrez plutôt
appeler getPosts({ filter: 'author:santa' }).refresh() — mais comment pourriez-vous savoir quelles
combinaisons spécifiques de filtres sont actuellement affichées sur le client, en particulier si
votre argument de query est plus compliqué qu’un objet avec une seule clé ?
SvelteKit résout ce problème en permettant au client de requêter des mises-à-jour de données
spécifiques au serveur en utilisant submit().updates (pour form) ou myCommand().updates (pour
command) :
awaitsubmitfunctionsubmit():Promise<any>&{updates(...updates:RemoteQueryUpdate[]):Promise<any>;}().updates(method)updates(...updates:RemoteQueryUpdate[]):Promise<any>(// pour requêter toutes les instances actives de getPostsgetPostsfunctiongetPosts(args:{filter:string;}):RemoteQuery<Post[]>,// pour requêter une instance spécifiquegetPostsfunctiongetPosts(args:{filter:string;}):RemoteQuery<Post[]>({filter(property)filter:string:'author:santa'}),// pour requêter une instance spécifique avec une surcharge optimistegetPostsfunctiongetPosts(args:{filter:string;}):RemoteQuery<Post[]>({filter(property)filter:string:'author:santa'}).withOverride(method)withOverride(update:(current:Post[])=>Post[]):RemoteQueryOverride
Temporarily override a query's value during a single-flight mutation to provide optimistic updates.
Inside a remote command or form callback, returns an iterable
of { arg, query } entries for the query instances the client asked to refresh, up to
the supplied limit. Each query is a RemoteQuery bound to the original
client-side cache key, so refresh() / set() propagate correctly even when
the query's schema transforms the input. arg is the validated argument,
i.e. the value after the schema has run (so InferOutput<Schema> for queries
declared with a Standard Schema).
Arguments that fail validation or exceed limit are recorded as failures in
the response to the client.
See Client-requested refreshes
for usage in a remote command or form.
import{requested}from'$app/server';for(const{arg,query}ofrequested(getPost,5)){// `arg` is the validated argument; `query` is bound to the client's// cache key. It's safe to throw away this promise -- SvelteKit will// await it and forward any errors to the client.voidquery.refresh();}
As a shorthand for the above, you can also call refreshAll on the result:
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
Hint: This schema removes unknown entries. The output will only include the
entries you specify. To include unknown entries, use looseObject. To
return an issue for unknown entries, use strictObject. To include and
validate unknown entries, use objectWithRest.
entries
The entries schema.
An object schema.
({/* ... */}),async(data(parameter)data:{})=>{ // la logique de formulaire est ici...
for(const{queryconstquery:RemoteQuery<void>reference}ofrequested(alias)requested<{filter:string;},void,{filter:string;}>(query:RemoteQueryFunction<{filter:string;},void,{filter:string;}>,limit:number):QueryRequestedResult<{filter:string;},void>(+1overload)importrequested
Inside a remote command or form callback, returns an iterable
of { arg, query } entries for the query instances the client asked to refresh, up to
the supplied limit. Each query is a RemoteQuery bound to the original
client-side cache key, so refresh() / set() propagate correctly even when
the query's schema transforms the input. arg is the validated argument,
i.e. the value after the schema has run (so InferOutput<Schema> for queries
declared with a Standard Schema).
Arguments that fail validation or exceed limit are recorded as failures in
the response to the client.
See Client-requested refreshes
for usage in a remote command or form.
import{requested}from'$app/server';for(const{arg,query}ofrequested(getPost,5)){// `arg` is the validated argument; `query` is bound to the client's// cache key. It's safe to throw away this promise -- SvelteKit will// await it and forward any errors to the client.voidquery.refresh();}
As a shorthand for the above, you can also call refreshAll on the result:
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.
();}// Redirige vers la page nouvellement crééeredirect(alias)redirect(status:300|301|302|303|304|305|306|307|308|({}&number),location:string|URL):neverimportredirect
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
{Redirect} This error instructs SvelteKit to redirect to the specified location.
{Error} If the provided status is invalid or the location cannot be used as a header value.
(303,`/blog/${slugconstslug:""}`);});
requested vous donne accès aux queries dont le client a demandé la mise à jour. Chaque entrée est
un objet { arg, query } : arg est la valeur que la fonction d’implémentation de la query a reçu
— c-à-d l’argument après qu’il a été validé et (lorsqu’applicable) transformé par le schéma — et
query est une RemoteQuery déjà liée à la clé de cache originelle du client, de sorte que appeler
query.refresh(...) / query.set(...) mette à jour l’instance correcte sur le client. Si le
parsing d’un argument échoue, cette query renverra une erreur, mais la commande entière n’échouera
pas. Le deuxième paramètre de requested, limit, est le nombre d’éléments maximum qu’elle
renverra. Toute requête de mise à jour au-delà de cette limite échouera.
limit est requis car la liste des requêtes de mise à jour est contrôllée par le client —
chaque entrée amène le serveur à valider un argument et généralement à re-requêter des données, de
sorte qu’une liste non liée est une risque de Déni-de-service. Choisissez une limite qui reflète le
cas le pire que vous souhaitez gérer par requête. Vous pouvez fournir Infinity si vous avez
décidé explicitement d’accepter n’importe quel nombre de mises à jour, mais ceci n’est pas
recommandé.
De plus, requested permet un raccourci lorsque vous voulez uniquement mettre à jour les instances
de la query demandée :
// ceci est équivalent à boucler sur son résultat puis appeler `void query(arg).refresh()`.awaitrequested(alias)requested<any,any,any>(query:RemoteQueryFunction<any,any,any>,limit:number):QueryRequestedResult<any,any>(+1overload)importrequested
Inside a remote command or form callback, returns an iterable
of { arg, query } entries for the query instances the client asked to refresh, up to
the supplied limit. Each query is a RemoteQuery bound to the original
client-side cache key, so refresh() / set() propagate correctly even when
the query's schema transforms the input. arg is the validated argument,
i.e. the value after the schema has run (so InferOutput<Schema> for queries
declared with a Standard Schema).
Arguments that fail validation or exceed limit are recorded as failures in
the response to the client.
See Client-requested refreshes
for usage in a remote command or form.
import{requested}from'$app/server';for(const{arg,query}ofrequested(getPost,5)){// `arg` is the validated argument; `query` is bound to the client's// cache key. It's safe to throw away this promise -- SvelteKit will// await it and forward any errors to the client.voidquery.refresh();}
As a shorthand for the above, you can also call refreshAll on the result:
Pourquoi la commande doit nommer chaque query qu’elle souhaite mettre à jour ? Il y a deux
raisons :
La taille de bundle. Si une commande pouvait implicitement rafraîchir n’importe quelle
query de cette application, SvelteKit devrait inclure le code de chaque query dans le bundle
serveur de la commande, car il ne peut pas savoir à l’avance lesquelles seront exécutées.
Déni-de-service. Tout utilisateur ou utilisatrice malveillante peut inspecter leur onglet
réseau pour découvrir quelles sont les requêtes utilisées par votre application, puis envoyer
une commande POST avec une liste fournie par le client contenant des milliers de mises-à-jour.
La seule défense possible est la déclaration par le gestionnaire du serveur des queries qu’il
souhaite mettre à jour — et dans quelle quantités (d’où la limit requise).
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.
reference(async()=>{constpostsconstposts:any[]=awaitdb(alias)module"$lib/server/database"importdb.sqlfunctionsql(strings:TemplateStringsArray,...values:any[]):Promise<any[]>`
SELECT title, slug
FROM post
ORDER BY published_at
DESC
`;returnpostsconstposts:any[];});
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.
Arguments de prerender
Comme avec les queries, les fonctions prerender peuvent accepter un argument, qui devrait être
validé avec un Standard Schema :
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.
(),async(slug(parameter)slug:string)=>{const[postconstpost:any]=awaitdb(alias)module"$lib/server/database"importdb.sqlfunctionsql(strings:TemplateStringsArray,...values:any[]):Promise<any[]>`
SELECT * FROM post
WHERE slug =${slug(parameter)slug:string}`;if(!postconstpost:any)error(alias)error(status:number,body?:{message:string;}extendsApp.Error?App.Error|string|undefined:never):never(+1overload)importerror
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.
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 :
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 :
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 }) :
reference('unchecked',async({id(parameter)id:string}:{id(property)id:string: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
});
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 :
reference(async()=>{constuserconstuser:User|null=awaitgetUserconstgetUser:(arg:void)=>RemoteQuery<User|null>();returnuserconstuser:User|null&&{name(property)name:string:userconstuser:User.name(property)User.name:string,avatar(property)avatar:string:userconstuser:User.avatar(property)User.avatar:string};});// cette query pourrait être appelée depuis plusieurs endroits,
// mais cette fonction ne sera exécutée qu'une fois par requêteconstgetUserconstgetUser:RemoteQueryFunction<void,User|null>=query(alias)query<User|null>(fn:()=>MaybePromise<User|null>):RemoteQueryFunction<void,User|null>(+2overloads)importquery
Creates a remote query. When called from the browser, the function will be invoked on the server via a fetch call.
Gets a cookie that was previously set with cookies.set, or from the request headers.
name
the name of the cookie
opts
the options, passed directly to cookie.parse. See documentation here
('session_id'));});
Notez que certaines propriétés de RequestEvent sont différentes au sein des fonctions distantes :
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)
route, params et url sont relatives à la page depuis laquelle la fonction distante a été
appelée, et non l’URL du endpoint que SvelteKit crée pour la fonction distante. Ne les utilisez
jamais pour déterminer si oui ou non un utilisateur ou utilisatrice est autorisée à accéder à
certaines données, puisque ces valeurs font partie de la requête et peuvent donc être manipulées.
Les queries ne sont de plus pas ré-exécutées lorsque l’utilisateur ou l’utilisatrice navigue (à
moins que l’argument de la query ait changé suite à une navigation), vous devriez donc être
conscient•e de la façon dont vous utilisez ces valeurs.
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.)