Utviklingen av moderne applikasjoner er i dag mer enn bare å bygge funksjoner som fungerer; det handler om å møte brukernes forventninger til enestående ytelse, sikkerhet og brukeropplevelse. Det er et skifte i hvordan vi ser på applikasjoner, hvor det ikke lenger er nok at en app kun løser et problem – brukerne ønsker at løsningen føles naturlig, rask og trygg. I en verden hvor konkurransen er hard, er det ikke nok å utvikle apper som bare fungerer. Man må også kunne levere funksjoner som føles intuitive og som imøtekommer brukerens behov på en sømløs måte.

For å møte disse forventningene, er det nødvendig å bygge robuste, funksjonssentrerte løsninger ved å bruke Python. Dette krever ikke bare teknisk dyktighet, men også et sterkt fokus på hvordan applikasjonens kjernefunksjoner samhandler med sluttbrukeren. En viktig komponent her er å implementere applikasjoner som er både dynamiske og sikre. For å kunne utvikle slike apper, kreves det en forståelse for både grunnleggende og avanserte teknologier, fra å bygge API-er til håndtering av sikkerhet, testing og distribusjon.

For å komme i gang med utvikling, begynner man ofte med å sette opp sitt utviklingsmiljø. Dette inkluderer installasjon av nødvendige verktøy og biblioteker, som Python 3.11, samt konfigurering av virtuelle miljøer som sikrer at arbeidsflyten er reproduserbar. Ved å bruke verktøy som FastAPI for å bygge API-er, Pydantic for datavalidering og SQLAlchemy for databasehåndtering, kan man raskt begynne å implementere sentrale funksjoner som CRUD-operasjoner, autentisering og sikkerhetsprotokoller. Dette utgjør kjernen i applikasjonen, og disse funksjonene må utvikles på en måte som er både sikker og skalerbar.

Når applikasjonen skal bygges, er det viktig å ta hensyn til flere faktorer. Brukeropplevelsen er essensiell, og derfor bør man integrere funksjoner som dynamiske skjemaer, dra-og-slipp opplastinger, tilpassede feilsider og adaptive temaer som gjør at applikasjonen føles mer interaktiv. I tillegg bør man inkludere elementer som sanntids varsler, to-faktor autentisering og god rollebasert tilgangskontroll. Sikkerhet er et viktig aspekt her, og det er viktig å implementere beskyttelsestiltak som CSRF, CORS, kryptering og innholds-sikkerhetspolicyer (CSP). Å bygge et API som støtter slike funksjoner uten å gjøre det unødvendig komplisert er en utfordring som krever både kunnskap og erfaring.

En annen viktig komponent er integreringen med eksterne tjenester. For å tilby en rikere brukeropplevelse, kan man integrere med tredjeparts tjenester som SMS via Twilio, sosiale pålogginger via OAuth2, eller webhook-håndtering for hendelsesdrevne arbeidsflyter. Dette gjør at applikasjonen ikke bare er mer funksjonell, men også at brukerne får en mer strømlinjeformet opplevelse. For å sikre at applikasjonen fungerer stabilt og skalerbart under høy belastning, kan man bruke verktøy som Docker for containerisering og Kubernetes for orkestrering. Dette sørger for at applikasjonen kan håndtere trafikk og last på en effektiv måte, samtidig som den kan distribueres på en trygg måte.

Når applikasjonen er utviklet, er det viktig å legge vekt på testing og automatisering. Å bruke Pytest for både enhetstesting og integrasjonstesting, kombinert med automatiserte arbeidsflyter via GitHub Actions, kan bidra til å opprettholde høy kvalitet på koden. Testing bør ikke bare dekke funksjonalitet, men også sikkerhet og ytelse, for å sikre at applikasjonen kan håndtere forskjellige scenarier, inkludert potensielle angrep. Automatisering av tester og byggprosesser gjør at man kan fokusere mer på utvikling, samtidig som man får pålitelige resultater som viser at applikasjonen er klar for produksjon.

