Zo zet je een REST API op jouw data warehouse

Data wordt waardevoller naarmate je er meer mee kunt doen. Wil je het potentieel van jouw data warehouse vergroten, dan helpt het als andere systemen op een eenvoudige manier toegang kunnen krijgen tot die data.
Businessman,Using,Laptop,With,Api,Icon,On,A,Virtual,Screen
Inhoud

Data wordt waardevoller naarmate je er meer mee kunt doen. Wil je het potentieel van jouw data warehouse vergroten, dan helpt het als andere systemen op een eenvoudige manier toegang kunnen krijgen tot die data. Veel moderne systemen beschikken over een REST-client waarmee gegevens via het HTTP-protocol opgevraagd kunnen worden. Door jouw data warehouse via een REST API beschikbaar te stellen, wordt het in één klap breder inzetbaar.

In dit artikel leer je hoe je relatief eenvoudig een REST API toevoegt aan een bestaand data warehouse. Het artikel is gericht op technisch onderlegde lezers. Hoewel het onderwerp niet direct over BI-visualisaties of analyses gaat, draagt het wél bij aan de waarde en bruikbaarheid van de BI-omgeving binnen jouw organisatie.

🏞️Scenario

BI-landschappen verschillen sterk per organisatie en maken gebruik van uiteenlopende technologieën. Om het artikel concreet en praktisch te houden, kiezen we voor een eenvoudig scenario dat vaak voorkomt. De nadruk ligt op de API zelf, maar de principes zijn breed toepasbaar, ook op omgevingen met cloud databases, Linux-servers of managed hosting voor webapplicaties. Kom je er binnen jouw eigen landschap niet uit, neem dan gerust contact met ons op.

Het voorbeeldlandschap

We werken met het volgende uitgangspunt:

  • 💻 Platform: Microsoft SQL Server op een Windows-machine
  • 🔐 Netwerk: De machine bevindt zich in een intern netwerk zonder directe toegang vanaf het internet
  • 🗃️ Data warehouse: Bevat feit- en dimensietabellen met bedrijfsdata
  • 🎯 Doelsysteem: Een klantportaal dat specifieke gegevens nodig heeft
  • 🔧 API-doel: Alleen toegang bieden tot relevante data voor dit klantportaal
  • 🔐 Voordeel: Fijnmazige autorisatie en heldere scheiding tussen technische en functionele logica

In plaats van één generieke API voor het hele data warehouse, bouwen we dus een gerichte API voor een specifieke toepassing.

⚙️Voorbereiding

De te implementeren API levert JSON documenten over HTTP op basis van GET requests. We gebruiken hiervoor het open source framework FastAPI omdat het – in de woorden van de makers – high performance, easy to learn, fast to code, ready for production is. FastAPI is een Python framework. Omdat het lichtgewicht is, kan het doorgaans prima op dezelfde machine als het data warehouse ge-host worden. Mocht het gebruik van de API in productie toch teveel resources vragen, dan is hij eenvoudig naar een andere machine verplaatsbaar.

1. Installeer Python

  1. Download de meest recente versie van Python vanaf:
    👉 https://www.python.org/downloads/

Gebruik niet de versie uit de Microsoft Store. Deze veroorzaakt vaak problemen bij package-installaties.

  1. Tijdens de installatie:

✅ Vink aan: Add Python to PATH

📦 Kies bij voorkeur een versie vanaf Python 3.10 of hoger. Dit artikel gebruikt Python 3.13.

2. Maak een virtuele omgeving

Het is goed gebruik om Python-oplossingen altijd in hun eigen geïsoleerde virtuele omgeving te plaatsen. Dit maakt package magement eenvoudig en zorgt ervoor dat de oplossing later verplaatsbaar is naar een andere machine.

Voor de bestanden van de API en de virtuele Python-omgeving maak je een directory op een goede plek, bijvoorbeeld C:\DWH_API_Klantportaal.

Open een PowerShell-venster en navigeer naar die directory. Maak hier een virtuele python omgeving.

