Les APIs REST sont partout dans les pipelines data : ingestion de sources externes, exposition de features ML, interfaces avec les équipes métier. Savoir les concevoir et les consommer rigoureusement est indispensable.
Quels sont les principes REST ? Quelle est la différence entre GET, POST, PUT et PATCH ?
| Méthode | Action | Idempotent | Exemple |
|---|---|---|---|
| GET | Lire | Oui | GET /orders/123 |
| POST | Créer | Non | POST /orders |
| PUT | Remplacer entièrement | Oui | PUT /orders/123 (toutes les propriétés) |
| PATCH | Modifier partiellement | Non | PATCH /orders/123 (status seulement) |
| DELETE | Supprimer | Oui | DELETE /orders/123 |
Quand utilisez-vous une API Key et quand OAuth2 pour authentifier une API data ?
import requests
# API Key : simple, pour les services M2M (machine à machine)
headers = {'X-API-Key': 'sk-prod-abc123...'}
response = requests.get('https://api.service.com/data', headers=headers)
# Bearer Token (JWT) : pour les utilisateurs
headers = {'Authorization': f'Bearer {token}'}
# OAuth2 Client Credentials : pour les services M2M avec rotation
import httpx
def get_token(client_id: str, client_secret: str, token_url: str) -> str:
response = httpx.post(token_url, data={
'grant_type': 'client_credentials',
'client_id': client_id,
'client_secret': client_secret
})
return response.json()['access_token']
# Stocker les credentials dans les variables d environnement
# JAMAIS en dur dans le codeQuelle est la différence entre la pagination par offset et par cursor ? Laquelle préférez-vous ?
import requests
# Offset pagination : simple mais problèmes sur données changeantes
def get_all_orders_offset(url: str) -> list:
all_orders = []
page = 1
while True:
resp = requests.get(f'{url}?page={page}&limit=100').json()
all_orders.extend(resp['data'])
if not resp.get('next_page'):
break
page += 1
return all_orders
# Cursor pagination : stable, recommandée pour les grandes APIs
def get_all_orders_cursor(url: str) -> list:
all_orders = []
cursor = None
while True:
params = {'limit': 100}
if cursor:
params['cursor'] = cursor
resp = requests.get(url, params=params).json()
all_orders.extend(resp['data'])
cursor = resp.get('next_cursor')
if not cursor:
break
return all_ordersComment versionnez-vous une API data pour ne pas casser les consommateurs existants ?
Comment gérez-vous les erreurs et le rate limiting quand vous consommez une API tierce ?
import requests
import time
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=2, max=60)
)
def fetch_with_retry(url: str, headers: dict) -> dict:
response = requests.get(url, headers=headers, timeout=30)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
time.sleep(retry_after)
raise Exception('Rate limited')
response.raise_for_status() # raise pour 4xx et 5xx
return response.json()
# Respecter les rate limits proactivement
from ratelimit import limits, sleep_and_retry
@sleep_and_retry
@limits(calls=100, period=60) # 100 appels par minute
def call_api(url: str) -> dict:
return requests.get(url).json()Comment exposez-vous une table BigQuery ou Snowflake via une API REST ?
from fastapi import FastAPI, Query, Depends
from pydantic import BaseModel
from typing import Optional
import duckdb
app = FastAPI()
class OrdersResponse(BaseModel):
data: list
total: int
cursor: Optional[str] = None
@app.get('/api/v1/orders', response_model=OrdersResponse)
async def get_orders(
region: Optional[str] = Query(None),
date_from: Optional[str] = Query(None),
limit: int = Query(100, ge=1, le=1000),
cursor: Optional[str] = Query(None)
):
query = 'SELECT * FROM orders WHERE 1=1'
if region: query += f" AND region = '{region}'"
if date_from: query += f" AND order_date >= '{date_from}'"
if cursor: query += f" AND order_id > '{cursor}'"
query += f' ORDER BY order_id LIMIT {limit + 1}'
results = duckdb.execute(query).fetchall()
has_more = len(results) > limit
data = results[:limit]
next_cursor = data[-1][0] if has_more else None
return {'data': data, 'total': len(data), 'cursor': next_cursor}| Niveau | Maitrise | Signal GO | NO-GO |
|---|---|---|---|
| Confirmé | Méthodes HTTP, auth API Key/Bearer, pagination basique | Implémente la pagination correctement, gère les erreurs HTTP | Ne gère pas le rate limiting, ignore les codes d erreur |
| Senior | OAuth2, cursor pagination, versioning, retry avec backoff | Utilise tenacity pour les retries, implémente cursor pagination, a versionné une API | N a jamais pensé à la dépréciation d une API |
Premier entretien gratuit. Rapport GO/NO-GO sous 48h.