En viktig del av utviklingen av moderne applikasjoner er loggføring og overvåkning. Dette bidrar til å sikre at man kan oppdage problemer tidlig og reagere på dem raskt. Ved å implementere robust loggføring, overvåking og håndtering av systemhendelser kan man unngå de fleste vanlige feil og opprettholde en høy driftssikkerhet. Brukere setter pris på applikasjoner som fungerer feilfritt, og dette kan oppnås ved å bruke best practices innen logging og overvåkning. Dette innebærer blant annet å sette opp sentralisert logghåndtering og implementere tiltak for å beskytte mot datalekkasjer og misbruk.

En annen viktig vurdering er hvordan applikasjonen skal håndtere sensitive data. Implementering av sterke krypteringsmetoder og sikre kommunikasjonskanaler bør være en prioritet fra starten av. AES-GCM-kryptering og HTTPS for kommunikasjon over nettverket er grunnleggende sikkerhetstiltak som bør implementeres. Samtidig er det viktig å ha et klart fokus på personvern og hvordan applikasjonen håndterer brukerdata i samsvar med lover og forskrifter som GDPR.

Det er derfor viktig at enhver utvikler som arbeider med Python for applikasjonsutvikling har et solid grunnlag i både de tekniske ferdighetene og forståelsen for hvordan applikasjoner skal bygges med tanke på både ytelse, sikkerhet og brukeropplevelse. Å ha de rette verktøyene og rammeverkene på plass, sammen med en forståelse for hvordan man kan teste, deployere og overvåke applikasjoner, gir en utvikler et sterkt fundament for å bygge applikasjoner som møter dagens krav. Det handler ikke bare om å bygge funksjoner – det handler om å bygge applikasjoner som brukerne elsker, som er sikre og som fungerer feilfritt under alle forhold.

Hvordan lage en effektiv filopplastingsløsning med sanntidsvisning og skalerbar lagring

I moderne webutvikling er det viktig å tilby brukerne en enkel, effektiv og interaktiv måte å laste opp filer på. En løsning som kombinerer sanntids tilbakemelding på opplastingsprogresjon med fleksibel og skalerbar filbehandling, er et utmerket valg for mange applikasjoner. Ved å bruke en kombinert frontend og backend-løsning med JavaScript og FastAPI, kan man enkelt implementere en opplastingsfunksjon som både er intuitiv og pålitelig.

I frontend-delen implementerer vi et JavaScript-skript som gjør det mulig for brukerne å dra og slippe filer direkte i en definert drop-zone. Når en fil blir droppet, starter opplastingsprosessen, og sanntidsdata om fremdrift blir synliggjort for brukeren. Skriptet bruker XMLHttpRequest for å sende filen til serveren, samtidig som det håndterer hendelser som oppdatering av progresjonsbar og visning av statusmeldinger som “Upload complete” når filen er opplastet.

På backend-siden håndteres filopplastingen av FastAPI, som gir en effektiv og asynkron løsning for filbehandling. Ved å lage et POST-endepunkt som mottar filene, lagrer dem i et spesifisert katalog, og deretter returnerer en URL som peker til den opplastede filen, kan vi gi brukeren en direkte lenke til filen for videre bruk. Filene blir lagret med unike ID-er for å unngå navnekonflikter, og det er implementert feilhåndtering som gir tilbakemelding ved eventuelle problemer med lagring.

Skalering med Sky-lagring

En løsning som begynner med lokal lagring er praktisk for utvikling og mindre applikasjoner, men når behovet for lagringsplass øker, kan det være lurt å overføre til skybaserte tjenester som AWS S3. Ved å bruke boto3-biblioteket i Python kan FastAPI lett konfigureres til å støtte S3-opplastinger. Denne overgangen kan gjøres uten å endre frontend-koden, ettersom serverens API forblir det samme, mens filene blir lagret på S3 i stedet for lokalt. Ved å bruke miljøvariabler kan vi konfigurere applikasjonen til å veksle mellom lokal lagring og skybasert lagring etter behov, noe som gjør systemet svært fleksibelt og lett å tilpasse.

Når man bruker S3, får man fordelene med høy tilgjengelighet, pålitelighet og global rekkevidde. Brukeren kan få tilgang til filene via en offentlig URL, og man kan utnytte S3’s innebygde sikkerhet og skalerbarhet for å håndtere store datamengder uten å måtte bekymre seg om lokal lagringskapasitet.

Forbedring av Brukeropplevelsen

