Sådan bygger du et Flask-API med Python: Den komplette guide

Python-kildekode med syntaksfremhævning vist i to kolonner i en mørkt-tema kodeeditor.

Spørg fem udviklere, hvad Flask er, og du får fem selvsikre svar, hvoraf de fleste kun er halvt rigtige. Her er sagens kerne: Et Flask Python-projekt kan gå fra en tom mappe til et fungerende API på en eftermiddag, for derefter – uden bevidste valg omkring struktur, validering og deployment – stille og roligt at blive til det system, som ingen har lyst til at røre ved. Hurtigt at starte. Dyrt at forsømme.

Denne guide dækker begge sider af historien. Hvis du er udvikler, bygger vi et fungerende REST API sammen, trin for trin: CRUD-endpoints, en SQLite-database via SQLAlchemy, request-validering og fejlhåndtering, OpenAPI-dokumentation samt en projektstruktur, der kan holde til vækst.

Hvis du er CTO eller engineering lead og overvejer Python Flask kontra FastAPI, så spring direkte til afsnittene om produktionsparathed og hvordan tekniske ledere bør træffe beslutninger. De afsnit taler dit sprog: teamets ekspertise, leveringsrisiko og hvad vedligeholdelse reelt koster.

blue arrow to the left
Imaginary Cloud logo

Hvad er Flask, og hvorfor bruge det til at bygge API'er?

Flask er et letvægts-webframework til Python, der bygger API'er og webapplikationer ved at mappe HTTP-ruter til funktioner, som returnerer JSON eller HTML. Det er et WSGI-framework, og WSGI (Web Server Gateway Interface) er ganske enkelt standardforbindelsen mellem Python-webapplikationer og de webservere, der kører dem. Den officielle Flask-dokumentation kalder det et mikroframework, hvilket er både præcist og en smule misvisende på samme tid.

Mikro beskriver kernen, ikke ambitionen. Flask giver dig et chassis frem for en færdig bil: motoren, sæderne og instrumentbrættet er dine valg, plukket fra et reservedelskatalog, som communityet har opbygget siden 2010. Den frihed er hele tiltrækningskraften (og, som vi skal se, hele risikoen).

Python tilbyder adskillige frameworks: blandt andre Tornado, Pyramid, Django og FastAPI. Hvis du stadig overvejer selve sproget, forklarer vores oversigt over fordelene ved Python hvorfor det dominerer backend- og dataarbejde.

Er Flask stadig et mainstream-valg? I høj grad. I 2025 Stack Overflow Developer Surveyrapporterede omkring 15 % af respondenterne, at de bruger Flask, hvilket placerer det sammen med FastAPI blandt de mest anvendte webframeworks på tværs af alle sprog. Reservedelskataloget er lige så modent: Flask-SQLAlchemy til databaser, Flask-Login og Flask-JWT-Extended til autentificering, Flask-Migrate til skemamigreringer, Flask-Limiter til rate limiting. Hver især vedligeholdt gennem årevis.

Fra IC's audit-praksis: Flasks frihed er et tveægget sværd. Det gør de første uger af et Flask Python-projekt hurtige, og det er også den hyppigste årsag til, at vi bliver bedt om at rede trådene ud i en kodebase et år senere. Frameworket forhindrer ikke tre udviklere i at opfinde tre forskellige strukturer i den samme applikation. Det gør intet, undtagen teamet selv.
blue arrow to the left
Imaginary Cloud logo

Hvad er en REST API, og hvordan fungerer den?

API står for Application Programming Interface: måden, hvorpå ét system kommunikerer med et andet. En REST-API (Representational State Transfer) er en arkitektonisk stil for denne kommunikation, bygget på tilstandsløse forespørgsler via standard HTTP-metoder, hvor klient- og serveransvar holdes skarpt adskilt.

I praksis udveksler en REST-API JSON og mapper slutpunkter til fire verber: Create (POST), Read (GET), Update (PUT eller PATCH) og Delete (DELETE). Samlet set kaldes de CRUD-operationer, og den API, du bygger i denne guide, implementerer alle fire for en enkelt entitet. REST er dog ikke den eneste stil. Hvis dine tjenester er interne og følsomme over for latenstid, kan vores gRPC vs REST-sammenligning forklare, hvornår en binær protokol er det rette valg.

Fra IC's audit-praksis: det hyppigste problem, vi finder i REST-design, er ikke et manglende slutpunkt. Det er manglende konsistens: én rute returnerer snake_case, en anden camelCase; én pakker fejl ind i JSON, en anden serverer en rå HTML-side. At blive enige om disse konventioner, før det første slutpunkt oprettes, tager en time og sparer uger.
blue arrow to the left
Imaginary Cloud logo

Sådan opsætter du et Python Flask-projekt til et REST API

Ethvert Flask API kræver det samme fundament, før den første linje applikationskode skrives. Lad os få det på plads.

Tekniske krav

Du skal have Python installeret. Koden her forudsætter Python 3; hvis du bruger Windows eller har brug for Python 2, så følg Flask-installationsguiden.

Start med at oprette en mappe til projektet. Kør følgende kommandoer i din terminal på det sted, hvor projektet skal ligge:

mkdir flask_api
cd flask_api

Vi har nu oprettet projektmappen og navigeret ind i den. Før du installerer noget, skal du oprette et virtuelt miljø:

python3 -m venv venv

Dette opretter en mappe ved navn venv i dit projekt. Aktivér det ved at køre:

source venv/bin/activate

# On Windows:
venv\Scripts\activate

Fra nu af vil al Python, du kører, bruge venv-miljøet. Hvis du arbejder i et IDE, skal du pege det mod det samme miljø (en klassisk kilde til forvirring, når tingene virker i terminalen, men ikke i editoren).

