DocsEntenda o modelo

Conceitos centrais

As palavras que o restante dos documentos usam – rota, página, visualização, componente, manipulador, endpoint – cada uma em uma linha, com um link para a página que se aprofunda. Novo no Nexy? Comece com O que é Nexy?.

Como ler esses documentos

Nível A pergunta que ele responde Comece com
Arquitetura de aplicativo qual camada responde a uma solicitação e o que ela se recusa a fazer Como funciona o Nexy
Estrutura do aplicativo onde fica o código e o que um arquivo se torna Estrutura do projeto
Apresentação e renderização o que o leitor vê e quem produz o HTML Visualizações, Renderização
Interação do aplicativo o que o leitor faz e o que é executado em resposta Ações

Os níveis são lidos nessa ordem uma vez. Depois disso, a página que você precisa é uma pesquisa, não um curso.

Arquitetura de aplicativo

Três maneiras de decidir qual código responde a uma URL. São alternativas, não camadas:

  • Roteamento baseado em arquivo (FBR): um arquivo em src/routes/ é uma rota. O padrão.
  • Roteador modular: controladores e módulos em src/apps/, declarados com decoradores, para uma base de código estilo NestJS.
  • Roteador personalizado: um FastAPI APIRouter existente incluído no aplicativo.

A escolha é uma linha da classe NexyConfig em nexyconfig.py.

O roteamento baseado em arquivo não precisa de linha: sem useRouter, Nexy lê src/routes/.

nexyconfig.py
from nexy.core.models import NexyConfigModel  class NexyConfig(NexyConfigModel):    pass  # file-based: no useRouter, the files of src/routes/ are the routes

O roteador modular é o módulo raiz de src/apps/:

nexyconfig.py
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/

Um roteador personalizado é um APIRouter que você já possui:

nexyconfig.py
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 is

Escolher um dos três é uma decisão. A escolha de uma estrutura de front-end é outra: consulte Escolha um roteador e Visualizações.

Estrutura do aplicativo

Cinco noções que um leitor vindo de Next.js costuma fundir em uma só. Eles não são a mesma coisa, e a diferença explica a maioria das perguntas "por que minha página não está sendo renderizada".

Noção O que é isso O que não é
Rota um padrão de URL, produzido pelo caminho de um arquivo um arquivo: um arquivo pode responder a vários URLs ([slug])
Página um arquivo de rota que renderiza uma visualização: .nexy, .md, .mdx, .tsx, .vue, .svelte um endpoint: responde a um documento, não a dados
Ver o que produz o corpo de uma página: Jinja2 e Python, Markdown, ou um componente cliente uma rota: a mesma visualização pode servir diversas páginas
Componente uma peça reutilizável que uma página importa uma rota: no modo servidor, um componente nunca é servido sozinho
Manipulador de rota a função de um arquivo de rota .py, nomeado após o método HTTP (GET, POST, …) uma página: retorna JSON, um arquivo ou um Response
Ponto final o par (URL, método) que um manipulador responde um manipulador: um arquivo pode responder a vários endpoints

Em uma frase: uma página é um arquivo de rota, uma visualização é o que a renderiza, um componente é o que uma página reutiliza, um manipulador responde a um endpoint.

Todos os cinco em um pequeno projeto:

src/
routes/
blog/[slug].nexya route (/blog/{slug}) whose file is a page
api/posts.pya route whose handlers answer two endpoints
components/
Like.tsxa component: never served on its own
src/rotas/blog/[slug].nexy
---from "@/components/Like.tsx" import Like slug: prop[str]---<h1>{{ slug }}</h1><Like post="{{ slug }}" />

O frontmatter e o modelo abaixo dele são a visualização da página; Like é o componente que ele reutiliza.

src/routes/api/posts.py
async def GET():  # the endpoint GET /api/posts    return {"posts": []}  async def POST():  # the endpoint POST /api/posts    return {"created": True}

Com o roteador modular, uma rota é o caminho de um controlador, e um manipulador é um de seus métodos, nomeado após o verbo HTTP:

src/
apps/
app_module.pythe root module: lists the controllers
posts_controller.py a route (/api/posts) whose handlers answer two endpoints
components/
Like.tsxa component: never served on its own
src/apps/posts_controller.py
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}
src/apps/app_module.py
from nexy.decorators import Module from .posts_controller import PostsController  @Module()class AppModule:    controllers = [PostsController]