Brukeropplevelsen er avgjørende for hvordan en filopplastingsløsning fungerer. Sanntidsoppdateringer, som viser hvor mye av filen som er lastet opp, gir en følelse av kontroll og gjør prosessen mer intuitiv. Det å gi brukerne visuell tilbakemelding, som for eksempel progresjonsindikatorer, er viktig for å redusere usikkerhet under opplastingen. Når opplastingen er fullført, kan en URL vises slik at brukeren lett kan dele eller bruke filen videre.

Det er også viktig å vurdere hvordan man kan håndtere store filer eller flere samtidige opplastinger. I slike tilfeller kan man vurdere å implementere en websocket-basert status-API for å få mer detaljert tilbakemelding om opplastingsstatusen. Alternativt kan man bruke NGINX-moduler som håndterer opplastingsprogresjon på servernivå for å gi mer robust overvåkning i produksjonsmiljøer.

Dynamisk Formrendering med Pydantic

I tillegg til filopplastinger, kan dynamisk rendering av skjemaer være et viktig aspekt ved å bygge fleksible webapplikasjoner. Ved å bruke Pydantic-skjemaer kan vi lage skjemaer som endres basert på brukerens interaksjon, for eksempel ved å vise ekstra felt dersom brukeren velger å abonnere på et nyhetsbrev. Dette gir muligheten til å bruke de samme datamodellene på både frontend og backend, slik at validering og visning av data er konsistent.

Skjemaene kan defineres som JSON-strukturer som lar frontend generere nødvendige felter dynamisk. Ved å bruke Pydantic for både validering på serveren og rendering på klienten, kan vi være sikre på at dataene er riktige og at logikken ikke blir duplisert.

Viktige betraktninger

Når du implementerer filopplastingssystemer og dynamiske skjemaer, er det flere viktige ting å huske på. Først og fremst er det essensielt at både frontend og backend er i synk med hverandre, slik at validering og visning skjer konsekvent. Feilhåndtering og tilbakemelding til brukeren er også viktige for å sikre at applikasjonen føles stabil og pålitelig. For høy ytelse og god brukeropplevelse bør det legges vekt på asynkron filbehandling og tilstrekkelig sikkerhet for filopplastinger.

Videre er det avgjørende å gjøre systemet skalerbart, enten gjennom skybasert lagring som AWS S3 eller ved å optimalisere filhåndteringen ved hjelp av WebSockets eller servermoduler som håndterer store opplastinger. Det er også viktig å tilpasse løsningen til de spesifikke behovene til applikasjonen, enten det er for en enkel webapplikasjon eller en mer kompleks, skalerbar løsning for større prosjekter.

Hvordan implementere OAuth2 med Google og GitHub i en webapplikasjon

OAuth2-protokollen gir en sikker måte for brukere å autorisere applikasjoner til å få tilgang til deres ressurser uten å måtte dele sine passord. Når en bruker logger inn via Google eller GitHub, blir applikasjonen omdirigert til en autorisasjonsserver for å godkjenne forespørselen, og etter godkjenning returneres brukeren til applikasjonen med en kode som kan brukes til å hente tilgangstoken. Dette gjør det mulig å bekrefte brukerens identitet, få tilgang til profilinformasjonen deres, og holde sesjonen aktiv uten videre inngrep fra brukeren.

For å bruke OAuth2 med Google eller GitHub, må applikasjonen først registreres hos begge leverandørene. For Google skjer dette via Google Developer Console, hvor du oppretter OAuth-legitimasjon og angir en godkjent omdirigerings-URL. På samme måte registreres applikasjonen hos GitHub Developer Settings, og du setter opp en callback-URL. Etter registreringen får du tilgang til klient-ID og klienthemmelighet for begge leverandører, som skal konfigureres som miljøvariabler eller i en sikker konfigurasjonsfil. Eksempel på hvordan dette kan gjøres i Python:

python
# config.py import os GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID") GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET") GOOGLE_REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI", "http://localhost:8000/auth/google/callback") GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID") GITHUB_CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET") GITHUB_REDIRECT_URI = os.getenv("GITHUB_REDIRECT_URI", "http://localhost:8000/auth/github/callback")