Hvordan ved du, at det er aktivt? Tjek venstre side af konsollen: Hvis miljøets navn står i parentes, er du klar. For at deaktivere det senere, skal du køre:

deactivate

Arbejdsgangen for Flask API-udvikling

Et typisk Python Flask API bygges i seks trin:

  1. Opsætning af Flask-applikationen.
  2. Definition af API-ruter og endpoints.
  3. Forbindelse af applikationen til en database.
  4. Implementering af CRUD-operationer.
  5. Validering af forespørgsler og håndtering af fejl.
  6. Dokumentation af API'et ved hjælp af OpenAPI-værktøjer.

Denne guide følger denne rækkefølge, fordi vi gør det samme i vores kundeprojekter. Det holder backend-tjenester organiserede fra første commit, fremfor først ved den femtende refaktorering.

Flask API request flow diagram showing Client, Nginx, Gunicorn, validation, SQLAlchemy, database, and response path.
blue arrow to the left
Imaginary Cloud logo

Sådan forbinder du et Flask-API til en database med SQLAlchemy

Et API, der gemmer data i en Python-liste, glemmer alt, så snart serveren genstarter. Rigtige applikationer gemmer data permanent, så i dette afsnit forbinder vi Flask-API'et til en database ved hjælp af SQLite og SQLAlchemy.

SQLite er en letvægtsdatabase, der ikke kræver en separat server, hvilket gør den ideel til selvstudier og mindre applikationer. Perfekt til i dag. Ikke nødvendigvis til lanceringsdagen.

Fra IC's audit-praksis: næsten alle de Flask-projekter, vi overtager, startede med SQLite "bare lige for nu". Det er et fint udgangspunkt, forudsat at SQLAlchemy-modellerne er skrevet, som om PostgreSQL er på vej. For i produktion er det oftest tilfældet.

Installer SQLAlchemy og Flask-SQLAlchemy

Installer de nødvendige afhængigheder:

pip install Flask Flask-SQLAlchemy

Hvis du administrerer afhængigheder med en requirements.txt -fil, skal du tilføje:

Flask
Flask-SQLAlchemy

I dette setup er SQLAlchemy din Object Relational Mapper. En ORM (Object Relational Mapper) lader dig håndtere databaseposter som almindelige Python-objekter i stedet for at skrive rå SQL; SQLAlchemy-dokumentationen dækker hele sortimentet. Flask-SQLAlchemy forbinder det med Flask.

Konfigurer databaseforbindelsen

Peg Flask mod SQLite-databasen ved at definere forbindelsesstrengen:

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///items.db"
db = SQLAlchemy(app)

Dette opretter en lokal SQLite-databasefil med navnet items.db i projektmappen.

Definer databasemodellen

Opret en model, der repræsenterer strukturen af databasetabellen:

class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    price = db.Column(db.Float, nullable=False)

    def to_dict(self):
        return {"id": self.id, "name": self.name, "price": self.price}

Modellen Item definerer tre felter: id (unik identifikator), name og pris. to_dict() -metoden konverterer databaseobjektet til en JSON-serialiserbar ordbog.

Opret databasetabellen

Før du kører API'et, skal du oprette tabellerne ved hjælp af SQLAlchemy:

with app.app_context():
    db.create_all()

Hent alle elementer

Dette endpoint returnerer hvert element, der er gemt i databasen:

@app.route("/items", methods=["GET"])
def get_items():
    items = Item.query.all()
    return jsonify([item.to_dict() for item in items])

Opret et nyt element

Dette endpoint læser JSON-anmodningens brødtekst, validerer inputtet, opretter en databasepost og returnerer det oprettede element:

@app.route("/items", methods=["POST"])
def create_item():
    data = request.get_json()
    if not data or "name" not in data or "price" not in data:
        return jsonify({"error": "Both 'name' and 'price' are required."}), 400
    item = Item(name=data["name"], price=data["price"])
    db.session.add(item)
    db.session.commit()
    return jsonify(item.to_dict()), 201

Hent et enkelt element

Dette endpoint henter et specifikt element via dets ID:

@app.route("/items/<int:item_id>", methods=["GET"])
def get_item(item_id):
    item = Item.query.get_or_404(item_id)
    return jsonify(item.to_dict())

Hvis elementet ikke findes, returnerer Flask automatisk en 404-fejl. Ingen ekstra kode påkrævet.

Opdater et element

Dette endpoint opdaterer et eksisterende element. Kun de angivne felter bliver ændret.

@app.route("/items/<int:item_id>", methods=["PUT"])
def update_item(item_id):
    item = Item.query.get_or_404(item_id)
    data = request.get_json() or {}
    if "name" in data:
        item.name = data["name"]
    if "price" in data:
        item.price = data["price"]
    db.session.commit()
    return jsonify(item.to_dict())

Slet et element

Dette endpoint fjerner det angivne element fra databasen:

@app.route("/items/<int:item_id>", methods=["DELETE"])
def delete_item(item_id):
    item = Item.query.get_or_404(item_id)
    db.session.delete(item)
    db.session.commit()
    return jsonify({"message": f"Item {item_id} deleted."})

Kør applikationen

Start Flask-udviklingsserveren:

python app.py

# or, with the Flask CLI:
flask --app app run --debug

Din API er nu tilgængelig lokalt.

Eksempel på forespørgsel og svar

Opret et nyt element, forespørgslen:

curl -X POST http://localhost:5000/items \
  -H "Content-Type: application/json" \
  -d '{"name": "Keyboard", "price": 49.9}'

Svaret:

{
  "id": 1,
  "name": "Keyboard",
  "price": 49.9
}

Hent alle elementer, svaret:

[
  {
    "id": 1,
    "name": "Keyboard",
    "price": 49.9
  }
]

Hvorfor bruge en database i stedet for en liste i hukommelsen?

