DocsCadres Fullstack
De SvelteKit à Nexy
Dans Nexy, un projet SvelteKit conserve ses pages et mises en page Svelte sous forme de fichiers .svelte simples, tandis que +server.ts, +page.server.ts se chargent, les actions de formulaire et les hooks se déplacent vers les fichiers Python à côté d'eux.
Chaque section ci-dessous montre une habitude SvelteKit, puis la même chose dans Nexy.
Choisissez d'abord le mode
| Votre application SvelteKit est | Mode Nexy | Les pages sont |
|---|---|---|
| une application avec navigation client | mode client, useCSR = True |
.svelte fichiers sous src/routes/ |
| principalement du contenu, quelques parties interactives | mode serveur, la valeur par défaut | .nexy pages avec Svelte îles |
from nexy.core.models import NexyConfigModelfrom nexy.frontend import svelte class NexyConfig(NexyConfigModel): useFF = svelte() useCSR = True useAliases = {"@": "src"}Les sections ci-dessous utilisent le mode client, sauf indication contraire.
+page.svelte
<h1>About</h1>Une page est le fichier lui-même, nommé d'après son URL :
<h1>About</h1>src/routes/+page.svelte est src/routes/index.svelte.
Itinéraires dynamiques et params
<script lang="ts"> import { page } from "$app/state";</script> <h1>{page.params.slug}</h1>Les segments atteignent la page en tant qu'accessoires :
<script lang="ts"> let { slug }: { slug: string } = $props();</script> <h1>{slug}</h1>Dans tout autre composant, le magasin params de @nexy/client les contient : $params.slug. Les dossiers [...rest] et (group) fonctionnent comme dans SvelteKit.
+layout.svelte
<script lang="ts"> let { children } = $props();</script> <header>My app</header><main>{@render children()}</main>Il n'y a pas de app.html : la racine layout.svelte est propriétaire du document.
<script lang="ts"> import type { Snippet } from "svelte"; let { children }: { children?: Snippet } = $props();</script> <html> <body> <header>My app</header> <main>{@render children?.()}</main> </body></html>+server.ts
import { json } from "@sveltejs/kit"; export function GET({ params }) { return json({ id: Number(params.id) });}Le même export GET, dans Python :
async def GET(id: int): return {"id": id}from pydantic import BaseModel, Field from nexy.http import JSONResponse class NewItem(BaseModel): name: str price: float = Field(gt=0) async def POST(item: NewItem): return JSONResponse(item.model_dump(), status_code=201)error()
import { error, json } from "@sveltejs/kit"; export function GET({ params, url }) { if (Number(params.id) > 100) error(404, "No such order"); return json({ id: Number(params.id), currency: url.searchParams.get("currency") ?? "EUR" });}from nexy.http import HTTPException async def GET(id: int, currency: str = "EUR"): if id > 100: raise HTTPException(status_code=404, detail="No such order") return {"id": id, "currency": currency}load dans +page.server.ts
export async function load() { return { users: await db.user.findMany() };}<script lang="ts"> let { data } = $props();</script> <ul> {#each data.users as user (user.id)} <li>{user.name}</li> {/each}</ul>En mode client, la page demande ses données à un gestionnaire :
async def GET(): return [{"id": 1, "name": "Ada"}, {"id": 2, "name": "Alan"}]<script lang="ts"> let users = $state<{ id: number; name: string }[]>([]); $effect(() => { fetch("/api/users").then((response) => response.json()).then((list) => (users = list)); });</script> <ul> {#each users as user (user.id)} <li>{user.name}</li> {/each}</ul>En mode serveur, l'élément principal d'une page .nexy est la fonction load, et elle s'exécute pendant que le serveur restitue :
users = [{"id": 1, "name": "Ada"}, {"id": 2, "name": "Alan"}]<ul> {% for user in users %} <li>{{ user.name }}</li> {% endfor %}</ul>Actions de formulaire et use:enhance
export const actions = { default: async ({ request }) => { const data = await request.formData(); await db.todo.create({ title: data.get("title") }); },};<script lang="ts"> import { enhance } from "$app/forms";</script> <form method="POST" use:enhance> <input name="title" /> <button>Add</button></form>Une action est une fonction Python avec un client généré et typé :
from pydantic import BaseModel from nexy.decorators import Action, revalidate class Todo(BaseModel): title: str done: bool = False @Actionasync def add_todo(title: str) -> Todo: revalidate("/todos") return Todo(title=title)<script lang="ts"> import { useAction } from "@nexy/client"; import { add_todo } from "@nexy/actions"; let title = $state(""); const save = useAction(add_todo, { onSuccess: () => (title = "") });</script> <form onsubmit={(e) => { e.preventDefault(); save.run({ title }); }}> <input bind:value={title} /> <button disabled={$save.pending}>Add</button></form>revalidate("/todos") joue le rôle de invalidateAll() : la page se récupère à nouveau.
goto, liens et $app/state
<script lang="ts"> import { goto } from "$app/navigation"; import { page } from "$app/state";</script> <a href="/about">About</a><span>{page.url.pathname}</span><button onclick={() => goto("/dashboard")}>Dashboard</button><script lang="ts"> import Link from "@nexy/Link.svelte"; import { pathname, router } from "@nexy/client";</script> <Link href="/about" activeClass="active">About</Link><span>{$pathname}</span><button onclick={() => router.push("/dashboard")}>Dashboard</button>hooks.server.ts
export async function handle({ event, resolve }) { if (event.url.pathname.startsWith("/dashboard") && !event.cookies.get("session")) { return new Response(null, { status: 307, headers: { location: "/login" } }); } const response = await resolve(event); response.headers.set("x-app", "demo"); return response;}Le test de chemin est le dossier. Une racine middleware.py enveloppe tout, une dashboard/middleware.py uniquement les routes en dessous :
from nexy.http import Request async def middleware(request: Request, call_next): response = await call_next(request) response.headers["x-app"] = "demo" return responsefrom nexy.http import RedirectResponse, Request async def middleware(request: Request, call_next): if "session" not in request.cookies: return RedirectResponse("/login", status_code=307) return await call_next(request)async def GET(): return {"visits": 12}prerender et entries
export const prerender = true; export function entries() { return [{ slug: "hello" }, { slug: "world" }];}Exportez getStaticParams depuis la page pour un document pré-rendu par valeur :
<script lang="ts" module> export function getStaticParams() { return [{ slug: "hello" }, { slug: "world" }]; }</script> <script lang="ts"> let { slug }: { slug: string } = $props();</script> <h1>{slug}</h1>En mode serveur, revalidate = 60 dans le frontmatter d'une page .nexy le met en cache par URL :
revalidate = 60<h1>News</h1><svelte:head>
<svelte:head> <title>Blog</title></svelte:head>En mode client, écrivez les balises à la racine layout.svelte. En mode serveur, une page .nexy les définit, et sa mise en page les écrit avec {{ useMetadata().html() }} :
metadata = {"title": "Blog", "description": "Posts about Nexy"}<h1>Blog</h1>Svelte composants dans les pages du serveur
<script lang="ts"> let { start = 0 }: { start?: number } = $props(); let count = $state(start);</script> <button onclick={() => count++}>{count}</button>En mode serveur, le composant est un îlot d'une page .nexy :
from "@/components/Counter.svelte" import Counter<h1>Static heading</h1><Counter start=3 directive="client:visible" />+error.svelte
src/routes/notfound.svelte répond aux URL inconnues en mode client, notfound.nexy en mode serveur et les clients API obtiennent {"detail": ...}.
$env/static/private
import { DATABASE_URL } from "$env/static/private";import os DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///dev.db")Courir
| SvelteKit | Nexy |
|---|---|
npx sv create |
nx new, modèle web-fbr-svelte |
vite dev |
nx dev |
vite build avec un adaptateur, puis node build |
nx build, puis nx start |
static/ |
public/, servi à / |
src/lib/ et $lib |
src/components/ et @ |