Uma página ainda é um arquivo de src/routes/, ou uma visualização que um controlador retorna com useViews.

Com um roteador personalizado, uma rota é um caminho do seu APIRouter, e um manipulador é a função que seu decorador registra:

src/legacy.py
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}

As páginas ainda são arquivos de src/routes/: uma página .nexy responde ao lado das rotas incluídas.

Mais duas palavras que voltam em todos os lugares:

  • Layout: um arquivo layout.* que agrupa as páginas abaixo de sua pasta; o root possui o documento HTML. Consulte Layouts.
  • Dependência: função executada antes de um manipulador, aplicada por rota ou por pasta. Consulte Dependências.

Apresentação e renderização

Duas decisões independentes, muitas vezes confundidas:

Noção Uma linha
Modo servidor o servidor renderiza páginas .nexy e .md; componentes são ilhas (modos)
Modo cliente useCSR = True: os arquivos do framework são as rotas e a navegação acontece no navegador
SSR, ISR, RSE, ilhas quatro modos de renderização, decididos por página, exceto CSR, que é por projeto (renderização)
Hidratação o navegador anexando uma estrutura ao HTML o servidor já enviou (ilhas)
Adereços os valores que um pai passa para um componente, declarado name: prop[type] em um arquivo .nexy (arquivos .nexy)

Uma página em modo servidor com uma ilha: o servidor renderiza a página, o navegador hidrata o gráfico apenas quando ele rola para visualização.

src/rotas/sales.nexy
---from "@/components/Chart.tsx" import Chart---<h1>Sales</h1><Chart directive="client:visible" />

A ilha entra na view e o controlador retorna a view:

src/apps/sales_view.nexy
---from "@/components/Chart.tsx" import Chart---<h1>Sales</h1><Chart directive="client:visible" />
src/apps/sales_controller.py
from nexy.decorators import Controllerfrom nexy.hooks import useViews  @Controller("/sales")class SalesController:    async def get(self):        return useViews("src/apps/sales_view.nexy")

Interação do aplicativo

Noção Uma linha
Ação uma função Python marcada como @Action, servida como POST, que pode ser chamada de um formulário ou como uma função digitada (ações)
Formulário um formulário HTML que posta em uma ação, com erros de validação voltando para a página (ações)
Navegação links que pré-buscam e ganchos que leem o caminho, a consulta, os cookies e a sessão (hooks)
Busca de dados feito pela página ou pelo manipulador que precisa; Nexy não fornece nenhuma camada de dados do cliente (manipuladores)

Uma ação e o formulário postado nela, que funciona antes do carregamento de qualquer script:

src/actions/newsletter.py
from nexy.decorators import Action  @Actionasync def subscribe(email: str) -> bool:    return True
src/rotas/index.nexy
<form method="post" action="{{ __actions_path['subscribe'] }}">  <input name="email" type="email">  <button type="submit">Subscribe</button></form>
src/apps/home_view.nexy
<form method="post" action="{{ __actions_path['subscribe'] }}">  <input name="email" type="email">  <button type="submit">Subscribe</button></form>

O que Nexy não faz

Autenticação, autorização, banco de dados, internacionalização, trabalhos em segundo plano, e-mail, pagamentos e pesquisa são de responsabilidade da sua aplicação, e não da estrutura. Nexy dá os pontos de extensão e diz onde conectar cada um: veja O que Nexy deixa para você.

A autenticação, por exemplo, se conecta a uma dependência da pasta que protege. Nexy o executa antes de cada rota abaixo; quem é o usuário fica com sua decisão:

src/routes/account/dependencies.py
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]

A autenticação, por exemplo, é conectada a uma proteção no controlador que ela protege. Nexy o executa antes de cada manipulador da classe; quem é o usuário fica com sua decisão:

src/apps/account_controller.py
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}

A autenticação, por exemplo, se conecta a uma dependência do roteador que ela protege, como em FastAPI; quem é o usuário fica com sua decisão:

src/conta.py
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)])

A razão não é o minimalismo por si só: uma estrutura que escolhe seu ORM ou sua biblioteca de autenticação decidiu algo que somente seu aplicativo pode decidir.

Próximas etapas

Editar no GitHub

Enter abre o resultado, Esc fecha.