Fordi lister mister data ved genstart, kan ikke deles mellem applikationsinstanser og kan ikke forespørges effektivt. En database som SQLite giver din API persistent, søgbar lagring: den funktionalitet, som en rigtig backend-tjeneste kræver.

Til produktionssystemer skifter teams typisk til PostgreSQL, MySQL eller MongoDB. Den overordnede struktur for Flask-API'et forbliver den samme.

blue arrow to the left
Imaginary Cloud logo

Flask-projektstruktur: Fra en enkelt fil til et produktionslayout

Begynderstruktur: et simpelt Flask API-projekt

Betragt et API med én fil som en etværelses lejlighed. Alt er inden for rækkevidde, intet kræver en plantegning, og for et lille projekt er det præcis pointen:

flask_api/
├── app.py
├── models.py
├── routes.py
├── requirements.txt
└── items.db

I denne struktur app.py opretter Flask-applikationen og konfigurerer udvidelser, models.py definerer SQLAlchemy-modellerne, routes.py indeholder slutpunkterne, requirements.txt oplister afhængigheder, og items.db er udviklingsdatabasen.

Dette layout passer til små API'er, prototyper og læringsprojekter. Men ingen stifter familie i en etværelses lejlighed, og ingen bør lade et produkt vokse i én enkelt fil.

Eksempel: hvad hver fil indeholder

app.py opretter Flask-applikationen, konfigurerer databasen og registrerer ruterne.

from flask import Flask
from models import db
import routes

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///items.db"
db.init_app(app)

app.register_blueprint(routes.bp)

with app.app_context():
    db.create_all()

if __name__ == "__main__":
    app.run(debug=True)

models.py definerer databasemodellen.

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    price = db.Column(db.Float, nullable=False)

    def to_dict(self):
        return {"id": self.id, "name": self.name, "price": self.price}

routes.py indeholder API-slutpunkterne.

from flask import Blueprint, request, jsonify
from models import db, Item

bp = Blueprint("items", __name__)

@bp.route("/items", methods=["GET"])
def get_items():
    return jsonify([item.to_dict() for item in Item.query.all()])

# ... remaining CRUD endpoints as in snippets 11-14, using @bp.route

Ved at opdele applikationen i filer holdes ansvarsområderne adskilt, og rod minimeres. Hvis du senere tilføjer godkendelse, brugere eller ordrer, udvider du blot strukturen i stedet for at skulle skrive hele projektet om.

Produktionsstruktur: en modulær Flask API-arkitektur

Større Python Flask-applikationer kræver et hus frem for en lejlighed: pakker i stedet for filer på øverste niveau, med et dedikeret rum til hvert ansvarsområde.

flask_api/
├── app/
│   ├── __init__.py
│   ├── models/
│   │   └── item.py
│   ├── routes/
│   │   └── items.py
│   └── schemas/
│       └── item.py
├── requirements.txt
└── run.py

I denne arkitektur indeholder app-pakken den primære applikationslogik, models/ definerer databasemodeller, routes/ grupperer slutpunkter efter ressource, og run.py starter applikationen. schemas/ -mappen håndterer validering af forespørgsler og svar ved hjælp af Marshmallow, et Python-bibliotek til definition af skemaer, der validerer indgående data og serialiserer udgående svar.

Gevinsten er enkel. Slutpunkter forbliver organiseret efter ressource, funktioner som godkendelse eller baggrundsopgaver kan nemt tilføjes, og flere udviklere kan arbejde parallelt uden at komme i vejen for hinanden.

Best practice: start med den enkle struktur og udvid kun, når applikationen vokser. Til små projekter er et par tydeligt navngivne filer rigeligt. Til større Flask-API'er er det pakke-baserede layout det, der holder vedligeholdelsesomkostningerne nede.

Hvornår skal man bruge Blueprints

Et blueprint er et objekt, der minder meget om et Flask-applikationsobjekt, bortset fra at det udvider den eksisterende applikation i stedet for at oprette en ny. Blueprints er måden, du opdeler et Flask-API i sektioner på: efter ressource, API-version eller tjeneste.

Så hvornår har du egentlig brug for dem? Tidligere end du tror.

Fra IC's revisionspraksis: en enkelt fil med ruter begynder at akkumulere merge-konflikter, så snart en anden udvikler kommer til, eller en ny ressource opstår. Det er signalet til at skifte til Blueprints. Ikke først når filen "føles" for lang.

Lad os konvertere koden ovenfor til et blueprint og indlæse det i hovedapplikationen. Opret en ny mappe med navnet blueprints, og indeni den en mappe og en fil til items-blueprintet:

from flask import Blueprint, request, jsonify
from models import db, Item

items_bp = Blueprint("items", __name__, url_prefix="/items")

@items_bp.route("", methods=["GET"])
def get_items():
    return jsonify([item.to_dict() for item in Item.query.all()])

# ... remaining CRUD endpoints moved here unchanged

Nu skal app.py blot indlæse det oprettede blueprint og registrere det på applikationsobjektet:

from blueprints.items.routes import items_bp

app.register_blueprint(items_bp)

Samme endpoints, bedre struktur. Det er dette, der gør en voksende Flask Python-applikation overskuelig.

Sådan validerer du forespørgsler og håndterer fejl i et Flask-API

Validering er din API's dørmand. Den tjekker legitimationsoplysninger ved indgangen, så du ikke skal trække dårlige data ud af databasen senere, når de allerede har blandet sig med dine rapporter. Et veldesignet Flask-API validerer indgående data, returnerer klare fejlmeddelelser og bruger de korrekte HTTP-statuskoder.

Fra IC's revisionspraksis: i de produktionshændelser, vi bliver tilkaldt for at diagnosticere, er manglende validering af forespørgsler en af de hyppigste årsager. Dårlige data sniger sig ind og dukker først op uger senere som en rapporteringsfejl, der er dyr at spore tilbage til kilden.

