Aller au contenu
HexaTransfer
Retour au blog
Approfondissements techniques

Conception d'API de transfert de fichiers : bonnes pratiques RESTful

Concevez des API de transfert de fichiers robustes selon les bonnes pratiques RESTful. Authentification, limitation de débit, envois multipart et gestion des erreurs.

Une API REST de transfert de fichiers bien conçue expose cinq ou six ressources (sessions, parties, partages, téléchargements, révocations), utilise honnêtement la sémantique HTTP (POST pour créer, PUT pour les chunks idempotents, DELETE pour la révocation), et pousse le mouvement réel des octets vers des URL de stockage présignées pour que votre serveur ne devienne jamais le goulot d'étranglement en bande passante. L'authentification utilise des tokens bearer à courte durée de vie sur TLS 1.3, les limites de débit différencient la création des lectures de métadonnées, les uploads chunkés suivent le schéma reprenables tus.io ou la sémantique S3 multipart, et les erreurs suivent RFC 7807 Problem Details pour que les clients puissent agir dessus de manière programmatique. Pour les API exposées à des clients en UE, l'ANSSI recommande TLS 1.3 comme minimum absolu et le chiffrement de bout en bout pour les transferts de données sensibles.

Modéliser la ressource autour des actions, pas des fichiers

Une erreur courante est de modéliser l'API comme une arborescence de fichiers. Les services de transfert de fichiers se modélisent mieux comme trois ressources :

  • /sessions — un upload en cours, créé par POST, alimenté via des PUTs de chunks
  • /shares — un transfert complété et adressable avec une expiration et un budget de téléchargements
  • /downloads — des handles d'accès signés de courte durée pour récupérer les octets

Les sessions deviennent des partages via une action complete ; les partages expirent via le temps ou le budget de téléchargements. Les révocations sont un PATCH sur un partage ou un DELETE avec un token de révocation. Aucun nom pour « fichier » ou « dossier » — ce sont des détails d'implémentation du stockage, pas votre contrat public.

Cette forme maintient la surface API petite (moins de 10 endpoints), s'aligne proprement sur le cache HTTP (les partages ont Cache-Control: private, max-age=60 ; les téléchargements ont no-store), et vous permet de changer de backend de stockage sans casser les clients.

Schémas d'authentification et d'autorisation

Pour les API orientées utilisateur, émettez des JWT de courte durée (TTL de 15 minutes) signés avec EdDSA, et rafraîchissez via un cookie sécurisé HttpOnly. Mettez le token dans Authorization: Bearer, jamais dans les chaînes de requête où il se retrouve dans les logs.

Pour le machine-à-machine, la signature de requête HMAC-SHA256 surpasse les tokens bearer car elle prouve la possession d'une clé sans l'envoyer. AWS SigV4 est la conception de référence :

Authorization: HEX4-HMAC-SHA256 
  Credential=AKIA.../20261202/eu/transfer/hex4_request,
  SignedHeaders=host;x-hex-date;x-hex-content-sha256,
  Signature=...

Pour les URL de téléchargement présignées, les TTL devraient être en minutes, pas en heures. Une fenêtre de 15 minutes équilibre l'utilisabilité contre le risque qu'une URL divulguée soit transmise. Incluez un verrou IP uniquement si vous pouvez tolérer de casser les utilisateurs derrière le CGNAT — généralement vous ne le pouvez pas.

Uploads chunkés et sémantique multipart

Deux protocoles crédibles existent pour les uploads chunkés reprenables : tus (brouillon IETF, en-têtes Upload-Offset et Upload-Length) et S3 multipart (PartNumber, UploadId, ETag). Choisissez-en un et engagez-vous. Inventer votre propre protocole semble tentant et finit mal quand vous réalisez que vous devez gérer les écritures partielles, les chunks hors ordre et les sessions abandonnées.

Le schéma tus en forme REST :

POST   /sessions              -> 201, Location: /sessions/abc
HEAD   /sessions/abc          -> 200, Upload-Offset: 104857600
PATCH  /sessions/abc          -> 204, corps = prochain chunk
POST   /sessions/abc/complete -> 201, Location: /shares/xyz
DELETE /sessions/abc          -> 204

Bornes de taille de chunk : minimum 5 Mo pour correspondre au multipart S3, maximum 100 Mo pour garder le coût de réessai borné, défaut 8 Mo. Rejetez les chunks qui ne s'alignent pas avec l'offset annoncé avec HTTP 409 Conflict plus un document Problem expliquant l'offset attendu.

Limitation de débit qui reflète les vrais abus

