CRUD-operationer (Create, Read, Update, Delete) utgör ryggraden i nästan alla webbapplikationer. Dessa operationer tillåter användare att interagera med data genom att skapa nya resurser, hämta befintliga, samt uppdatera och ta bort resurser. I det här fallet representeras resurserna av bilar, som lagras som dokument i en MongoDB-databas. FastAPI följer webbstandarder och mappas därmed direkt till HTTP-metoder: POST används för att skapa nya instanser, GET för att läsa, PUT för att uppdatera och DELETE för att ta bort resurser.

För att implementera dessa funktioner, måste vi börja med att skapa en API-router som kan hantera bilresurserna. Låt oss nu gå igenom stegen för att sätta upp denna router.

Förberedelserna börjar med att skapa en dedikerad mapp för API-router i din applikation. Skapa en mapp som heter /routers, som kommer att innehålla alla rutter. Inuti denna mapp skapar vi en fil för bilresurser, exempelvis /routers/cars.py, där vi kommer att definiera de olika CRUD-operationerna.

En grundläggande FastAPI-router (APIRouter) är lätt att definiera. Här är ett exempel på hur man skapar en router för hantering av bilresurser:

python
from fastapi import APIRouter, Body, Request, status from models import CarModel router = APIRouter()

För att routerfunktionen ska kunna användas i den huvudsakliga applikationen måste vi ansluta den till huvudapplikationen genom att importera och inkludera den i app.py:

python
from fastapi import FastAPI
from routers.cars import router as cars_router app = FastAPI() app.include_router(cars_router, prefix="/cars", tags=["cars"])

Den första rutt som vi kommer att implementera är en POST-begäran för att skapa en ny bil. Här använder vi Pydantic-modellen CarModel för att validera och strukturera indata. När en POST-begäran skickas till denna rutt, kommer bilen att läggas till i databasen:

python
@router.post(
"/", response_description="Lägg till ny bil", response_model=CarModel, status_code=status.HTTP_201_CREATED, ) async def add_car(request: Request, car: CarModel = Body(...)): cars = request.app.db["cars"] document = car.model_dump(by_alias=True, exclude=["id"]) inserted = await cars.insert_one(document) return await cars.find_one({"_id": inserted.inserted_id})

För att testa denna funktion kan vi använda HTTPie eller applikationens interaktiva dokumentation, som kan nås på http://127.0.0.1:8000/docs.

För att visa alla bilar som finns i databasen implementerar vi en GET-begäran. Denna metod använder MongoDB:s find()-metod för att hämta alla dokument från samlingen och returnera dem som en lista:

python
@router.get(
"/", response_description="Lista alla bilar", response_model=CarCollection, ) async def list_cars(request: Request): cars = request.app.db["cars"] results = [] cursor = cars.find() async for document in cursor: results.append(document) return CarCollection(cars=results)

Om du kör applikationen och gör en GET-begäran med HTTPie till http://127.0.0.1:8000/cars/, kommer du att få en lista med alla bilar som lagts till i databasen.

För att förbättra prestanda kan du också använda to_list()-metoden från Motor (MongoDB:s asynkrona driver) för att direkt omvandla resultatet till en lista:

python
return CarCollection(
cars=await cars.find().to_list(1000) )

Genom att följa dessa enkla steg har vi nu implementerat de grundläggande CRUD-operationerna för bilresurser i FastAPI och MongoDB. Men det är inte allt. När du bygger och utökar din applikation kan du behöva implementera ytterligare funktioner, som autentisering och auktorisering för att hantera användarbehörigheter, eller att införa mer sofistikerade sökfunktioner för att effektivt kunna filtrera bilresurser baserat på olika parametrar som pris, årtal eller märke.

För att göra applikationen mer robust, är det också viktigt att hantera fel och validering ordentligt. FastAPI gör detta genom att generera informativa felmeddelanden när användare skickar felaktiga eller ofullständiga data. Genom att noggrant definiera dina modeller och använda korrekt datahantering, kan du säkerställa en användarvänlig och pålitlig API.

Hur hanterar man användarautentisering och serveråtgärder i Next.js?