Valider indgående forespørgselsdata

Når en klient sender data, bør serveren verificere, at de påkrævede felter er til stede og korrekt formateret. Oprettelse af et nyt element bør for eksempel kræve både et navn og en pris.

Her er et simpelt valideringseksempel:

@app.route("/items", methods=["POST"])
def create_item():
    data = request.get_json()
    if not data or "name" not in data or "price" not in data:
        return jsonify({"error": "Both 'name' and 'price' are required."}), 400
    item = Item(name=data["name"], price=data["price"])
    db.session.add(item)
    db.session.commit()
    return jsonify(item.to_dict()), 201

API'et tjekker, at anmodningens brødtekst indeholder JSON, og at de påkrævede felter er til stede. Hvis valideringen fejler, returneres et 400 Bad Request -svar. Dørmanden afviser ved døren, høfligt og i JSON.

Brug meningsfulde HTTP-statuskoder

Statuskoder fortæller klienter, om en anmodning lykkedes eller fejlede. De mest almindelige i REST-API'er er:

StatuskodeBetydning
200 OKAnmodningen lykkedes
201 CreatedEn ressource blev oprettet med succes
400 Bad RequestAnmodningen er ugyldig
404 Not FoundDen anmodede ressource findes ikke
500 Internal Server ErrorNoget uventet gik galt

Den rigtige kode lader API-brugere håndtere svar programmatisk i stedet for at skulle analysere fejltekst.

Håndter manglende ressourcer

Når en klient anmoder om en ressource, der ikke findes, bør API'et returnere en 404-fejl. Flask-SQLAlchemy tilbyder en praktisk hjælper:

@app.route("/items/<int:item_id>", methods=["GET"])
def get_item(item_id):
    item = Item.query.get_or_404(item_id)
    return jsonify(item.to_dict())

Hvis elementet ikke findes, returnerer Flask automatisk et svar som:

{
  "error": "Resource not found"
}

Ingen tomme svar, ingen vildledende svar.

Tilføj en global fejlhåndtering

I større applikationer bør du definere globale fejlhåndteringer, der returnerer ensartede svar ved almindelige fejl:

@app.errorhandler(400)
def bad_request(error):
    return jsonify({"error": "Bad request"}), 400

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "Resource not found"}), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({"error": "Internal server error"}), 500

Med disse håndteringer returnerer API'et altid struktureret JSON i stedet for standard HTML-fejlsider.

Eksempel på fejlrespons

Hvis en klient forsøger at oprette et element uden de påkrævede felter, kan API'et returnere:

{
  "error": "Both 'name' and 'price' are required."
}

Tydelige fejlmeddelelser gør det muligt for udviklere, der integrerer med dit API, at diagnosticere deres egne fejl uden at skulle læse din kildekode. Dit fremtidige jeg tæller i øvrigt også som en integrerende udvikler.

Hvorfor validering og fejlhåndtering er vigtigt

Sammen holder de ugyldige data ude af databasen, gør svar forudsigelige og lader integratorer fejlfinde hurtigt. Det er standardpraksis i moderne API-udvikling og et ufravigeligt krav i en Flask-tjeneste i produktion.

blue arrow to the left
Imaginary Cloud logo

Sådan genererer du OpenAPI-dokumentation til et Flask-API

Moderne API'er bør tilbyde maskinlæsbar dokumentation, så udviklere kan forstå endpoints, forespørgselsformater og svar uden at skulle læse koden. Den dominerende standard er OpenAPI, som beskriver HTTP-API'er i et format, der automatisk kan tolkes af værktøjer.

I Flask er den mest bekvemme vej flask-smorest, et bibliotek der forbinder Flask med OpenAPI 3, validering af forespørgsler og automatisk Swagger UI-dokumentation.

Installer flask-smorest og marshmallow

Installer de nødvendige pakker:

pip install flask-smorest marshmallow

Eller tilføj dem til din requirements.txt -fil:

flask-smorest
marshmallow

I dette setup flask-smorest genererer OpenAPI-dokumentationen og styrer API-routing, mens marshmallow (introduceret i afsnittet om struktur) håndterer validering af forespørgsler og serialisering af svar.

Konfigurer Flask til OpenAPI-dokumentation

Før du opretter endpoints, skal du konfigurere applikationen til at generere dokumentation:

from flask import Flask
from flask_smorest import Api

app = Flask(__name__)
app.config["API_TITLE"] = "Items API"
app.config["API_VERSION"] = "v1"
app.config["OPENAPI_VERSION"] = "3.0.3"
app.config["OPENAPI_URL_PREFIX"] = "/"
app.config["OPENAPI_SWAGGER_UI_PATH"] = "/swagger-ui"
app.config["OPENAPI_SWAGGER_UI_URL"] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist/"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///items_smorest.db"

Vigtige indstillinger inkluderer API_TITLE (navnet på dit API), API_VERSION, OPENAPI_VERSION (specifikationsversionen) og OPENAPI_SWAGGER_UI_PATH (hvor den interaktive dokumentation findes, for eksempel /swagger-ui).

Definer request- og response-skemaer

Med flask-smorest defineres formen på de data, dit API accepterer og returnerer, ved hjælp af Marshmallow-skemaer. Opret et, der beskriver et element:

from marshmallow import Schema, fields

class ItemSchema(Schema):
    id = fields.Int(dump_only=True)
    name = fields.Str(required=True)
    price = fields.Float(required=True)

Ét skema udfører fire opgaver: Det validerer indgående requests, dokumenterer det forventede format, serialiserer responses og genererer præcis OpenAPI-output. Det er derfor, vi anbefaler en skema-først-tilgang.

Opret dokumenterede API-endpoints

Opret endpoints ved hjælp af en Blueprint, gruppering af relaterede ruter:

from flask_smorest import Blueprint

blp = Blueprint("items", __name__, url_prefix="/items", description="Operations on items")

Hvert blueprint bliver til en sektion i den genererede dokumentation.

Implementer CRUD-endpoints med validering og dokumentation

Implementer nu endpoints til oprettelse og hentning af elementer:

@blp.route("/")
class ItemList(MethodView):
    @blp.response(200, ItemSchema(many=True))
    def get(self):
        return Item.query.all()

    @blp.arguments(ItemSchema)
    @blp.response(201, ItemSchema)
    def post(self, new_data):
        item = Item(**new_data)
        db.session.add(item)
        db.session.commit()
        return item

Her @blp.arguments validerer den indgående anmodnings brødtekst, @blp.response dokumenterer og serialiserer outputtet, og MethodView grupperer flere HTTP-metoder under én rute. Dekoratorerne opdaterer automatisk OpenAPI-dokumentationen.

Registrer blueprintet

Registrer blueprintet med API-instansen for at aktivere endpoints:

api.register_blueprint(blp)

Se den interaktive API-dokumentation

Når applikationen kører, skal du åbne Swagger UI-URL'en i din browser:

http://localhost:5000/swagger-ui

Det interaktive Swagger UI giver dig mulighed for at se alle endpoints, inspicere parametre, teste anmodninger direkte fra browseren og udforske skemaer. Da OpenAPI er en industristandard, driver den samme specifikation desuden klientgenerering og automatiserede API-testværktøjer.

blue arrow to the left
Imaginary Cloud logo

Sådan deployer du et Flask API til produktion

Kan den indbyggede Flask-server håndtere produktion? Nej. Den er kun til udvikling, og produktionsmiljøer kræver en WSGI-server og en webserver for at kunne håndtere reel trafik pålideligt.

Fra IC's audit-praksis: et tilbagevendende fund i vores gennemgange er Flask-udviklingsservere, der er sat i produktion, fordi det "virker". Det gør det også. Lige indtil der kommer samtidig trafik.

En almindelig deployment-opsætning kombinerer Gunicorn eller uWSGI som WSGI-server med Nginx som reverse proxy, hostet på en cloud- eller containerplatform såsom AWS, Azure eller Google Cloud. Gunicorn og uWSGI er WSGI-servere i produktionskvalitet: programmer, der kører flere kopier af din Python-applikation og håndterer indgående forespørgsler, hvilket er præcis den opgave, den enkelttrådede udviklingsserver aldrig var designet til.

Kør for eksempel en Flask-applikation i produktion ved hjælp af Gunicorn:

gunicorn -w 4 "app:app"

I denne kommando -w 4 starter fire worker-processer og app:app refererer til Flask-applikationsobjektet.

I moderne miljøer leveres Flask-API'er normalt som Docker-containere, hvilket forenkler skalering, miljøstyring og løbende udrulning. Hvis dit team bruger Kubernetes, gennemgår vores guide til opbygning af en Kubernetes-optimeret CI/CD-pipeline hvordan containeriserede API'er som denne bevæger sig fra commit til cluster.

blue arrow to the left
Imaginary Cloud logo

Sådan sikrer du et Flask-API med autentificering

De fleste produktions-API'er begrænser adgangen, så kun autoriserede brugere eller tjenester kan tilgå beskyttede slutpunkter. De gængse metoder er API-nøgler til simpel service-til-service-adgang, JWT (JSON Web Tokens) til brugerautentificering og OAuth 2.0 til tredjepartsintegrationer.

JWT-baseret autentificering er det mønster, vi oftest bruger i Flask-API'er. En bruger logger ind med sine legitimationsoplysninger, serveren udsteder et signeret token, og klienten præsenterer dette token ved hver anmodning i Authorization -headeren (headerens navn bevarer sin amerikanske stavemåde, fordi HTTP-standarden definerer det sådan).

Eksempel på header:

Authorization: Bearer <your-jwt-token>

Udvidelser som Flask-JWT-Extended håndterer token-håndtering og rutebeskyttelse, så du ikke selv skal stå for kryptografien. Med autorisation på plads kan kun tilladte klienter tilgå API'et, følsomme data forbliver beskyttede, og brugen kan overvåges pr. klient.

Fra IC's audit-praksis: vi betragter autentificering som en del af den indledende arkitektur, ikke som en sikkerhedsopgave til sidst. At eftermontere det på tværs af dusinvis af eksisterende slutpunkter er konsekvent dyrere end at designe det ind fra den første rute.
blue arrow to the left
Imaginary Cloud logo

IC's tjekliste til produktionsklare Flask-API'er

Selvstudier stopper normalt ved "API'et virker". Før vi hos Imaginary Cloud godkender et Flask-API til produktion, skal det bestå vores IC-tjekliste til produktionsklare løsninger – den samme gennemgang, som vi anvender i vores Tekniske og UX-audits.

Fem punkter på den tjekliste mangler i de fleste Flask-guides:

  1. Test: kan kildekoden ændres sikkert og videregives til et nyt team?
  2. Håndtering af hemmeligheder: er legitimationsoplysninger ude af koden og ude af versionsstyringen?
  3. Rate limiting: er API'et beskyttet mod misbrug og overbelastning fra gentagne forespørgsler?
  4. Logning og overvågning: når noget går i stykker, kan du så se hvad og hvorfor?
  5. CORS-konfiguration: kan de frontends, der skal bruge API'et, rent faktisk få adgang til det, og er andre udelukket?

Lad os gennemgå dem én efter én.

Sådan tester du et Flask-API

Flask leveres med en testklient, der kalder dine endpoints uden at køre en server, og den fungerer naturligt sammen med pytest. En minimal test ser således ud:

import pytest
from app import create_app, db

@pytest.fixture
def client():
    app = create_app(testing=True)
    with app.test_client() as client:
        with app.app_context():
            db.create_all()
        yield client

