الوثائقPython الأطر
من FastAPI إلى النكسي
Nexy هو FastAPI مع كتابة المسار كملف: دالة تجيب ضمن @app.get("/x") تجيب في src/routes/x.py كـ GET، وتبقى بقية التعليمات البرمجية الخاصة بك كما هي.
ما يبقى، ما يتغير
يخدم Nexy معالجاتك مع FastAPI نفسه، لذلك كل شيء داخل المعالج يعمل دون تغيير:
| يبقى كما هو | التغييرات |
|---|---|
| Pydantic النماذج وهيئات الطلب والردود | @app.get("/x") يصبح src/routes/x.py مع async def GET() |
Depends، Query، Header، Cookie، Form، File |
{id} في المسار يصبح ملف [id].py أو [id]/ مجلد |
HTTPException، status، JSONResponse، RedirectResponse |
APIRouter(prefix="/users") يصبح المجلد users/ |
Request، BackgroundTasks، UploadFile، ويب سوكيتس |
dependencies=[...] على جهاز التوجيه يصبح ملف dependencies.py |
فئات البرمجيات الوسيطة، /docs، TestClient |
app.add_middleware(...) يصبح حقل nexyconfig.py: useCORS، useGZip، useMiddlewares |
قم باستيرادها من fastapi كما في السابق، أو من nexy.http، الذي يعيد تصدير نفس الكائنات.
كل قسم أدناه يوضح عادة FastAPI، ثم نفس الشيء في Nexy.
نقطة نهاية
FastAPI يسجل دالة في تطبيق باستخدام مصمم الديكور:
from fastapi import FastAPI app = FastAPI() @app.get("/api/hello")async def hello(): return {"message": "Hello"}يقرأ Nexy عنوان URL من مسار الملف والطريقة من اسم الوظيفة:
async def GET(): return {"message": "Hello"}معلمات المسار والاستعلام
@app.get("/api/users/{id}")async def get_user(id: int, fields: str | None = None): return {"id": id, "fields": fields}تصبح معلمة المسار مقطعًا [id] من اسم الملف. لا تتحرك معلمة الاستعلام:
async def GET(id: int, fields: str | None = None): return {"id": id, "fields": fields}/api/users/abc يجيب 404، لأن abc ليس ب int.
هيئة طلب
from pydantic import BaseModel class NewItem(BaseModel): name: str price: float @app.post("/api/items", status_code=201)async def create_item(item: NewItem): return itemوالنموذج Pydantic هو نفسه. ينتقل رمز الحالة إلى الاستجابة:
from pydantic import BaseModel from nexy.http import JSONResponse class NewItem(BaseModel): name: str price: float async def POST(item: NewItem): return JSONResponse(item.model_dump(), status_code=201)والجسم غير المطابق للنموذج يجيب 422، كما في FastAPI.
عدة طرق على عنوان URL واحد
@app.get("/api/notes/{id}")async def read_note(id: int): ... @app.delete("/api/notes/{id}")async def delete_note(id: int): ...يحتوي ملف واحد على كل طريقة لعنوان URL:
async def GET(id: int): return {"id": id} async def DELETE(id: int): return {"deleted": id}أجهزة التوجيه والبادئات
from fastapi import APIRouter admin = APIRouter(prefix="/admin") @admin.get("/stats")async def stats(): return {"users": 3} app.include_router(admin)المجلد هو البادئة، ولا يوجد شيء لتضمينه:
async def GET(): return {"users": 3}التبعيات على مجموعة من الطرق
from fastapi import Depends, Header, HTTPException def require_admin(x_role: str = Header(...)) -> None: if x_role != "admin": raise HTTPException(status_code=403, detail="Admins only") admin = APIRouter(prefix="/admin", dependencies=[Depends(require_admin)])يتم تشغيل dependencies.py في المجلد قبل كل مسار أسفله:
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]لا تزال تبعية معالج واحد Depends، في توقيعه:
from nexy.http import Depends, Header def current_user(authorization: str = Header(...)) -> str: return authorization.removeprefix("Bearer ") async def GET(user: str = Depends(current_user)): return {"user": user}الوسيطة
from fastapi import Request @app.middleware("http")async def add_header(request: Request, call_next): response = await call_next(request) response.headers["x-app"] = "demo" return responseA middleware.py يغلف مسارات المجلد الخاص به، ويغلفها الجذر جميعًا:
from nexy.http import Request async def middleware(request: Request, call_next): response = await call_next(request) response.headers["x-app"] = "demo" return responseCORS وGZip وغيرها من البرامج الوسيطة على مستوى التطبيق
from fastapi.middleware.cors import CORSMiddlewarefrom fastapi.middleware.gzip import GZipMiddleware app.add_middleware(CORSMiddleware, allow_origins=["https://example.com"])app.add_middleware(GZipMiddleware, minimum_size=1000)كل واحد هو حقل nexyconfig.py، مع نفس الوسائط:
from nexy.core.models import NexyConfigModel class NexyConfig(NexyConfigModel): useCORS = {"allow_origins": ["https://example.com"]} useGZip = {"minimum_size": 1000}صفحات HTML
from fastapi import Requestfrom fastapi.templating import Jinja2Templates templates = Jinja2Templates(directory="templates") @app.get("/users")async def users_page(request: Request): users = [{"name": "Ada"}, {"name": "Alan"}] return templates.TemplateResponse(request, "users.html", {"users": users})صفحة .nexy تحتوي على Python أعلى السطر --- و Jinja2 أسفله، في ملف واحد:
users = [{"name": "Ada"}, {"name": "Alan"}]<ul> {% for user in users %} <li>{{ user.name }}</li> {% endfor %}</ul>وراثة القالب
<html> <body>{% block content %}{% endblock %}</body></html>A layout.nexy يغلف كل صفحة من المجلد الخاص به، مع عدم وجود {% extends %} في الصفحات:
children: prop[NexyElement]<!DOCTYPE html><html> <body>{{ children | safe }}</body></html>الملفات الثابتة
from fastapi.staticfiles import StaticFiles app.mount("/static", StaticFiles(directory="static"), name="static")يتم تقديم الملفات الموجودة في public/ من الجذر: public/robots.txt هو /robots.txt.
صفحات لم يتم العثور عليها
from fastapi import Requestfrom fastapi.responses import HTMLResponsefrom starlette.exceptions import HTTPException as StarletteHTTPException @app.exception_handler(StarletteHTTPException)async def not_found(request: Request, exc: StarletteHTTPException): return HTMLResponse("<h1>Not found</h1>", status_code=exc.status_code)يجيب notfound.nexy على المتصفحات، ولا يزال عملاء API يحصلون على {"detail": ...}:
<h1>Not found</h1><p><a href="/">Back home</a></p>WebSockets
from fastapi import WebSocket @app.websocket("/ws/echo")async def echo(websocket: WebSocket): await websocket.accept() await websocket.send_text(await websocket.receive_text())from fastapi import WebSocket async def SOCKET(websocket: WebSocket): await websocket.accept() await websocket.send_text(await websocket.receive_text())الاختبارات
from fastapi.testclient import TestClient from main import app def test_hello(): assert TestClient(app).get("/api/hello").json() == {"message": "Hello"}العميل هو نفسه؛ التطبيق يأتي من Nexy:
from fastapi.testclient import TestClient from nexy.app import app def test_hello(): assert TestClient(app).get("/api/hello").json() == {"message": "Hello"}الجري
| FastAPI | نكسي |
|---|---|
uvicorn main:app --reload |
nx dev |
uvicorn main:app --workers 4 |
nx build، ثم nx start |
/docs من FastAPI() |
/docs، سرد كل .py الطريق |
الحفاظ على التطبيق الموجود
ليس عليك تحريك كل مسار مرة واحدة. APIRouter لديك بالفعل حوامل كما هي:
from nexy.core.models import NexyConfigModel from legacy.api import router class NexyConfig(NexyConfigModel): useRouter = routerمساراتك القديمة وملفات src/routes/ ثم أجب جنبًا إلى جنب. أثناء تعيين useRouter، لا يتم تغليف الصفحات .nexy بواسطة layout.nexy: انقل واجهة برمجة التطبيقات أولاً، ثم الصفحات، ثم قم بإزالة الحقل. انظر تكامل جهاز التوجيه المخصص.