Når applikasjonen er registrert, kan vi begynne å implementere OAuth2-innloggingsflyten. Når en bruker klikker på “Logg inn med Google” eller “Logg inn med GitHub,” blir de omdirigert til leverandørens autorisasjons-URL med nødvendige parametre, for eksempel klient-ID, omdirigerings-URL og forespurte tillatelser. Eksemplet nedenfor viser hvordan Google-innloggingen kan startes:

python
# app/routes/oauth2.py from fastapi import APIRouter from fastapi.responses import RedirectResponse from config import GOOGLE_CLIENT_ID, GOOGLE_REDIRECT_URI router = APIRouter() @router.get("/auth/google/login") def login_with_google(): scope = "openid email profile" auth_url = ( f"https://accounts.google.com/o/oauth2/v2/auth?" f"client_id={GOOGLE_CLIENT_ID}" f"&redirect_uri={GOOGLE_REDIRECT_URI}" f"&response_type=code" f"&scope={scope}" f"&access_type=offline" f"&prompt=consent" ) return RedirectResponse(auth_url)

For GitHub er prosessen nesten identisk, men med en annen autorisasjons-URL og ulike tilgangsparametre:

python
# app/routes/oauth2.py
from config import GITHUB_CLIENT_ID, GITHUB_REDIRECT_URI @router.get("/auth/github/login") def login_with_github(): scope = "read:user user:email" auth_url = ( f"https://github.com/login/oauth/authorize?" f"client_id={GITHUB_CLIENT_ID}" f"&redirect_uri={GITHUB_REDIRECT_URI}" f"&scope={scope}" ) return RedirectResponse(auth_url)

Når brukeren godkjenner tilgangen, omdirigeres de tilbake til applikasjonen via en callback-URL, og kodeparameteren som returneres, brukes til å hente tilgangs- og oppfriskningstoken. Dette kan gjøres ved hjelp av HTTPX for å sende POST-forespørsler til leverandørens token-endepunkt. Eksempelet nedenfor viser hvordan dette kan implementeres for Google:

python
import httpx
from config import GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI @router.get("/auth/google/callback") async def google_callback(code: str): token_url = "https://oauth2.googleapis.com/token" data = { "client_id": GOOGLE_CLIENT_ID, "client_secret": GOOGLE_CLIENT_SECRET, "code": code, "redirect_uri": GOOGLE_REDIRECT_URI, "grant_type": "authorization_code" } async with httpx.AsyncClient() as client: resp = await client.post(token_url, data=data) resp.raise_for_status() tokens = resp.json() access_token = tokens["access_token"] refresh_token = tokens.get("refresh_token") id_token = tokens.get("id_token")

Når du har fått tilgangstoken, kan applikasjonen hente brukerens profilinformasjon ved å sende en GET-forespørsel til Google eller GitHubs API med tilgangstokenet i autorisasjonsheaderen. Dette muliggjør synkronisering av brukerdata i applikasjonen, enten ved å oppdatere eksisterende brukerinformasjon eller opprette en ny bruker.

python
# Hente Google-brukerinformasjon async with httpx.AsyncClient() as client: userinfo_resp = await client.get( "https://www.googleapis.com/oauth2/v3/userinfo", headers={"Authorization": f"Bearer {access_token}"} ) userinfo = userinfo_resp.json()

For GitHub skjer synkroniseringen på samme måte:

python
# Hente GitHub-brukerinformasjon
async with httpx.AsyncClient() as client: userinfo_resp = await client.get( "https://api.github.com/user", headers={"Authorization": f"Bearer {access_token}"} ) userinfo = userinfo_resp.json()

Etter at brukerens informasjon er hentet, kan applikasjonen bruke disse dataene til å oppdatere brukerdatabasen eller opprette en ny brukerrekord. Deretter kan applikasjonen generere et eget session-token eller JWT for å logge brukeren inn uten at de trenger å skrive inn passordet.

Et annet viktig aspekt av OAuth2 er håndteringen av tokenfornyelse. OAuth2-tilgangstokener er vanligvis kortvarige, og for å opprettholde tilgangen til brukerens data, må applikasjonen håndtere tokenfornyelse. Google gir et oppfriskningstoken som kan brukes til å hente et nytt tilgangstoken uten at brukeren må gi sitt samtykke på nytt, mens GitHub-tokenene vanligvis er langvarige, men kan tilbakekalles. For å forlenge tilgangen, kan applikasjonen bruke et oppfriskningstoken for å hente et nytt tilgangstoken:

python
async def refresh_google_token(refresh_token):
token_url = "https://oauth2.googleapis.com/token" data = { "client_id": GOOGLE_CLIENT_ID, "client_secret": GOOGLE_CLIENT_SECRET, "refresh_token": refresh_token, "grant_type": "refresh_token" } async with httpx.AsyncClient() as client: resp = await client.post(token_url, data=data) resp.raise_for_status() tokens = resp.json() return tokens["access_token"]

Denne funksjonen kan kalles når tilgangstokenet utløper, og det nye tokenet kan lagres og brukes for videre API-forespørsler.

Det er også viktig å merke seg at sikkerheten til brukerdata må ivaretas hele veien, og at oppfriskningstoken og tilgangstoken bør lagres sikkert, for eksempel i databasen eller som miljøvariabler. Bruk av HTTPS for alle kommunikasjon er også avgjørende for å beskytte dataene under overføring.

OAuth2-flyten kan tilpasses for å støtte flere leverandører, og ved å følge samme mønster for autorisasjon, tokenutveksling og synkronisering av brukerprofiler, kan applikasjonen enkelt utvides til å inkludere flere tredjepartsautentiseringstjenester.

Hvordan effektivt håndtere bakgrunnsjobber og asynkrone oppgaver med Celery og FastAPI

I dagens applikasjoner er det avgjørende å håndtere tunge operasjoner på en effektiv måte uten å blokkere hovedprosessen. Dette kan oppnås ved å bruke bakgrunnsjobber som kjører asynkront, og som kan trigges dynamisk via API-endepunkter. Dette kapittelet utforsker hvordan du kan bruke Celery for å administrere bakgrunnsprosesser og integrere asynkrone funksjoner i FastAPI-applikasjoner.

Celery er et kraftig verktøy for å kjøre bakgrunnsprosesser i Python. Ved å bruke Celery kan man fordele oppgaver som kan ta tid, som rapportgenerering eller opprydding av utdaterte sesjoner, til bakgrunnen. Dette frigjør hovedprosessen og gir applikasjonen muligheten til å håndtere andre anrop uten forsinkelser. Typiske oppgaver i denne sammenhengen kan for eksempel være en opprydding av utdaterte brukersesjoner, som er en relativt tung oppgave for serveren, men som kan håndteres effektivt uten å påvirke brukeropplevelsen.

Trigge bakgrunnsjobber fra FastAPI-endepunkter

Når du utvikler applikasjoner med FastAPI, kan du enkelt trigge bakgrunnsjobber når det er behov for det. Dette gir brukeren eller administratoren umiddelbar tilbakemelding om at en prosess har startet, samtidig som den tunge behandlingen skjer asynkront. For eksempel kan man sette opp et endepunkt som starter en jobb for å generere en rapport:

python
from fastapi import APIRouter
from app.tasks import generate_report_task router = APIRouter() @router.post("/reports/generate") def generate_report(user_id: int): report_id = random.randint(1000, 9999) task = generate_report_task.delay(report_id, user_id) return {"msg": "Report generation started", "task_id": task.id}

I dette tilfellet vil generate_report_task kjøres i bakgrunnen, og hovedprosessen kan fortsette å håndtere andre forespørsler uten blokkering.

Kjeding av oppgaver med callback-struktur

En annen vanlig situasjon i applikasjoner er når flere oppgaver må utføres i rekkefølge. For eksempel kan du generere en rapport, sende en e-post til brukeren med rapportens URL, og deretter logge utfallet av operasjonen. Celery støtter dette med funksjoner som chain og link. En kjede sørger for at en oppgave kjøres etter en annen, og gir deg muligheten til å passere resultater mellom oppgavene:

python
from celery import chain
@celery_app.task def notify_user_task(result, user_id): print(f"Notify user {user_id}: report is ready at {result['url']}") return {"notified": True} def start_report_workflow(report_id, user_id): job_chain = chain( generate_report_task.s(report_id, user_id), notify_user_task.s(user_id) ) job_chain.apply_async()

Ved å bruke kjeder kan vi sikre at prosessen skjer i riktig rekkefølge og at resultatene overføres fra en oppgave til en annen. Dette gjør at komplekse arbeidsflyter kan håndteres på en strukturert og effektiv måte.