def test_create_item(client):
    response = client.post("/items", json={"name": "Keyboard", "price": 49.9})
    assert response.status_code == 201
    assert response.get_json()["name"] == "Keyboard"

Enhedstests dækker individuelle funktioner; integrationstests som den ovenfor gennemgår hele forespørgselscyklussen, fra routing via validering og database til serialisering. Kør begge dele i CI ved hvert commit mod en midlertidig database.

Sådan håndterer du konfiguration og hemmeligheder

Database-URL'er, JWT-signeringsnøgler og tredjepartsoplysninger må aldrig være hardcodede eller committet til versionsstyring. Læs dem i stedet fra miljøvariabler. Lokalt gør en .env fil indlæst med python-dotenv det nemt:

import os
from dotenv import load_dotenv

load_dotenv()
SQLALCHEMY_DATABASE_URI = os.environ["DATABASE_URL"]
JWT_SECRET_KEY = os.environ["JWT_SECRET_KEY"]

Tilføj .env til .gitignore. I produktion skal du indsætte de samme variabler via din platforms secret manager (AWS Secrets Manager, Azure Key Vault eller Kubernetes-hemmeligheder).

Sådan tilføjer du rate limiting til et Flask-API

Rate limiting beskytter API'et mod misbrug og mod velmenende klienter, der sidder fast i retry-loops. Flask-Limiter tilføjer det med få linjer:

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(get_remote_address, app=app, default_limits=["100 per minute"])

Grænser pr. rute kan derefter strammes for dyre slutpunkter. Login-ruter og søge-slutpunkter er de sædvanlige kandidater.

Sådan tilføjer du logging til et Flask-API i produktion

Når der opstår en hændelse i produktionen, er logs forskellen på en diagnose, der tager fem minutter, og en, der tager fem timer. Konfigurer Pythons standard logging -modul med struktureret output, og brug Flasks request hooks (en letvægtsform for middleware, der kører kode før og efter hver anmodning) til at tagge hver loglinje med et request-ID:

import logging, uuid
from flask import g, request

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s [%(request_id)s] %(message)s")

@app.before_request
def assign_request_id():
    g.request_id = request.headers.get("X-Request-ID", uuid.uuid4().hex)

Med request-ID'er på plads kan en fejlbehæftet anmodning spores på tværs af hver loglinje, den har genereret, og på tværs af tjenester, hvis downstream-kald videresender headeren. For fuld overvågelighed bør du sende logfilerne til en aggregator (CloudWatch, Grafana Loki eller en APM-platform som Sentry eller Datadog) i stedet for at lade dem ligge på serveren.

Sådan opsætter du CORS i et Flask-API

Hvis en browserbaseret frontend bruger dit API fra et andet domæne, blokerer browseren anmodningerne, indtil API'et sender de korrekte CORS-headere (Cross-Origin Resource Sharing). Flask-CORS håndterer dette:

from flask_cors import CORS

CORS(app, origins=["https://app.example.com"])

Angiv præcis de origins, dine frontends bruger. At bruge wildcard for origins med * er en almindelig genvej, og en almindelig fund ved sikkerhedsgennemgange, fordi det tillader enhver hjemmeside på internettet at kalde dit API fra brugernes browsere.

Fra IC's audit-praksis: af disse fem er manglende tests det punkt, der afgør, om en kodebase overhovedet kan overdrages til et nyt team. Et utestet Flask-API er ikke færdigt. Det kører bare.

Flask vs. FastAPI: Hvilket framework bør du vælge?

Flask er et af de mest udbredte Python-webframeworks til udvikling af API'er, men det er ikke den eneste mulighed. FastAPI er vokset hurtigt som et moderne framework, der er designet specifikt til højtydende API'er.

Stack Overflow Developer Survey 2025 sætter tal på denne udvikling: FastAPI og Flask bruges hver især af cirka 15 % af respondenterne, og FastAPIs stigning på fem procentpoint fra år til år var en af de største blandt alle webframeworks. Begge er klar til produktion. De er blot uenige om filosofien.

Sammenligningsmatrix for funktioner

Et detaljeret kig på de tekniske forskelle, der dækker værktøjer, arkitektur og udvikleroplevelse:

FunktionskategoriFlaskFastAPI
UdgivelseshistorikVeltableret (2010)Moderne (2018)
Asynkrone funktionerBegrænset supportNativ ASGI
DataintegritetKræver MarshmallowIndbygget (Pydantic)
SelvdokumenterendeKræver udvidelserAutomatisk OpenAPI
LæringskompleksitetLavModerat (type-hints)

To begreber i denne tabel fortjener en forklaring. ASGI (Asynchronous Server Gateway Interface) er efterfølgeren til WSGI, som gør det muligt for Python-applikationer at håndtere mange forespørgsler på én gang. Pydantic er et Python-bibliotek, der validerer og parser data ved hjælp af type-hints, og det driver FastAPIs indbyggede validering af forespørgsler.

Kort sagt: Flask giver dig en minimal, synkron kerne og lader dig sammensætte validering og dokumentation ved hjælp af udvidelser som Marshmallow og flask-smorest. FastAPI vender denne tilgang om. Asynkron håndtering, validering og OpenAPI-dokumentation er indbygget, på bekostning af et nyere økosystem og en indlæringskurve for type-hints.

Hvornår skal du bruge Flask

Flask passer godt, når du har brug for et framework, der tilpasser sig applikationen frem for omvendt: små og mellemstore API'er, mikrotjenester, backend-tjenester til webapplikationer og projekter, der kræver fuld kontrol over arkitekturen. Det er også det pragmatiske valg for teams, der allerede er fortrolige med Flask eller dets økosystem af udvidelser.

Hvornår du IKKE bør bruge Flask

