DokumentasiJavaScript ujung belakang
Dari Hono hingga Nexy
Di Nexy, rute Hono menjadi file Python di bawah src/routes/: c.req berubah menjadi parameter yang diketik, zValidator menjadi model Pydantic, app.route menjadi folder, dan klien RPC hc menjadi tindakan yang diketik.
Setiap bagian di bawah ini menunjukkan kebiasaan Hono, lalu hal yang sama di Nexy.
Sebuah rute
import { Hono } from "hono"; const app = new Hono(); app.get("/api/hello", (c) => c.json({ message: "Hello" })); export default app;Jalur file adalah URL, nama fungsi adalah metodenya, dan dict yang dikembalikan adalah c.json:
async def GET(): return {"message": "Hello"}c.req.param dan c.req.query
app.get("/api/users/:id", (c) => { const id = Number(c.req.param("id")); const fields = c.req.query("fields"); return c.json({ id, fields });});async def GET(id: int, fields: str | None = None): return {"id": id, "fields": fields}/api/users/abc jawaban 404: abc bukan int.
zValidator
import { zValidator } from "@hono/zod-validator";import { z } from "zod"; const NewItem = z.object({ name: z.string(), price: z.number().positive() }); app.post("/api/items", zValidator("json", NewItem), (c) => c.json(c.req.valid("json"), 201));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)app.route dan basePath
export const admin = new Hono(); admin.get("/stats", (c) => c.json({ users: 3 }));app.route("/admin", admin);Folder tersebut adalah jalur dasar:
async def GET(): return {"users": 3}Middleware di jalur
import { createMiddleware } from "hono/factory";import { HTTPException } from "hono/http-exception"; const requireAdmin = createMiddleware(async (c, next) => { if (c.req.header("x-role") !== "admin") throw new HTTPException(403, { message: "Admins only" }); await next();}); app.use("/admin/*", requireAdmin);/admin/* adalah foldernya: a dependencies.py dijalankan sebelum setiap rute di bawahnya.
from nexy.http import Header, HTTPException def require_admin(x_role: str = Header("")) -> None: if x_role != "admin": raise HTTPException(status_code=403, detail="Admins only") dependencies = [require_admin]Middleware di setiap permintaan
app.use("*", async (c, next) => { await next(); c.header("x-app", "demo");});from nexy.http import Request async def middleware(request: Request, call_next): response = await call_next(request) response.headers["x-app"] = "demo" return responsec.set dan c.get
app.use("/api/*", async (c, next) => { c.set("token", c.req.header("authorization")?.replace("Bearer ", "")); await next();}); app.get("/api/whoami", (c) => c.json({ token: c.get("token") }));Nilai yang dihitung untuk pengendali adalah ketergantungan, yang ditanyakan berdasarkan nama:
from nexy.http import Depends, Header def token(authorization: str = Header("")) -> str: return authorization.removeprefix("Bearer ") async def GET(token: str = Depends(token)): return {"token": token}Middleware bawaan
import { compress } from "hono/compress";import { cors } from "hono/cors";import { secureHeaders } from "hono/secure-headers"; app.use("*", cors({ origin: "https://example.com" }), compress(), secureHeaders());from nexy.core.models import NexyConfigModel class NexyConfig(NexyConfigModel): useCORS = {"allow_origins": ["https://example.com"]} useGZip = {"minimum_size": 1000}Dari kumpulan header secureHeaders, nx start menambahkan dua sendiri: X-Content-Type-Options dan Referrer-Policy. Atur yang lain, misalnya Content-Security-Policy, di middleware.py. Lihat Keamanan.
c.html dan Hono BEJ
app.get("/users", (c) => { const users = [{ name: "Ada" }, { name: "Alan" }]; return c.html( <ul> {users.map((user) => ( <li>{user.name}</li> ))} </ul>, );});Halaman .nexy merender HTML di server dari Python dan Jinja2:
users = [{"name": "Ada"}, {"name": "Alan"}]<ul> {% for user in users %} <li>{{ user.name }}</li> {% endfor %}</ul>BEJ interaktif tetap menjadi JSX: komponen React, Preact atau Solid dirender sebagai pulau laman. Lihat Pulau.
c.redirect
app.get("/old", (c) => c.redirect("/new", 301));from nexy.http import RedirectResponse async def GET(): return RedirectResponse("/new", status_code=301)kue
import { getCookie, setCookie } from "hono/cookie"; app.get("/api/theme", (c) => c.json({ theme: getCookie(c, "theme") ?? "light" }));app.post("/api/theme", (c) => { setCookie(c, "theme", "dark"); return c.json({ theme: "dark" });});from nexy.http import Cookie, JSONResponse async def GET(theme: str = Cookie("light")): return {"theme": theme} async def POST(): response = JSONResponse({"theme": "dark"}) response.set_cookie("theme", "dark") return responseHTTPException, notFound dan onError
app.get("/api/orders/:id", (c) => { if (Number(c.req.param("id")) > 100) throw new HTTPException(404, { message: "No such order" }); return c.json({ id: c.req.param("id") });}); app.notFound((c) => c.html("<h1>Not found</h1>", 404));from nexy.http import HTTPException async def GET(id: int): if id > 100: raise HTTPException(status_code=404, detail="No such order") return {"id": id}<h1>Not found</h1>serveStatic
import { serveStatic } from "hono/bun"; app.use("/*", serveStatic({ root: "./public" }));Tidak ada yang perlu ditulis: public/ disajikan di /.
soket web
import { upgradeWebSocket } from "hono/bun"; app.get( "/ws/echo", upgradeWebSocket(() => ({ onMessage(event, ws) { ws.send(event.data); }, })),);from fastapi import WebSocket async def SOCKET(websocket: WebSocket): await websocket.accept() await websocket.send_text(await websocket.receive_text())Klien RPC hc
const routes = app.post("/todos", zValidator("json", z.object({ title: z.string() })), (c) => c.json({ title: c.req.valid("json").title, done: false }),); export type AppType = typeof routes;import { hc } from "hono/client";import type { AppType } from "./src/index"; const client = hc<AppType>("http://localhost:3000");const todo = await (await client.todos.$post({ json: { title: "Write the docs" } })).json();
Tindakan adalah fungsi server dan klien yang diketik menjadi satu. Nexy menulis tipe TypeScript dari tanda tangan Python:
from pydantic import BaseModel from nexy.decorators import Action class Todo(BaseModel): title: str done: bool = False @Actionasync def add_todo(title: str) -> Todo: return Todo(title=title)import { useAction } from "@nexy/client";import { add_todo } from "@nexy/actions"; export function AddTodo() { const { run, data } = useAction(add_todo); return <button onClick={() => run({ title: "Write the docs" })}>{data?.title ?? "Add"}</button>;}Tes dengan app.request
test("says hello", async () => { const res = await app.request("/api/hello"); expect(await res.json()).toEqual({ message: "Hello" });});from fastapi.testclient import TestClient from nexy.app import app def test_hello(): assert TestClient(app).get("/api/hello").json() == {"message": "Hello"}Berlari
| Sayang | Nexy |
|---|---|
npm create hono@latest |
nx new |
bun run --hot src/index.ts atau wrangler dev |
nx dev |
wrangler deploy (Pekerja) |
nx build, lalu nx start di server atau container |
bun test / vitest |
nx test |
Nexy berjalan pada server Python, bukan pada runtime edge: lihat Penerapan.