PS C:\DWH_API_Klantportaal> python -m venv .venv

Activeer vervolgens deze omgeving.

PS C:\DWH_API_Klantportaal> .\.venv\Scripts\Activate.ps1

Je ziet nu (.venv) voor je prompt verschijnen. Dit betekent dat je geïsoleerde Python-omgeving actief is.

3. Installeer de benodigde pakketten

Installeer FastAPI en de overige libraries die je nodig hebt om een API te draaien en verbinding te maken met SQL Server:

(.venv) PS C:\DWH_API_Klantportaal> pip install fastapi uvicorn pyodbc starlette

Deze bibliotheken zijn:

PakketFunctie
fastapiWebframework voor het bouwen van de API
uvicornASGI-server om de API uit te voeren
pyodbcVerbindingslaag met SQL Server via ODBC
starletteOnderliggende middleware voor FastAPI

Je Python-omgeving is nu klaar voor gebruik!

🚀Jouw eerste API-endpoint

Nu je Python-omgeving klaar is, kun je beginnen met het schrijven van je eerste API. FastAPI definieer je in een Python-bestand. Dit bestand bewerk je het liefst in een geschikte editor zoals Visual Studio Code (VS Code), zodat je gebruik kunt maken van handige functies zoals syntax highlighting, autocompletion en debugging. VS Code kun je hier downloaden.

1. Open VS Code

Als je code als commandoregelprogramma beschikbaar hebt (via je %PATH%), kun je de editor starten vanuit de terminal:

(.venv) PS C:\DWH_API_Klantportaal> code . -g main.py

  • Hiermee open je de map in VS Code én wordt een nieuw bestand main.py aangemaakt.
  • Werkt dit niet? Start dan VS Code via Start > Visual Studio Code, open handmatig de juiste map, en maak zelf een bestand aan met de naam main.py

2. Plak het volgende script in main.py

import pyodbc, os
from fastapi import FastAPI, HTTPException, Query
from typing import Optional, List, Dict, Any

app = FastAPI()

# Pas deze verbindingsstring aan jouw situatie aan:
conn_str = (
    "DRIVER={ODBC Driver 17 for SQL Server};"
    "SERVER=localhost;"
    "DATABASE=WideWorldImportersDW;"
    "Trusted_Connection=yes;"
)

@app.get("/items") def get_items(): conn = pyodbc.connect( conn_str ) cursor = conn.cursor() cursor.execute("SELECT ItemID, Name FROM [dwh].[Item]") rows = cursor.fetchall() return [ {"id": r.ItemID, "name": r.Name } for r in rows ]

🔍 Wat doet dit script?

  • Definieert een GET-endpoint op /items
  • Maakt verbinding met SQL Server via ODBC
  • Voert een eenvoudige SELECT uit op de tabel [dwh].[Item]
  • Geeft de resultaten terug als een JSON-lijst van objecten met id en name

3 .Start de API

Ga terug naar je terminal en start de API met:

(.venv) PS C:\DWH_API_Klantportaal> uvicorn main:app --reload --host 0.0.0.0 --port 8000

📌 Wat betekent dit?

  • main:app verwijst naar de app variabele in main.py
  • --reload zorgt dat wijzigingen automatisch worden doorgevoerd (handig tijdens ontwikkeling)
  • --host 0.0.0.0 maakt de API bereikbaar vanaf andere apparaten in het netwerk
  • --port 8000 stelt de poort in waarop de API bereikbaar is

Je zou nu deze melding moeten zien:.

INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

4. Test het endpoint

Open een browser en ga naar:

http://localhost:8000/items 

Als alles goed is gegaan, zie je een JSON-resultaat zoals dit:

[
{
"id": 0,
"name": "Unknown"
},
{
"id": 1,
"name": "Void fill 400 L bag (White) 400L"
},
{
"id": 2,
"name": "Void fill 300 L bag (White) 300L"
},
etc ...

Gefeliciteerd, je eerste API-endpoint werkt! 🎉

🛠️ Opmerking: Zorg ervoor dat de SQL-gebruiker waarmee je de API uitvoert toegang heeft tot de gebruikte tabel, en dat de juiste ODBC-driver is geïnstalleerd (bijv. versie 17 of 18 van Microsoft).

🧱 Een onderhoudbare API

Het eerste /items-endpoint is een goede start maar naarmate je meer endpoints toevoegt of grotere datavolumes verwerkt, is het slim om je code beter te structureren. Zo wordt onderhoud eenvoudiger en kun je logica hergebruiken.

✨ Wat verbeteren we?

  • ✅ We verplaatsen de querylogica naar aparte functies
  • ✅ We voegen foutafhandeling toe
  • ✅ We implementeren paginering voor grotere datasets (bijv. /orders)
  • ✅ We geven bij bulkresultaten ook het totaal aantal rijen terug

🔧 Stap 1: Herbruikbare functies

Voeg bovenin je main.py de volgende functies toe, boven de endpoint-definities:

def query_sql(sql: str):
    try:
        with pyodbc.connect(conn_str) as conn:
            with conn.cursor() as cursor:
                cursor.execute(sql)
                columns = [column[0] for column in cursor.description]
                results = [dict(zip(columns, row)) for row in cursor.fetchall()]
                return results
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

def get_total_count( table: str ) -> int:
    try:
        with pyodbc.connect(conn_str) as conn:
            with conn.cursor() as cursor:
                cursor.execute(f"SELECT COUNT(*) FROM {table}")
                return cursor.fetchone()[0]
    except Exception as e:
        raise HTTPException( status_code = 500, detail = str(e) )

Deze functies:

  • Maken verbinding met SQL Server
  • Voeren de query uit
  • Mappen de resultaten naar een lijst van dictionaries (JSON-compatibel)
  • Vangen fouten netjes af en geven een HTTP 500 terug

🧩 Stap 2: Items-endpoint herschrijven

Gebruik de nieuwe functie in een compactere, schonere versie van /items:

@app.get( "/items", response_model = List[Dict] )
def get_items():
    sql = "SELECT * FROM dwh.Item"
    return query_sql( sql )

📄 Stap 3: Orders met paginering

Voor grotere datasets is paginering essentieel. Zo voorkom je dat clients duizenden rijen tegelijk moeten verwerken. Voeg dit endpoint toe:

@app.get( "/orders", response_model = Dict )
def get_orders( skip: int = Query( 0, ge = 0 ), limit: int = Query( 100, ge = 1, le = 1000 ) ):
    total = get_total_count("dwh.Orders")
    sql = f"""
        SELECT * FROM dwh.Orders
        ORDER BY [Order Date Key] DESC
        OFFSET {skip} ROWS
        FETCH NEXT {limit} ROWS ONLY
    """
    items = query_sql( sql )
    return {
        "total": total,
        "skip": skip,
        "limit": limit,
        "items": items
    }

Met dit endpoint:

  • Haal je maximaal 1000 orders tegelijk op
  • Geef je via skip en limit aan welk “pagina”-deel je opvraagt
  • Geef je naast de resultaten ook het totaal aantal beschikbare rijen terug

Voorbeeld:

http://localhost:8000/orders?limit=1000&skip=3000

📝 Best practices

  • Zet complexe filtering of businesslogica liever in SQL views dan in Python-code. Zo blijft de API generiek en de logica beheersbaar in het data warehouse.
  • Maak endpoints eenvoudig en voorspelbaar — met duidelijke parameters, consistente namen en logische responsstructuren.
Resultaat 1

🔎Filtering

Een veelvoorkomende functionele eis is dat een applicatie alleen data wil zien die voor haar relevant is. In dit geval: een klantportaal dat alleen de orders van één specifieke klant mag opvragen. Hiervoor voeg je een nieuw endpoint toe met een path parameter in plaats van een query parameter.

📥 Endpoint: orders per klant

Voeg het volgende toe aan main.py:

@app.get( "/customer/{customer_id}/orders", response_model = List[Dict] )
def get_orders_by_customer( customer_id: int ):
    sql = f"SELECT * FROM dwh.Orders WHERE [Customer Key] = {customer_id}"
    return query_sql( sql )
  • customer_id is hier onderdeel van de URL zelf (een zogeheten path parameter).
  • Deze waarde wordt direct gebruikt in de SQL-query.

Voorbeeld:

http://localhost:8000/customer/50/orders.

…haalt alle orders op voor klant met ID 50.

⚠️ Veiligheidstip

Het gebruik van f"SELECT ... {value}" is vatbaar voor SQL-injectie als je geen controle hebt over de input. Voor extra veiligheid (zeker bij tekstvelden), gebruik je parameterbinding met ?

  1. Pas je functie query_sql aan zodat die optioneel parameters accepteert:
from typing import Optional, List, Any

def query_sql(sql: str, params: Optional[List[Any]] = None):
    try:
        with pyodbc.connect(conn_str) as conn:
            with conn.cursor() as cursor:
                if params:
                    cursor.execute(sql, params)
                else:
                    cursor.execute(sql)
                columns = [column[0] for column in cursor.description]
                results = [dict(zip(columns, row)) for row in cursor.fetchall()]
                return results
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
  1. In je endpoint stuur je nu de SQL met placeholders (?) mee, en een lijst met parameters:
@app.get("/customer/{customer_id}/orders", response_model=List[Dict])
def get_orders_by_customer(customer_id: int):
    sql = "SELECT * FROM dwh.Orders WHERE [Customer Key] = ?"
    return query_sql(sql, [customer_id])

Waarom werkt dit?

  • PyODBC (net als de meeste database-API’s in Python) ondersteunt parameter binding via ? placeholders in de SQL-string.
  • Parameters worden los van de query verstuurd, waardoor SQL-injectie onmogelijk is.
  • Door de generieke functie aan te passen met een optionele params parameter, kun je voor eenvoudige queries zonder parameters nog steeds dezelfde functie gebruiken.

🧼 Extra filtering: alleen open orders

Stel dat je alléén openstaande orders wilt ophalen. Dan kun je een tweede gefilterd endpoint maken:

@app.get("/customer/{customer_id}/open_orders", response_model=List[Dict])
def get_open_orders_by_customer(customer_id: int):
    sql = f"""
        SELECT * FROM dwh.Orders
        WHERE [Customer Key] = {customer_id} AND [Order Status] = 'open'
    """
    return query_sql(sql)

🎯 Best practice

Definieer een view in het data warehouse met alleen de open orders per klant, en roep die aan vanuit de API. Zo blijft alle logica op één centrale plek beheerd.

📝 Let op datatype van klantnummers 

De voorbeelden hierboven gaan ervan uit dat het klantnummer een int is. In jouw eigen data warehouse kan dat ook een varcharguid, of iets anders zijn. Pas dan de type-aanduiding customer_id: int aan naar het juiste type, bijvoorbeeld str.

⚙️Endpoints genereren

Als je data warehouse veel tabellen bevat en je wilt niet handmatig voor elke tabel een endpoint schrijven, kun je dit proces automatiseren. Met een slimme SQL-query op de system catalogus van SQL Server genereer je Python-code die je direct in je API kunt plakken.

🎯 Doel

  • Snel API-endpoints maken voor alle tabellen in bepaalde schema’s (bijvoorbeeld Fact en Dimension)
  • Ondersteuning voor endpoints die zoeken op primary key (detail-endpoints)
  • Endpoints met paginering voor bulkdata

📋 Query 1: Endpoints per primary key

Deze query genereert een FastAPI endpoint per tabel, dat een record ophaalt op basis van de primaire sleutel (uitgaande van één kolom als primary key).

SELECT '@app.get( "/' + REPLACE( KCU.TABLE_NAME, ' ', '' ) + '/{' + REPLACE( KCU.TABLE_NAME, ' ', '' ) + '_id}", response_model = List[Dict] )
def get_' + REPLACE( KCU.TABLE_NAME, ' ', '' ) + '_by_id( ' + REPLACE( KCU.TABLE_NAME, ' ', '' ) + '_id: ' + case C.DATA_TYPE WHEN 'int' then 'int' WHEN 'bigint' then 'int' WHEN 'varchar' then 'str' WHEN 'date' then 'str' END + ' ):
    sql = f"SELECT * FROM [' + KCU.TABLE_CATALOG + '].[' + KCU.TABLE_SCHEMA + '].[' + KCU.TABLE_NAME + '] WHERE [' + KCU.COLUMN_NAME + '] = {' + REPLACE( KCU.TABLE_NAME, ' ', '' ) + '_id}"
    return query_sql( sql )

'
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC ON KCU.CONSTRAINT_NAME = TC.[CONSTRAINT_NAME] AND KCU.CONSTRAINT_SCHEMA = TC.[CONSTRAINT_SCHEMA] AND TC.[CONSTRAINT_TYPE] = 'PRIMARY KEY'
JOIN INFORMATION_SCHEMA.COLUMNS C ON KCU.TABLE_CATALOG = C.TABLE_CATALOG AND KCU.TABLE_SCHEMA = C.TABLE_SCHEMA AND KCU.TABLE_NAME = C.TABLE_NAME AND KCU.COLUMN_NAME = C.COLUMN_NAME
WHERE KCU.TABLE_SCHEMA IN ('Fact','Dimension') AND KCU.ORDINAL_POSITION = 1

 

📋 Query 2: Bulk endpoints met paginering

Deze query genereert endpoints die data per tabel pagineren, handig voor tabellen met veel records.

SELECT '@app.get( "/all_' + REPLACE( KCU.TABLE_NAME, ' ', '' ) + '", response_model = Dict )
def get_all_' + REPLACE( KCU.TABLE_NAME, ' ', '' ) + '( skip: int = Query( 0, ge = 0 ), limit: int = Query( 100, ge = 1, le = 1000 ) ):
    total = get_total_count("[' + KCU.TABLE_CATALOG + '].[' + KCU.TABLE_SCHEMA + '].[' + KCU.TABLE_NAME + ']")
    sql = f"""
        SELECT * FROM [' + KCU.TABLE_CATALOG + '].[' + KCU.TABLE_SCHEMA + '].[' + KCU.TABLE_NAME  + ']
		ORDER BY [' +  KCU.COLUMN_NAME + ']
        OFFSET {skip} ROWS
        FETCH NEXT {limit} ROWS ONLY
    """
    items = query_sql( sql )
    return { "total": total, "skip": skip, "limit": limit, "values": items }

'
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC ON KCU.CONSTRAINT_NAME = TC.[CONSTRAINT_NAME] AND KCU.CONSTRAINT_SCHEMA = TC.[CONSTRAINT_SCHEMA] AND TC.[CONSTRAINT_TYPE] = 'PRIMARY KEY'
JOIN INFORMATION_SCHEMA.COLUMNS C ON KCU.TABLE_CATALOG = C.TABLE_CATALOG AND KCU.TABLE_SCHEMA = C.TABLE_SCHEMA AND KCU.TABLE_NAME = C.TABLE_NAME AND KCU.COLUMN_NAME = C.COLUMN_NAME
WHERE KCU.TABLE_SCHEMA IN ('Fact','Dimension') AND KCU.ORDINAL_POSITION = 1

⚠️ Let op

  • Deze queries gaan ervan uit dat alle tabellen een enkele kolom als primaire sleutel hebben (geen samengestelde keys).
  • Pas eventueel de schema-namen (FactDimension) aan naar jouw data warehouse omgeving.
  • Plak de output van deze queries in je main.py onder je bestaande code.

💡 Tip

Wil je de gegenereerde endpoints combineren met de parameter-gebonden veilige query_sql() functie, pas dan de gegenereerde SQL-strings aan met ? en stuur parameters mee, zoals eerder besproken.

🧬 Goed om te weten: Datatype-conversie tussen SQL en JSON

Wanneer je data uit je SQL-database via FastAPI naar JSON exposeert, vindt er automatisch een conversie plaats van SQL-datatypen naar JSON-compatibele formaten. Dit gebeurt doordat de database driver (zoals pyodbc) de SQL-resultaten omzet naar Python-typen, waarna FastAPI deze Python-typen naar JSON serialiseren.

De meest voorkomende SQL-typen zoals integers, strings en booleans worden rechtstreeks vertaald naar respectievelijk JSON-nummers, strings en booleans. Datums en tijden worden automatisch omgezet naar ISO 8601-geformatteerde strings, zodat ze in JSON leesbaar en gestandaardiseerd zijn. Null-waarden in de database verschijnen als null in JSON.

Voor complexere of speciale datatypes, zoals binaire data of geografische typen, kan het nodig zijn om zelf conversies of validaties te implementeren. Op die manier houd je de controle over hoe data in JSON verschijnt en zorg je voor consistente en correcte API-responses.

SQL datatypePython datatypeJSON datatypeOpmerking
intbigintintNumber (integer)Direct omgezet
floatdecimalfloat / DecimalNumber (float)Decimal wordt meestal float
varchartextstrStringDirect omgezet
bitboolBooleanWordt true/false
datetimedatedatetime.datetime / datetime.dateString (ISO 8601)Wordt ISO 8601 string (bv. "2025-07-02T10:30:00")
timedatetime.timeString (HH:MM:SS)Wordt string
NULLNonenullJSON null

🔐Een veilige(re) API met een API-Key

Zonder beveiliging kan iedereen met netwerktoegang je API gebruiken. Dat is meestal niet wenselijk, ook niet binnen een intern netwerk. Daarom voegen we een eenvoudige, centrale API-key authenticatie toe.

🛠️ Voorbereiding: API-key instellen

  1. Genereer een lange, sterke API-key.

Bijvoorbeeld via een online generator of via PowerShell:

  1. Zet deze API-key als environment variabele op de machine waar de API draait, bijvoorbeeld:

(.venv) PS C:\DWH_API_Klantportaal> $env:API_KEY = "jouw-lange-api-key-hier"

Bovenstaand commando maakt de variabele beschikbaar in de huidige PowerShell-sessie. Zorg er ook voor dat dit een globale systeemvariabele is zodat hij na een reboot bewaard blijft.

🧩 Code-aanpassingen

Voeg bovenaan je main.py de volgende regels toe:

import os

API_KEY = os.getenv("DWH_API_KEY") if not API_KEY: raise RuntimeError("DWH_API_KEY environment variable niet gevonden. Zorg ervoor dat deze is ingesteld.")

🚧 Middleware toevoegen voor API-key validatie

Implementeer een Starlette middleware die elk binnenkomend verzoek controleert op de juiste API-key in de header X-API-Key:

from fastapi.responses import Response
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request

class APIKeyMiddleware( BaseHTTPMiddleware ): async def dispatch( self, request: Request, call_next ): if request.headers.get( "X-API-Key" ) != API_KEY: return Response( status_code = 403, content = "Forbidden" ) return await call_next( request )
app = FastAPI() app.add_middleware( APIKeyMiddleware )

🔑 Gebruik van de API

  • Verplicht: Voeg bij elk verzoek de header X-API-Key toe met de juiste sleutel.
  • Voor testen kun je Postman gebruiken of browsers zoals Firefox met devtools, of commandline tools zoals curl:

curl -H "X-API-Key: jouw-lange-api-key-hier" http://localhost:8000/items

⚙️Naar productie

de API als Windows-service draaien

Als je API op een Windows-machine draait, wil je dat deze automatisch start bij het opstarten van de server en altijd blijft draaien, ook na crashes of herstarts. Dit regel je het eenvoudigst door de API als een Windows-service te installeren.

🛠️ Wat heb je nodig?

  • nssm (Non-Sucking Service Manager), een handig tooltje om elk script als Windows-service te draaien.
  • Een dedicated, non-privileged Windows-gebruiker voor de service (voor veiligheid en bestandsrechten).
  • Toegang tot de map met je Python virtuele omgeving en API code.
  • De juiste rechten om services te installeren en te beheren (admin rechten).

📝 Voorbeeld: batch-script voor nssm

Maak een .bat-bestand, bijvoorbeeld start_dwh_api.bat met deze inhoud:

@echo off
cd /d "C:\DWH_API_Klantportaal"
call .venv\Scripts\activate.bat
python -m uvicorn main:app --host 0.0.0.0 --port 8000

🔧 Service installeren met nssm

  1. Download nssm van https://nssm.cc/download
  2. Open een command prompt als administrator
  3. Installeer de service met nssm:

PS C:\> nssm install DWH_API_Service "C:\Path\to\start_dwh_api.bat"

  1. Configureer de service in nssm (gebruik de GUI of commandline) om te draaien onder de juiste gebruiker en met de juiste werkmap.
  2. Start de service

PS C:\> nssm start DWH_API_Service

🔐 Rechten en veiligheid

  • Geef de Windows-gebruiker alleen SELECT-rechten op de benodigde database-views.
  • Geef deze gebruiker ook lees- en execute-rechten op de map waar de API en virtuele omgeving staan.
  • Vermijd het draaien van de service onder een Administrator-account.

🌐 Bereikbaarheid van je API

Je API draait nu, maar hoe zorg je dat andere systemen ‘m kunnen bereiken? Dit hangt af van waar je API-host staat en waar de consumerende applicaties draaien.

🖥️ Lokale toegang

  • Als de API draait op een machine binnen hetzelfde (virtuele) netwerk als de consumerende applicatie, dan kun je het IP-adres of de hostnaam van die machine gebruiken in plaats van localhost.
  • Bijvoorbeeld:

http://192.168.1.100:8000/items

of

http://api-server-01.yourdomain.local:8000/items

🌍 Externe toegang via internet

Als applicaties de API via internet moeten benaderen:

  • Zorg voor een Fully Qualified Domain Name (FQDN) die verwijst naar het publieke IP-adres van je API-host (of een tussenliggende firewall/load balancer).
  • Richt DNS zo in dat de FQDN naar dat IP verwijst.
  • Zet een reverse proxy of load balancer in tussen internet en je API-host, zoals nginxAzure Application Gateway, of AWS ALB.
  • Deze tussenlaag regelt vaak ook TLS (https) terminatie en routing.

🔒 Beveiliging in netwerklaag

  • Gebruik altijd TLS (HTTPS) om verkeer te versleutelen.
  • Stel je firewall zo in dat alleen verkeer vanaf specifieke IP-adressen of netwerken wordt toegelaten.
  • Overweeg VPN-toegang voor interne applicaties.

🔧 FastAPI configuratie voor HTTPS redirect

FastAPI zelf kan geen TLS afhandelen, maar je kunt wel FastAPI instrueren om HTTP-verzoeken automatisch door te sturen naar HTTPS:

from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware

app = FastAPI()
app.add_middleware( HTTPSRedirectMiddleware )

Let op: je reverse proxy of load balancer moet TLS afhandelen.

💡 Samenvatting

ScenarioToegangsadres voorbeeldOpmerkingen
Zelfde netwerkhttp://192.168.1.100:8000/itemsEenvoudig via IP of hostname
Publieke internethttps://api.jouwbedrijf.nl/itemsGebruik DNS, reverse proxy, TLS
Development (lokaal)http://localhost:8000/itemsAlleen lokaal beschikbaar

📚 You’ve got the docs! — Automatische API-documentatie

FastAPI levert van zichzelf een grote bonus mee: automatische, interactieve documentatie. Hiermee kun je jouw API makkelijk verkennen en testen, en het helpt bij het communiceren met andere teams of externe developers.

🔎 Wat krijg je erbij?

  • Swagger UI:
    Een webinterface waar je alle beschikbare endpoints, parameters en responsmodellen kunt bekijken en direct kunt uitproberen.
    Bezoek het via:

http://localhost:8000/docs

  • ReDoc:
    Een alternatieve, meer technische API-documentatie, overzichtelijk en met goede leesbaarheid.
    Te vinden via:

http://localhost:8000/redoc

  • OpenAPI-spec:
    De complete machine-leesbare API-specificatie in JSON-formaat, waarmee andere tools en systemen jouw API kunnen begrijpen:

http://localhost:8000/openapi.json

🎯 Waarom is dit handig?

  • Sneller testen en ontwikkelen: zonder zelf documentatie te schrijven of aparte tools te gebruiken.
  • Communicatie: makkelijk delen met ontwikkelaars, support, of externe partijen.
  • Automatisering: tooling kan automatisch client libraries genereren op basis van de OpenAPI-specificatie.
  • Up-to-date: de documentatie is altijd synchroon met je code, omdat die realtime wordt gegenereerd.

💡 Tips

  • Gebruik type hints en response_model in je endpoints om de documentatie compleet en correct te maken.
  • Voeg waar nodig beschrijvingen toe met FastAPI’s Query()Path(), en Body() parameters.
  • Voor beveiligde API’s geldt: zorg dat je testtools ook de benodigde API-key header meesturen om de endpoints uit te kunnen proberen.
Read the docs

🌐 API-standaarden: FastAPI en OpenAPI versus anderen

FastAPI is volledig compliant met OpenAPI (voorheen Swagger), wat zorgt voor heldere, machine-leesbare documentatie en brede ondersteuning door tools. Dit maakt het eenvoudig om RESTful APIs te ontwerpen en te documenteren.

FastAPI ondersteunt echter niet standaard andere API-standaarden zoals OData, dat vooral bekend is om zijn uitgebreide query- en filtermogelijkheden binnen URL’s, en veel gebruikt wordt binnen Microsoft-omgevingen.

Andere bekende API-standaarden zijn onder meer:

  • GraphQL: stelt clients in staat precies te definiëren welke data ze willen ontvangen, vaak gebruikt voor complexe front-end toepassingen.
  • gRPC: een protocol voor snelle, typeveilige communicatie tussen microservices.
  • JSON:API: een standaard voor uniforme structuur en communicatie in RESTful APIs.
  • SOAP: een oudere, op XML gebaseerde webservice-standaard die nog steeds in veel enterprise-omgevingen voorkomt.

Hoewel FastAPI zich richt op moderne RESTful API’s met OpenAPI, is het mogelijk om met aanvullende libraries sommige andere standaarden te ondersteunen, maar dat vereist extra configuratie en maatwerk. FastAPI is een topkeuze voor RESTful APIs met HTTP/JSON en OpenAPI-documentatie. Andere standaarden vereisen andere tooling en aanpak die FastAPI niet out-of-the-box ondersteunt.

🎯 Conclusie

Met een relatief eenvoudige FastAPI-implementatie maak je jouw data warehouse veel beter toegankelijk voor moderne applicaties. Door de juiste structuur, beveiliging en gebruik van standaardtools bouw je snel een betrouwbare REST API die direct waarde toevoegt.

Het resultaat:

  • Meer gebruiksgemak: andere systemen kunnen op een uniforme, webvriendelijke manier data opvragen.
  • Betere beheerbaarheid: businesslogica blijft waar die hoort — in het data warehouse — en API’s blijven eenvoudig en onderhoudbaar.
  • Veiligheid: met API-keys en HTTPS voorkom je onbevoegde toegang en bescherm je data in transit.
  • Schaalbaarheid: door endpoints te pagineren en te genereren, houd je ook grotere datasets beheersbaar.

Heb je vragen of wil je hulp bij het bouwen van jouw API? Neem gerust contact op!

📢 Disclaimer

Dit artikel is tot stand gekomen in samenwerking met een AI-taalmodel. De inhoud is bedoeld om je zo goed mogelijk te ondersteunen, maar blijf altijd zelf kritisch en pas voorbeelden aan aan jouw specifieke situatie en beveiligingseisen.

Share