For at være helt ærlig, så er der projekter, hvor vi ikke ville vælge Flask. Hvis din API er et nyt projekt, async-first og forventes at håndtere tusindvis af samtidige forbindelser (chat, streaming, højfrekvente integrationer), så gør FastAPIs indbyggede ASGI-model det samme ud af boksen, som Flask kræver workarounds for. Det samme gælder, hvis dit team er nyt i begge frameworks, og projektet læner sig op ad omfattende request-validering: Pydantics indbyggede tjek fjerner en arbejdsbyrde, som Flask ellers overlader til din egen disciplin.

Hvornår skal man bruge FastAPI

FastAPI er bygget specifikt til API'er og leveres med mere funktionalitet ud af boksen, som FastAPI-dokumentationen beskriver i detaljer. Det er det stærkere valg til højtydende og asynkrone API'er, kodebaser der benytter Pythons type hints, og teams der ønsker validering og interaktiv dokumentation genereret automatisk frem for at skulle sammensætte det via udvidelser.

Hvilket framework er bedst?

Det er ærligt talt det forkerte spørgsmål. Flask tilbyder maksimal fleksibilitet og seksten års modenhed i økosystemet; FastAPI tilbyder indbygget async samt indbygget validering og dokumentation. De afgørende faktorer er dit teams erfaring, projektets krav og de risikovurderinger, vi gennemgår herunder – de samme faktorer, der driver enhver beslutning om teknologistak.

Flask vs. FastAPI: Sådan bør CTO'er og tekniske ledere vælge

For CTO'er og tekniske ledere handler spørgsmålet om Flask vs. FastAPI sjældent om benchmarks. Det handler om risiko: ansættelsesrisiko, leveringsrisiko og de langsigtede omkostninger ved at vedligeholde det, dit team leverer i dette kvartal.

Flask er det valg, der indebærer mindst risiko, når dit team allerede kender det. Udviklere med mange års Flask-erfaring leverer hurtigere og begår færre arkitektoniske fejl, end de ville gøre i et framework, der skal læres under tidspres, og Flasks modne økosystem betyder færre ukendte faktorer, når systemet er i drift. Desuden trækker det på en af de største puljer af udviklere inden for Python-webudvikling (omkring hver syvende udvikler i 2025 Stack Overflow-undersøgelsen bruger det allerede), hvilket er vigtigt, når teamet skal vokse.

FastAPI reducerer en anden type risiko. Til async-tunge API'er med høj gennemstrømning fjerner den indbyggede validering og dokumentation hele kategorier af fejl, som Flask-teams er nødt til at forebygge gennem disciplin og udvidelser.

Afvejningen handler om tid til markedet nu kontra vedligeholdelsesomkostninger senere. I de kodebaser, vi auditerer, er de systemer, der er dyrest at vedligeholde, sjældent bygget på det "forkerte" framework: Det er dem, hvor hver udvikler validerede forespørgsler forskelligt, og hvor ingen skrev tests. Disse mangler akkumuleres som teknisk gæld uanset hvilket framework du har valgt.

Hvad koster det over tolv måneder? Mønsteret er konsekvent: Levering af nye funktioner går langsommere, da hver ændring kræver genindlæring af udokumenteret adfærd, onboarding af en ny udvikler strækker sig fra dage til uger, og det tager længere tid end nødvendigt at diagnosticere den første alvorlige hændelse. Det er her, og ikke i valget af framework, at en struktureret arkitekturgennemgang tjener sig selv hjem.

Vigtige pointer: Byg API'er med Flask

Kort fortalt: Flask er et letvægts Python-webframework, der har været i løbende udvikling siden 2010, og som bygger REST API'er ved at mappe HTTP-ruter til funktioner. Et produktionsklart Flask API tilføjer fem ting til den grundlæggende tutorial-version: database-persistens via SQLAlchemy, anmodningsvalidering med meningsfulde fejlmeddelelser, OpenAPI-dokumentation genereret med flask-smorest, en pakkebaseret projektstruktur ved hjælp af Blueprints samt deployment bag en produktions-WSGI-server som f.eks. Gunicorn.

FastAPI er det primære alternativ, som er hurtigere til asynkrone arbejdsbelastninger og selvdokumenterende fra start. Valget mellem dem bør baseres på teamets ekspertise og vedligeholdelsesomkostninger, ikke på benchmarks.

  • Flask er velegnet til REST API'er og mikrotjenester, hvor teamet ønsker fuld kontrol over arkitekturen.
  • Produktionsklarhed betyder persistens, validering, struktureret fejlhåndtering, tests, håndtering af hemmeligheder, rate limiting, logging og CORS – ikke bare fungerende endpoints.
  • SQLAlchemy abstraherer databaselaget, så et projekt kan starte på SQLite og flytte til PostgreSQL uden at skulle omstruktureres.
  • flask-smorest genererer OpenAPI-dokumentation ud fra de samme Marshmallow-skemaer, som validerer anmodninger.
  • Beslutningen mellem Flask og FastAPI er et spørgsmål om risiko og teamets ekspertise for tekniske ledere, ikke en benchmark-konkurrence.

Hvis du kun skal huske én ting, så lad det være denne: Frameworks skaber ikke vedligeholdelsesomkostninger. Det gør vaner.

Ofte stillede spørgsmål

Hvad er en Flask API?

En Flask API er en RESTful webtjeneste bygget med Flask-frameworket i Python. Den eksponerer HTTP-endepunkter, som klienter kalder, og udveksler typisk data i JSON-format.

Kan jeg bruge Flask som backend til min applikation?

Ja. Flask fungerer som backend for API'er, der forsyner frontend-applikationer, mobilapps eller tredjepartstjenester. Udvidelser håndterer de understøttende funktioner: Flask-SQLAlchemy til data, Flask-JWT-Extended til autentificering og Flask-Limiter til hastighedsbegrænsning.