I Next.js finns det en rad funktioner som gör det möjligt att skapa både statiska och dynamiska sidor, samt hantera komplexa användarinteraktioner och autentisering. En av de mest kraftfulla funktionerna är serveråtgärder, som är asynkrona funktioner som endast körs på servern och hanterar datainhämtning samt dataändringar (genom POST, PUT, DELETE-metoder). Dessa kan anropas både genom vanliga formulärsändningar och genom React-händelsehanterare eller tredjepartsbibliotek som Axios.

Fördelarna med att använda serveråtgärder är flera. För det första förbättras prestandan avsevärt eftersom klientbaserad JavaScript-kod minimeras. Eftersom åtgärderna körs uteslutande på servern kan applikationens säkerhet stärkas, och applikationen kan till och med köras utan JavaScript, vilket påminner om gamla applikationer från tidigare decennier.

En central del i användarhantering är autentisering, och här spelar Iron Session en viktig roll. Iron Session är ett verktyg som hanterar stateless-sessioner baserade på cookies. Genom att implementera denna funktionalitet kan man enkelt autentisera användare utan att behöva hålla användartillstånd på klientsidan. I stället sker all autentisering på servern genom säkra cookies, vilket också minskar säkerhetsrisker.

För att komma igång med autentisering i en Next.js-applikation krävs några grundläggande steg. Först och främst måste Iron Session-paketet installeras:

bash
npm i iron-session

Nästa steg är att skapa en konfigurationsfil som definierar inställningarna för sessionen. Detta görs i en fil, exempelvis /src/lib.js, där sessionens inställningar specificeras. Här måste ett starkt lösenord användas för att säkerställa att sessionens cookies är ordentligt krypterade:

js
export const sessionOptions = {
password: "complex_password_at_least_32_characters_long", cookieName: "farmcars_session", cookieOptions: { httpOnly: true, secure: false, maxAge: 60 * 60, } };

Iron Session API är enkelt att använda, och den gör det möjligt att sätta och hämta värden i en session. I Next.js använder vi detta för att hålla reda på den inloggade användarens information, såsom användarnamn och autentiseringstoken, vilket är nödvändigt för att kunna göra anrop till serverns API:er.

För att autentisera en användare skapar vi en serveråtgärd som hanterar inloggning. Detta görs genom att skicka användarnamn och lösenord till servern, där de valideras. Om inloggningen är framgångsrik, sparas användarens uppgifter i sessionen, annars tas sessionen bort:

js
export const login = async (status, formData) => {
const username = formData.get("username"); const password = formData.get("password"); const result = await fetch(`${process.env.API_URL}/users/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }) }); const data = await result.json(); const session = await getSession(); if (result.ok) { session.username = data.username; session.jwt = data.token; await session.save(); redirect("/private"); } else { session.destroy(); return { error: data.detail }; } };

Den här koden hanterar inloggningen och sparar användarens session om autentiseringen lyckas. Om användarnamnet och lösenordet är korrekt, kommer användaren att omdirigeras till en privat sida där de kan få tillgång till känslig information.

Det finns även en annan viktig aspekt att beakta, nämligen hantering av sidor som inte finns, dvs. 404-fel. För att skapa en anpassad sida för dessa fel, kan du skapa en fil som hanterar alla rutter som inte hittats:

js
const NotFoundPage = () => {
return ( <div> <h1>Custom Not Found Page</h1> <p>take a look at our cars</p> </div> ); };

När det gäller prestanda och användarupplevelse är det viktigt att förstå hur Next.js hanterar statiska sidor och dynamiska rutter. För statiska sidor använder man generateStaticParams(), som gör det möjligt att definiera vilka sidor som ska genereras vid byggtillfället. För en applikation som listar bilar kan detta innebära att alla bilsidor genereras som statiska HTML-sidor.

För att optimera prestandan ytterligare, är det också viktigt att använda komponenter som Image från Next.js. Denna komponent erbjuder ett API som gör det möjligt att optimera bildhantering och förbättra sidans laddningstider.

Sammanfattningsvis är Next.js ett kraftfullt ramverk som erbjuder en mängd funktioner för att skapa snabbare och mer effektiva webbapplikationer. Genom att använda serveråtgärder, autentisering via Iron Session och statiska sidor, kan du bygga säkra och prestandaoptimerade applikationer. Dessutom, genom att tänka på hantering av fel och användarvänliga omdirigeringar, skapar du en smidig upplevelse för användaren, oavsett om de navigerar rätt eller hamnar på en ogiltig sida.