Les limites de débit doivent varier selon le coût des endpoints :

  • POST /sessions : 20 par IP par heure (coûteux — alloue du stockage)
  • PATCH /sessions/:id : 10 000 par heure par session (bon marché — écrit des octets)
  • GET /shares/:id : 1 000 par IP par heure (recherche de métadonnées)
  • GET /shares/:id/download : 100 par IP par heure (coût d'egress)

Appliquez des limites à l'edge (Cloudflare, Fastly) pour la protection de plancher, et une seconde couche à l'application (express-rate-limit, @fastify/rate-limit de Fastify) pour le contrôle des pics. Retournez 429 avec Retry-After en secondes. Incluez aussi des en-têtes de limite de débit sur les réponses réussies :

RateLimit-Limit: 20
RateLimit-Remaining: 17
RateLimit-Reset: 2843

Suivre le brouillon IETF RateLimit permet aux clients bien élevés de s'auto-rythmer plutôt que d'être bloqués.

Réponses d'erreur sur lesquelles les clients peuvent agir

RFC 7807 Problem Details est la norme. Chaque erreur retourne Content-Type: application/problem+json :

{
  "type": "https://api.example.com/errors/chunk-offset-mismatch",
  "title": "Décalage de chunk non concordant",
  "status": 409,
  "detail": "Le serveur attendait l'offset 5242880, reçu 4194304.",
  "expected_offset": 5242880,
  "session_id": "abc123"
}

L'URI type devrait être documenté et stable ; c'est ce sur quoi les clients font du pattern-matching. title est générique ; detail est spécifique. Les champs personnalisés ajoutent un contexte actionnable par machine. Ne divulguez jamais de traces de pile, de chemins de fichiers ou d'ID internes dans les erreurs.

Mappez les codes de statut HTTP honnêtement : 400 pour les entrées malformées, 401 pour l'auth manquante, 403 pour l'authentifié-mais-non-autorisé, 404 uniquement quand la ressource n'a jamais existé (utilisez 410 Gone pour les partages expirés), 413 pour les payloads hors quota, 429 pour les limites de débit, 500 pour les bugs, 503 pour la maintenance.

Négociation de contenu et streaming

Les endpoints d'upload devraient accepter application/octet-stream et exiger Content-Length. Rejetez multipart/form-data pour les uploads de chunks — cela ajoute une surcharge de parsing et n'aide personne. Acceptez Content-Range pour les écritures partielles de style tus.

Les endpoints de téléchargement devraient prendre en charge les requêtes HTTP Range (RFC 7233) pour les téléchargements reprenables :

GET /shares/xyz/blob
Range: bytes=104857600-209715199
-> 206 Partial Content
   Content-Range: bytes 104857600-209715199/2147483648

C'est ce qui permet aux navigateurs de reprendre un téléchargement de 2 Go après une interruption Wi-Fi. La plupart des stores compatibles S3 servent les requêtes Range nativement — votre API n'a qu'à transmettre ou présigner.

Versionner sans accumuler de dette

Versionnez via le chemin URL (/v1/sessions) pas via les en-têtes. Le versionnage par chemin est visible dans les logs, cacheable, et plus facile à déboguer que Accept: application/vnd.example.v1+json. Maintenez v1 au moins 24 mois après la livraison de v2. Ajoutez des champs librement (les clients doivent ignorer les champs inconnus) ; ne supprimez jamais ni ne renommez les champs dans une version stable.

Quand des changements cassants sont nécessaires, faites tourner v1 et v2 en parallèle pendant 12 mois, exposez un en-tête Sunset sur les réponses v1 selon RFC 8594, et publiez des guides de migration avec de vrais exemples avant/après.

Observabilité et débogage

Chaque réponse devrait inclure un ID de corrélation (X-Request-ID) repris de la requête ou généré. Loggez l'ID, l'IP client (hachée si sensible à la vie privée), l'endpoint, le statut et la durée en JSON structuré. Ne loggez pas les corps de requête — c'est comme ça que les clés de chiffrement finissent dans Datadog.

Émettez des métriques par endpoint : nombre de requêtes, latence p50/p95/p99, taux d'erreur, octets entrants, octets sortants. Alertez sur les pics de latence p99 et le taux 5xx au-dessus de la ligne de base. Les traces via OpenTelemetry vous donnent le flux de requête à travers API, stockage et base de données.

HexaTransfer suit les schémas ci-dessus — ressources courtes, uploads chunk de style tus.io, erreurs Problem Details, téléchargements présignés de 15 minutes, en-têtes RateLimit.

Documentation qui correspond à la réalité

Publiez une spec OpenAPI 3.1 aux côtés de l'API et gardez-la dans le même dépôt que le code serveur pour que la dérive de schéma soit un problème de revue de PR plutôt qu'une surprise en production. Générez au moins un SDK (TypeScript ou Python) depuis la spec et utilisez-le dans vos propres exemples — les bugs de SDK font remonter les bugs de spec rapidement. Incluez des exemples curl pour chaque endpoint, un quickstart qui uploade un vrai fichier en moins de 20 lignes, et une page documentant spécifiquement chaque URI type d'erreur. Une API que les développeurs peuvent intégrer en une après-midi arrivera dans 10 fois plus de produits que celle qui nécessite une semaine d'ingénierie inverse.

Essayez-le sur https://hexatransfer.com — gratuit, sans compte, 10 Go maximum.

Envoyez vos fichiers volumineux en toute sécurité avec le chiffrement de bout en bout

Transférez des fichiers jusqu'à 10 Go gratuitement avec le chiffrement de bout en bout. Aucun compte requis. Vos fichiers sont chiffrés dans votre navigateur avant l'envoi — personne d'autre ne peut les lire.

Envoyer un fichier