DokumentasiJavaScript ujung belakang
Dari Ekspres hingga Nexy
Di Nexy, rute Ekspres menjadi file Python di bawah src/routes/: app.get adalah fungsi GET, req berubah menjadi parameter yang diketik, res.json menjadi nilai kembalian, dan app.use menjadi kolom konfigurasi atau middleware.py.
Setiap bagian di bawah menunjukkan kebiasaan Express, lalu hal yang sama di Nexy.
Sebuah rute
import express from "express"; const app = express(); app.get("/api/hello", (req, res) => { res.json({ message: "Hello" });}); app.listen(3000);Jalur file adalah URL, nama fungsinya adalah metode, dan nilai yang dikembalikan adalah isi JSON:
async def GET(): return {"message": "Hello"}req.params
app.get("/api/users/:id", (req, res) => { const id = Number(req.params.id); if (Number.isNaN(id)) return res.status(404).end(); res.json({ id });});:id adalah segmen [id], dan anotasi jenis melakukan konversi dan 404:
async def GET(id: int): return {"id": id}Wildcard * adalah file [...rest].py.
req.query
app.get("/api/search", (req, res) => { const q = req.query.q ?? ""; const page = Number(req.query.page ?? 1); res.json({ q, page });});async def GET(q: str = "", page: int = 1): return {"q": q, "page": page}?page=abc menjawab 422 dengan parameter yang gagal, bukan NaN.
req.body dan validasi
import { z } from "zod"; app.use(express.json()); const NewItem = z.object({ name: z.string(), price: z.number().positive() }); app.post("/api/items", (req, res) => { const parsed = NewItem.safeParse(req.body); if (!parsed.success) return res.status(422).json(parsed.error); res.status(201).json(parsed.data);});Isinya diurai dan divalidasi terhadap model Pydantic sebelum fungsi berjalan:
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)express.Router()
const admin = express.Router(); admin.get("/stats", (req, res) => res.json({ users: 3 })); app.use("/admin", admin);Folder tersebut adalah jalur pemasangan:
async def GET(): return {"users": 3}Middleware rute
function requireAdmin(req, res, next) { if (req.get("x-role") !== "admin") return res.status(403).json({ error: "Admins only" }); next();} admin.use(requireAdmin);dependencies.py berjalan sebelum setiap rute foldernya. Menaikkan akan menghentikan permintaan, seperti halnya tidak memanggil next():
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]app.use perangkat tengah
app.use((req, res, next) => { const start = Date.now(); res.on("finish", () => console.log(req.method, req.url, Date.now() - start)); next();});A middleware.py di root rute membungkus setiap permintaan; call_next adalah next():
import time from nexy.http import Request async def middleware(request: Request, call_next): start = time.perf_counter() response = await call_next(request) response.headers["x-time-ms"] = f"{(time.perf_counter() - start) * 1000:.1f}" return responsecors, compression, helmethost bergaya
import compression from "compression";import cors from "cors"; app.use(cors({ origin: "https://example.com" }));app.use(compression());from nexy.core.models import NexyConfigModel class NexyConfig(NexyConfigModel): useCORS = {"allow_origins": ["https://example.com"]} useGZip = {"minimum_size": 1000} useTrustedHost = {"allowed_hosts": ["example.com", "*.example.com"]} useSession = {"secret_key": "change-me"}kue
import cookieParser from "cookie-parser"; app.use(cookieParser()); app.post("/api/theme", (req, res) => { res.cookie("theme", "dark").json({ theme: "dark" });}); app.get("/api/theme", (req, res) => res.json({ theme: req.cookies.theme ?? "light" }));Cookie adalah parameter untuk dibaca, dan metode respons untuk ditulis:
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 responseexpress-session
import session from "express-session"; app.use(session({ secret: "change-me" })); app.post("/api/login", (req, res) => { req.session.user = req.body.name; res.json({ user: req.session.user });});useSession di nexyconfig.py (di atas) mengaktifkan sesi; request.session adalah dikt:
from pydantic import BaseModel from nexy.http import Request class Login(BaseModel): name: str async def POST(request: Request, body: Login): request.session["user"] = body.name return {"user": body.name}res.redirect
app.get("/old", (req, res) => res.redirect(301, "/new"));from nexy.http import RedirectResponse async def GET(): return RedirectResponse("/new", status_code=301)Unggah file dengan multer
import multer from "multer"; const upload = multer({ limits: { fileSize: 1_000_000 } }); app.post("/api/upload", upload.single("file"), (req, res) => { res.json({ name: req.file.originalname, size: req.file.size });});from nexy.http import HTTPException, UploadFile async def POST(file: UploadFile): data = await file.read() if len(data) > 1_000_000: raise HTTPException(status_code=413, detail="File too large") return {"name": file.filename, "size": len(data)}Tampilan dengan EJS atau Pug
app.set("view engine", "ejs"); app.get("/users", (req, res) => { res.render("users", { users: [{ name: "Ada" }, { name: "Alan" }] });});<ul> <% users.forEach((user) => { %> <li><%= user.name %></li> <% }) %></ul>Halaman .nexy adalah rute dan tampilan sekaligus:
users = [{"name": "Ada"}, {"name": "Alan"}]<ul> {% for user in users %} <li>{{ user.name }}</li> {% endfor %}</ul><%= %> lolos dan {{ }} tidak: lolos dari apa yang diketik pengunjung dengan {{ value | e }}.
express.static
app.use(express.static("public"));Tidak ada yang perlu ditulis: public/ disajikan di /.
Pengendali 404 dan pengendali kesalahan
app.use((req, res) => res.status(404).send("<h1>Not found</h1>")); app.use((err, req, res, next) => { console.error(err); res.status(500).send("<h1>Something broke</h1>");});Dua file di root rute. Klien API masih mendapatkan JSON, browser mendapatkan halaman:
<h1>Not found</h1><h1>Something broke</h1>WebSockets dengan ws
import { WebSocketServer } from "ws"; const wss = new WebSocketServer({ server, path: "/ws/echo" });wss.on("connection", (socket) => socket.on("message", (data) => socket.send(data)));from fastapi import WebSocket async def SOCKET(websocket: WebSocket): await websocket.accept() await websocket.send_text(await websocket.receive_text())Tes dengan Jest dan Supertest
import request from "supertest";import { app } from "./app.js"; test("hello", async () => { const res = await request(app).get("/api/hello"); expect(res.body).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
| Ekspres | Nexy |
|---|---|
npm init + npm i express |
nx new |
nodemon index.js |
nx dev |
NODE_ENV=production node index.js |
nx build, lalu nx start |
npm test |
nx test |
| tidak ada dokumen API | /docs, mencantumkan setiap .py rute |
Mempertahankan bagian depan React Anda
Jika aplikasi Express menyajikan React SPA, komponen akan berpindah ke src/components/ dan dirender sebagai pulau pada halaman .nexy, atau seluruh situs berpindah ke perutean klien dengan useCSR = True. Lihat Pulau dan CSR.