Parallelle arbeidsflyter og grupper

Celery gjør det også mulig å kjøre flere oppgaver parallelt. Dette kan være nyttig når du har flere prosesser som kan kjøres uavhengig av hverandre, for eksempel når du skal rydde opp i flere datasett samtidig:

python
from celery import group
def run_cleanup_jobs(): cleanup_group = group( cleanup_expired_sessions_task.s(), # Andre opprydningsoppgaver kan legges til her ) cleanup_group.apply_async()

Når alle jobbene i en gruppe er fullført, samles resultatene og kan behandles videre.

Overvåking av bakgrunnsjobber

Celery gir deg muligheten til å overvåke og administrere bakgrunnsprosesser gjennom verktøy som Flower. Dette verktøyet gir et sanntids dashbord som viser statusen på alle jobbene dine: hvilke som kjører, er vellykkede, har feilet eller er ventende. Dette er spesielt nyttig for feilsøking og ytelsesoptimalisering.

Enkelte funksjoner som retries (for å prøve en oppgave på nytt ved feil), timeouts (for å hindre at oppgaver henger seg) og crontab-planlegging (for periodiske jobber som kan kjøre på bestemte tider) gir ytterligere kontroll over hvordan bakgrunnsjobbene håndteres.

Asynkrone endepunkter i FastAPI

Når applikasjonen din krever ekstern kommunikasjon med databaser, API-er eller filservere, kan det oppstå forsinkelser. Tradisjonelt vil en synkron applikasjon vente på svar fra en ekstern kilde før den kan fortsette å håndtere andre forespørsler. Dette kan føre til at serveren blir blokkert og at andre brukere må vente på tur. Med asynkrone endepunkter kan FastAPI håndtere flere forespørsler samtidig, og dermed forbedre applikasjonens ytelse under høy belastning.

For å gjøre en FastAPI-rute asynkron, kan du bruke async def og await for å definere funksjoner som ikke blokkerer tråden mens de venter på ekstern I/O, som en databaseforespørsel eller et API-anrop:

python
@router.get("/profile/{user_id}")
async def get_profile(user_id: int): profile = await async_db_query(user_id) # Ikke blokkere: håndter andre forespørsler mens du venter return profile

Dette gjør det mulig for serveren å håndtere andre forespørsler mens den venter på svaret fra databasen, noe som øker skalerbarheten.

Samle flere HTTP-anrop samtidig

En vanlig utfordring i applikasjoner som involverer flere eksterne API-er er ventetiden. Hvis du gjør anrop til forskjellige tjenester sekvensielt, vil den totale ventetiden være summen av alle forsinkelsene. Ved å bruke httpx.AsyncClient sammen med asyncio.gather(), kan du gjøre flere anrop samtidig og vente på at alle skal fullføres, noe som reduserer ventetiden betraktelig:

python
import httpx import asyncio @router.get("/aggregate/{user_id}")
async def aggregate_user_data(user_id: int):
user_url =
f"https://api.example.com/users/{user_id}" stats_url = f"https://api.example.com/stats/{user_id}" rec_url = f"https://api.example.com/recs/{user_id}" async with httpx.AsyncClient() as client: user_future = client.get(user_url) stats_future = client.get(stats_url) rec_future = client.get(rec_url) user_resp, stats_resp, rec_resp = await asyncio.gather(user_future, stats_future, rec_future) return { "user": user_resp.json(), "stats": stats_resp.json(), "recommendations": rec_resp.json() }

I dette eksempelet sendes alle forespørslene samtidig, og når de er ferdige, samles resultatene i én operasjon.

Strømming av store data

Asynkrone operasjoner kan også benyttes for å håndtere store datamengder på en effektiv måte. Ved å bruke asynkrone generatorer kan du streame data til brukeren i små biter, noe som gjør at store filer kan lastes ned uten at serverens minne blir overbelastet.

python
import aiofiles
async def stream_large_file(file_path):
async with aiofiles.open(file_path, mode='rb') as file:
while chunk := await file.read(1024): # Les filen i 1 KB-biter
yield chunk

Ved å bruke denne teknikken kan serveren sende data kontinuerlig til brukeren, selv når andre forespørsler behandles samtidig.