Hvad er forskellen på Flask og REST?

Flask er et web-framework; REST er en arkitektonisk stil til design af netværksapplikationer. Du bruger Flask til at implementere REST API'er: Frameworket leverer routing og forespørgselshåndtering, mens REST leverer designkonventionerne.

Er Python Flask god til at bygge API'er?

Ja, af en specifik årsag: Dens lille kerne betyder, at API'en kun indeholder det, du selv tilføjer. Bagsiden af medaljen er, at validering, dokumentation og struktur er dit ansvar, og denne guide dækker, hvordan du tilføjer hver af disse dele.

Er Flask stadig relevant i 2026?

Ja. Omkring 15 % af respondenterne i 2025 Stack Overflow Developer Survey bruger Flask, hvilket er på niveau med FastAPI, og dens økosystem af udvidelser har haft seksten års kontinuerlig vedligeholdelse. Det, der har ændret sig, er standardvalget til nye, asynkront tunge API'er, hvor FastAPI nu er det mest almindelige valg.

Skal jeg bruge Flask eller FastAPI til et nyt projekt i 2026?

Vælg Flask, hvis dit team allerede kender det, hvis API'en ikke er asynkront tung, eller hvis du udvider et eksisterende Flask-system. Vælg FastAPI til nye projekter, systemer med høj gennemstrømning eller asynkront-første API'er, hvor indbygget validering og dokumentation reducerer leveringsrisikoen. Den fulde analyse af fordele og ulemper findes i afsnittet for tekniske ledere ovenfor.

Hvor hurtig er Flask sammenlignet med FastAPI?

Ved synkrone CRUD-arbejdsbelastninger bag en korrekt konfigureret WSGI-server er det sjældent frameworket, der er flaskehalsen. Det er som regel databasen. FastAPI trækker klart fra ved I/O-bundne, samtidige arbejdsbelastninger, hvor dens native ASGI-model håndterer mange samtidige forespørgsler, som hver især ville optage en Flask-worker.

Flask vs. Django: Hvad er bedst?

De løser forskellige problemer. Flask er et mikro-framework, der starter minimalistisk og udvides efter behov; Django leveres med indbygget ORM, administrationsgrænseflade og godkendelse. Vælg Flask til API'er og mikrotjenester, hvor du ønsker kontrol; vælg Django, når du vil have alt inkluderet og kan acceptere dens konventioner.

Kan Flask bruges i produktion?

Ja, med den rette server. Den indbyggede udviklingsserver er ikke sikker til produktion, så ved deployment kører man en WSGI-server som Gunicorn eller uWSGI bag en reverse proxy som Nginx, typisk inde i Docker-containere. Konfigureret på den måde håndterer Flask produktionsbelastninger pålideligt.

Hvordan deployer man et Flask API?

Med en WSGI-server og en reverse proxy. De grundlæggende trin er:

  1. Pak applikationen og installer afhængigheder.
  2. Kør den med en WSGI-produktionsserver som f.eks. Gunicorn.
  3. Placer en reverse proxy som Nginx foran til at håndtere indgående trafik.
  4. Host den på en cloud-server eller en containerplatform.

En typisk Gunicorn-kommando ser således ud:

gunicorn -w 4 "app:app"

I moderne infrastruktur leveres hele denne stack som en Docker-container på AWS, Azure eller Google Cloud.

Hvordan aktiverer jeg CORS i et Flask API?

Installer Flask-CORS-udvidelsen og registrer den i din app med de præcise origins, som dine frontends benytter. Undgå at bruge wildcard for origins med * i produktion, da det gør det muligt for enhver hjemmeside at kalde dit API fra de besøgendes browsere. Sektionen om produktionsklargøring ovenfor viser opsætningen på to linjer.

Hvad er forskellen på Flask og Django REST Framework?

Forskellen ligger i mængden af indbygget struktur. Flask er et letvægts-mikroframework, hvor du selv vælger biblioteker og arkitektur; Django REST Framework er et fuldt udstyret lag oven på Django, der inkluderer autentificering, serialisering, rettighedsstyring og API-views.

Generelt egner Flask sig bedst til små og mellemstore API'er, mikrotjenester og specialtilpassede arkitekturer, mens Django REST Framework er velegnet til større applikationer, der drager fordel af Djangos integrerede økosystem og konventioner.

Hvis dit team skal beslutte, hvordan et nyt API skal struktureres, eller hvis I er ved at flytte en eksisterende tjeneste til produktion, kan Imaginary Cloud gennemgå jeres arkitektur og finde den rette tilgang til jeres behov og team. Kontakt os for at starte dialogen.

Build scalable products with Web and Mobile Development call to action
Pedro Martinho
Pedro Martinho

Associeret udvikler, der arbejder mest med Backend-teknologier. En iværksætter med datavidenskabsinteresse. Kærlighed til sport, lydbøger, kaffe og memes!

Read more posts by this author
Tiago Franco
Tiago Franco

CEO @ Imaginary Cloud og medforfatter af bogen Product Design Process. Jeg nyder mad, vin og Krav Maga (ikke nødvendigvis i denne rækkefølge).

Read more posts by this author
Alexandra Mendes
Alexandra Mendes

Alexandra Mendes er Senior Growth Specialist hos Imaginary Cloud med 3+ års erfaring med at skrive om softwareudvikling, AI og digital transformation. Efter at have gennemført et frontend-udviklingskursus fik Alexandra nogle praktiske kodningsevner og arbejder nu tæt sammen med tekniske teams. Alexandra brænder for, hvordan nye teknologier former erhvervslivet og samfundet, og hun nyder at omdanne komplekse emner til klart og nyttigt indhold for beslutningstagere.

LinkedIn

Read more posts by this author

People who read this post, also found these interesting:

Dropdown caret icon