Sandwich : une bibliothèque de réponses API scellées pour les couches réseau Kotlin
Aperçu du projet : Sandwich est une bibliothèque d'API scellée, adaptable et légère, conçue pour gérer les réponses et exceptions d'API dans Kotlin pour Retrofit, Ktor et Kotlin Multiplatform.
En bref
- De quoi s’agit-il ?
- Sandwich modélise les réponses Retrofit et Ktor comme des valeurs ApiResponse scellées avec des branches succès, erreur et exception, plus des opérateurs pour le mapping, la récupération et les nouvelles tentatives.
- À qui s’adresse-t-il ?
- Sandwich organise le traitement des réponses autour de trois types scellés, des scopes d'extension par branche, des opérateurs compatibles coroutines et un hook d'opérateur global, le tout distribué sous Apache-2.0 avec des règles R8 incluses.
- Puis-je l’utiliser commercialement ?
- Oui. Apache-2.0 est une licence permissive : vous pouvez utiliser, modifier et vendre un logiciel qui en dépend, à condition de conserver les mentions de droit d’auteur et de licence.
- Est-il encore maintenu ?
- Oui. Le dépôt a reçu de nouveaux commits au cours des dernières 24 heures.
- En quel langage est-il écrit ?
- Principalement Kotlin, d’après les statistiques de langage de GitHub.
Ces réponses reposent sur les données GitHub du projet (dernière synchronisation le 19 septembre 2026) et sur notre analyse. Elles ne constituent pas un avis juridique.
ANALYSE OPEN SOURCE APPROFONDIE
Le problème que Sandwich résout
La README décrit Sandwich comme une bibliothèque API scellée, adaptable et légère, pour traiter les réponses API et les exceptions en Kotlin pour Retrofit, Ktor et Kotlin Multiplatform. Le projet a été conçu pour rationaliser la création d'interfaces standardisées afin de modéliser les réponses de Retrofit, Ktor et d'autres sources, de sorte que les données du corps, les erreurs et les cas exceptionnels puissent être traités avec des opérateurs fonctionnels dans une architecture multicouche. La README indique que cela élimine le besoin d'écrire des classes wrapper comme Resource ou Result, laissant le code applicatif se concentrer sur la logique métier. Elle liste également le traitement global des réponses, Mapper, Operator et ApiResponse avec Coroutines comme fonctionnalités principales.
ApiResponse et ses trois types de résultats
L'abstraction centrale est ApiResponse, une interface que la README définit comme un moyen de créer des réponses cohérentes à partir d'appels API ou d'E/S, y compris le réseau, la base de données ou d'autres sources. Elle englobe trois types distincts. ApiResponse.Success représente une réponse réussie et peut porter une valeur de données plus une balise optionnelle pour distinguer l'origine ou faciliter le post-traitement. ApiResponse.Failure.Exception signale des tâches échouées capturées par des exceptions inattendues lors de la création de la requête ou du traitement de la réponse, comme une panne de connexion réseau. ApiResponse.Failure.Error désigne des requêtes échouées, généralement dues à de mauvaises requêtes ou à des erreurs internes du serveur, et peut contenir une charge utile d'erreur avec des informations détaillées. La README montre des constructeurs pour chaque type et illustre des types d'erreur personnalisés qui étendent Failure.Error ou Failure.Exception, comme LimitedRequest et WrongArgument.
Créer des réponses et les scopes d'extension
La README documente plusieurs façons de créer une ApiResponse. ApiResponse.of et apiResponseOf enveloppent un lambda de requête, tandis que ApiResponse.suspendOf et suspendApiResponseOf acceptent des fonctions suspendues dans le lambda. Une note dans la README indique que si vous comptez utiliser l'opérateur global ou le mappeur ApiResponse global, vous devez créer les réponses avec of ou suspendOf pour que ces fonctions globales s'appliquent. Une fois la réponse obtenue, les scopes onSuccess, onError, onException et onFailure ne s'exécutent que lorsque la réponse correspond à ce type. onFailure couvre à la fois les branches erreur et exception, et la README montre chaque scope accédant aux données, messages, charges utiles ou exceptions pertinents. Le scope d'erreur expose messageOrNull et payload ; le scope d'exception expose messageOrNull et l'exception.
Coroutines, Flow et opérateurs fonctionnels
Les variantes compatibles coroutines suspendOnSuccess, suspendOnError, suspendOnException et suspendOnFailure permettent d'appeler des fonctions suspendues, comme un insert DAO, dans le scope. L'extension toFlow convertit une ApiResponse en Flow de coroutines, avec une variante qui accepte un lambda de transformation pour les données. Les extensions fonctionnelles documentées dans la README incluent la récupération (recover, recoverWith), la validation (validate, requireNotNull), le filtrage (filter, filterNot), la combinaison (zip, zip3) et l'observation (peek, peekSuccess, peekFailure, peekError, peekException). La README précise que chaque extension a un équivalent suspendu comme suspendRecover et suspendValidate. Un exemple travaillé enchaîne validate, filter, recover et peekSuccess sur une réponse.
Nouvelles tentatives, appels séquentiels et récupération de données
Pour la logique de nouvelle tentative, la README documente une interface RetryPolicy avec les méthodes shouldRetry et retryTimeout, et une extension runAndRetry qui exécute une tâche sous cette politique ; la politique d'exemple réessaie jusqu'à trois tentatives avec un délai de 3000 ms. Pour les requêtes dépendantes, then et suspendThen enchaînent les tâches afin que chaque étape reçoive le résultat de la précédente ; la README enchaîne getUserToken, getUserDetails et queryPosters dans cet ordre. Les aides de récupération getOrNull, getOrElse et getOrThrow retournent les données encapsulées en cas de succès ; en cas d'échec, elles retournent respectivement null, une valeur par défaut ou lèvent la Throwable encapsulée. L'exemple de la README utilise getOrThrow dans un try/catch et imprime la trace de la pile.
Opérateurs et traitement global des réponses
ApiResponseOperator, utilisé avec l'extension operator, définit des processeurs réutilisables pour les cas de succès, d'erreur et d'exception, afin qu'une séquence de traitement cohérente puisse être partagée entre plusieurs requêtes API. L'exemple CommonResponseOperator journalise dans onError et appelle map pour convertir l'erreur en modèle personnalisé, et journalise le message dans onException. ApiResponseSuspendOperator et suspendOperator prennent en charge les lambdas suspendus, par exemple pour émettre vers un Flow depuis le scope de succès. L'opérateur global enregistre des instances dans SandwichInitializer pour qu'elles s'appliquent à toutes les instances ApiResponse. La README montre un TokenRefreshGlobalOperator qui vérifie les en-têtes et les codes de statut 401 et 403, actualise le jeton et est câblé avec Hilt et App Startup. Une note séparée indique que les mappeurs et opérateurs suspendus, courants avec Ktor et Ktorfit, nécessitent ApiResponse.suspendOf pour être correctement attendus.
Distribution, intégration et licence
La README fait état de plus de 1 200 000 téléchargements dans des projets Android et backend. La configuration Gradle utilise une BOM, com.github.skydoves:sandwich-bom:2.4.0, plus sandwich pour le noyau, sandwich-retrofit pour Android, sandwich-ktor, sandwich-ktor-serialization et sandwich-ktorfit pour Kotlin Multiplatform, et sandwich-test pour les tests. Les règles R8 et ProGuard sont regroupées dans le JAR. Les cas d'utilisation listés incluent Pokedex, ChatGPT Android, DisneyMotions, MarvelHeroes, Neko et TheMovies2. Les métadonnées du dépôt au moment de la rédaction montrent 1 766 étoiles, 113 forks et 4 problèmes ouverts, le dépôt n'étant pas archivé. Le projet est sous licence Apache-2.0, droit d'auteur 2020 skydoves (Jaewoong Eum). La licence accorde des droits perpétuels, mondiaux, non exclusifs et gratuits de reproduire, préparer des œuvres dérivées et distribuer, et déclare qu'aucune garantie n'est fournie. La README ne dit rien sur les attentes de support ou le rythme de maintenance.
Conclusion éditoriale
Sandwich organise le traitement des réponses autour de trois types scellés, des scopes d'extension par branche, des opérateurs compatibles coroutines et un hook d'opérateur global, le tout distribué sous Apache-2.0 avec des règles R8 incluses.
Notes de la communauté