DocsComprendre le modèle
Concepts de base
Les mots utilisés par le reste de la documentation - route, page, vue, composant, gestionnaire, point de terminaison - chacun sur une seule ligne, avec un lien vers la page qui va plus loin. Nouveau sur Nexy ? Commencez par Qu'est-ce que Nexy ?.
Comment lire ces documents
| Niveau | La question à laquelle il répond | Commencez par |
|---|---|---|
| Architecture des applications | quelle couche répond à une demande et ce qu'elle refuse de faire | Comment fonctionne Nexy |
| Structure de la candidature | où se trouve le code et ce que devient un fichier | Structure du projet |
| Présentation et rendu | ce que le lecteur voit et qui produit le HTML | Vues, Rendu |
| Interaction avec les applications | ce que fait le lecteur et ce qui s'exécute en réponse | Actions |
Les niveaux sont lus une fois dans cet ordre. Après cela, une page dont vous avez besoin est une recherche, pas un cours.
Architecture des applications
Trois façons de décider quel code répond à une URL. Ce sont des alternatives, pas des couches :
- Routage basé sur des fichiers (FBR) : un fichier sous
src/routes/est une route. La valeur par défaut. - Routeur modulaire : contrôleurs et modules sous
src/apps/, déclarés auprès des décorateurs, pour une base de code de style NestJS. - Routeur personnalisé : un FastAPI
APIRouterexistant inclus dans l'application.
Le choix est une ligne de la classe NexyConfig dans nexyconfig.py.
Le routage basé sur des fichiers ne nécessite aucune ligne : sans useRouter, Nexy lit src/routes/.
from nexy.core.models import NexyConfigModel class NexyConfig(NexyConfigModel): pass # file-based: no useRouter, the files of src/routes/ are the routesLe routeur modulaire est le module racine de src/apps/ :
from nexy.core.models import NexyConfigModel from src.apps.app_module import AppModule class NexyConfig(NexyConfigModel): useRouter = AppModule # modular: the root module of src/apps/Un routeur personnalisé est un APIRouter que vous possédez déjà :
from nexy.core.models import NexyConfigModel from src.legacy import api # an APIRouter you already have class NexyConfig(NexyConfigModel): useRouter = api # custom: your router, included as it isChoisir l’un des trois est une décision. Le choix d'un framework frontend en est un autre, distinct : voir Choisir un routeur et Vues.
Structure de la candidature
Cinq notions qu'un lecteur venant de Next.js fusionne habituellement en une seule. Ce n'est pas la même chose, et la différence explique la plupart des questions « pourquoi ma page ne s'affiche-t-elle pas ».
| Notion | Qu'est-ce que c'est | Ce que ce n'est pas |
|---|---|---|
| Itinéraire | un modèle d'URL, produit par le chemin d'un fichier | un fichier : un fichier peut répondre à plusieurs URL ([slug]) |
| Page | un fichier de route qui restitue une vue : .nexy, .md, .mdx, .tsx, .vue, .svelte |
un endpoint : il répond à un document, pas à des données |
| Voir | ce qui produit le corps d'une page : Jinja2 et Python, Markdown, ou un composant client | un itinéraire : une même vue peut servir plusieurs pages |
| Composant | une pièce réutilisable qu'une page importe | une route : en mode serveur un composant n'est jamais servi seul |
| Gestionnaire de route | la fonction d'un fichier de route .py, nommé d'après la méthode HTTP (GET, POST, …) |
une page : elle renvoie du JSON, un fichier ou un Response |
| Point de terminaison | la paire (URL, méthode) à laquelle un gestionnaire répond | un gestionnaire : un fichier peut répondre à plusieurs points de terminaison |
En une phrase : une page est un fichier de route, une vue est ce qui le restitue, un composant est ce qu'une page réutilise, un gestionnaire répond à un point de terminaison.
Les cinq dans un petit projet :
from "@/components/Like.tsx" import Like slug: prop[str]<h1>{{ slug }}</h1><Like post="{{ slug }}" />Le frontmatter et le modèle en dessous sont la vue de la page ; Like est le composant qu'il réutilise.
async def GET(): # the endpoint GET /api/posts return {"posts": []} async def POST(): # the endpoint POST /api/posts return {"created": True}Avec le routeur modulaire, une route est le chemin d'un contrôleur, et un gestionnaire est l'une de ses méthodes, nommée d'après le verbe HTTP :
from nexy.decorators import Controller @Controller("/api/posts")class PostsController: async def get(self) -> dict: # the endpoint GET /api/posts return {"posts": []} async def post(self) -> dict: # the endpoint POST /api/posts return {"created": True}from nexy.decorators import Module from .posts_controller import PostsController @Module()class AppModule: controllers = [PostsController]Une page est toujours un fichier de src/routes/, ou une vue qu'un contrôleur renvoie avec useViews.
Avec un routeur personnalisé, une route est un chemin de votre APIRouter, et un gestionnaire est la fonction enregistrée par son décorateur :
from fastapi import APIRouter api = APIRouter() @api.get("/api/posts") # the endpoint GET /api/postsasync def list_posts(): return {"posts": []} @api.post("/api/posts") # the endpoint POST /api/postsasync def create_post(): return {"created": True}Les pages sont toujours des fichiers de src/routes/ : une page de .nexy répond à côté des itinéraires inclus.
Encore deux mots qui reviennent partout :
- Layout : un fichier
layout.*qui enveloppe les pages sous son dossier ; la racine est propriétaire du document HTML. Voir Mise en page. - Dépendance : une fonction qui s'exécute avant un gestionnaire, appliquée par route ou par dossier. Voir Dépendances.
Présentation et rendu
Deux décisions indépendantes, souvent confondues :
- Le format décide quel fichier restitue la page : Vues.
- Le mode décide qui produit le HTML et quand : Rendu.
| Notion | Une ligne |
|---|---|
| Mode serveur | le serveur restitue les pages .nexy et .md ; les composants sont des îlots (modes) |
| Mode client | useCSR = True : les fichiers framework sont les itinéraires et la navigation se fait dans le navigateur |
| SSR, ISR, CSR, îles | quatre modes de rendu, décidés par page sauf CSR, qui est par projet (rendu) |
| Hydratation | le navigateur attachant un framework au HTML que le serveur a déjà envoyé (îlots) |
| Accessoires | les valeurs qu'un parent transmet à un composant, déclarées name: prop[type] dans un fichier .nexy (fichiers .nexy) |
Une page en mode serveur avec un îlot : le serveur restitue la page, le navigateur hydrate le graphique uniquement lorsqu'il défile.
from "@/components/Chart.tsx" import Chart<h1>Sales</h1><Chart directive="client:visible" />L'île apparaît dans la vue et le contrôleur renvoie la vue :
from "@/components/Chart.tsx" import Chart<h1>Sales</h1><Chart directive="client:visible" />from nexy.decorators import Controllerfrom nexy.hooks import useViews @Controller("/sales")class SalesController: async def get(self): return useViews("src/apps/sales_view.nexy")Interaction avec les applications
| Notion | Une ligne |
|---|---|
| Action | une fonction Python marquée @Action, servant de POST, appelable depuis un formulaire ou comme fonction typée (actions) |
| Formulaire | un formulaire HTML qui publie dans une action, avec des erreurs de validation revenant sur la page (actions) |
| Navigation | des liens qui prélevent et des hooks qui lisent le chemin, la requête, les cookies et la session (hooks) |
| Récupération de données | effectué par la page ou le gestionnaire qui en a besoin ; Nexy ne fournit aucune couche de données client (gestionnaires) |
Une action et le formulaire qui y est publié, qui fonctionne avant le chargement de tout script :
from nexy.decorators import Action @Actionasync def subscribe(email: str) -> bool: return True<form method="post" action="{{ __actions_path['subscribe'] }}"> <input name="email" type="email"> <button type="submit">Subscribe</button></form><form method="post" action="{{ __actions_path['subscribe'] }}"> <input name="email" type="email"> <button type="submit">Subscribe</button></form>Ce que Nexy ne fait pas
L'authentification, l'autorisation, la base de données, l'internationalisation, les tâches en arrière-plan, le courrier électronique, les paiements et la recherche sont la responsabilité de votre application, et non celle du framework. Nexy donne les points d'extension et indique où brancher chacun d'eux : voir Ce que Nexy vous laisse.
L'authentification, par exemple, se connecte à une dépendance du dossier qu'elle protège. Nexy l'exécute avant chaque itinéraire ci-dessous ; qui est l'utilisateur reste votre décision :
from nexy import unauthorizedfrom nexy.http import Request def require_login(request: Request) -> None: if not request.session.get("user"): unauthorized(scheme="Bearer") dependencies = [require_login]L'authentification, par exemple, se connecte à un garde sur le contrôleur qu'elle protège. Nexy l'exécute avant chaque gestionnaire de la classe ; qui est l'utilisateur reste votre décision :
from nexy import unauthorizedfrom nexy.decorators import Controller, UseGuardfrom nexy.http import Request def require_login(request: Request) -> None: if not request.session.get("user"): unauthorized(scheme="Bearer") @Controller("/account")@UseGuard(require_login)class AccountController: async def get(self) -> dict: return {"ok": True}L'authentification, par exemple, se connecte à une dépendance du routeur qu'elle protège, comme dans FastAPI ; qui est l'utilisateur reste votre décision :
from fastapi import APIRouter, Depends from nexy import unauthorizedfrom nexy.http import Request def require_login(request: Request) -> None: if not request.session.get("user"): unauthorized(scheme="Bearer") api = APIRouter(prefix="/account", dependencies=[Depends(require_login)])La raison n'est pas le minimalisme en soi : un framework qui sélectionne votre ORM ou votre bibliothèque d'authentification a décidé quelque chose que seule votre application peut décider.
Prochaines étapes
- Structure du projet : chaque dossier et fichier d'un projet
- Glossaire : les mêmes mots, dans un index alphabétique
- Comment fonctionne Nexy : les couches, qui construit quoi et ce que Nexy vous laisse