Lorsque votre interface cloud doit imprimer une étiquette physique sur une imprimante USB installée dans un entrepôt, il vous faut une solution architecturale créative. Voici le modèle que j'ai conçu.
Le problème
Mon frontend ERP sur mesure tourne sur Retool Cloud. Mon imprimante d'étiquettes (Brother QL-700) est connectée en USB à un PC Windows dans l'entrepôt. Ma base de données (NeonDB) est dans le cloud. Quand un employé clique sur « Imprimer l'étiquette » dans Retool, une étiquette physique doit sortir d'une imprimante située à 3 000 km des serveurs de Retool.
Retool Cloud ne peut pas communiquer avec une imprimante locale. Il n'existe aucun chemin réseau direct. La redirection de port est fragile. Les VPN ajoutent de la complexité. Les webhooks exigent un point d'accès public.
Il me fallait quelque chose de plus simple et de plus fiable.
La solution : PostgreSQL comme file de commandes
L'idée clé, c'était que les deux côtés — Retool Cloud et le backend local — partageaient déjà une chose : la base de données PostgreSQL. Pourquoi ne pas l'utiliser comme courtier de messages ?
Retool Cloud NeonDB (PostgreSQL)
│ │
│ INSERT INTO │
│ extraction_command_queue │
│ (type='print', payload=...) │
│ ──────────────────────────────────→ │
│ │
│ │ ← POLL every 5s
│ │ (SELECT ... WHERE
│ │ status='pending')
│ │
│ Local Backend (Windows)
│ │
│ │ Process command
│ │ Download label PDF
│ │ Send to Brother QL-700
│ │ UPDATE status='completed'
│ │
│ ← Query result │
│ (status='completed') │
Les détails d'ingénierie
Verrouillage exclusif
Que se passe-t-il si deux instances du backend tournent en parallèle ? Ou si le backend plante en plein milieu d'une commande ? J'ai mis en place un modèle de verrou exclusif :
- Au démarrage, le
CommandPolleracquiert un verrou en écrivant son nom d'hôte et son PID dans une tablebackend_lock - Avant de traiter une commande, il vérifie qu'il détient toujours le verrou
- Si une autre instance tente de démarrer, elle voit le verrou et se met en retrait
Surveillance par heartbeat
Un verrou sans heartbeat, c'est un interblocage en puissance. Toutes les 30 secondes, le backend actif met à jour son horodatage last_heartbeat. Si le heartbeat est obsolète (>60 secondes), toute nouvelle instance du backend peut s'emparer du verrou — partant du principe que le détenteur précédent a planté.
Types de commandes
La file ne se limite pas à l'impression :
- Imprimer une étiquette — Télécharger l'étiquette du transporteur depuis l'API Wallapop, générer un code-barres, l'envoyer à la Brother QL-700
- Imprimer une étiquette LPN — Générer un code-barres Code128 pour un article en stock, l'imprimer sur une étiquette thermique
- Enrichir un article — Déclencher le pipeline d'enrichissement par IA (scraping d'Amazon → catégorisation → génération de description)
- Lancer une extraction — Déclencher une extraction à la demande des commandes/conversations/annonces
Récupération après échec
Les commandes peuvent échouer (imprimante hors ligne, Amazon bloquant le scraping, délai réseau dépassé). Le système gère cela :
- Les commandes en échec sont marquées avec
status='failed'et un message d'erreur - Retool affiche l'erreur à l'utilisateur avec un bouton « Réessayer »
- Au démarrage, le backend recherche les commandes orphelines (status='processing' mais heartbeat obsolète) et les réinitialise à 'pending'
Pourquoi cela fonctionne mieux que les alternatives
- face aux webhooks : Aucun point d'accès public requis. Aucun certificat SSL. Aucun DDNS. Le backend local n'est qu'un consommateur qui interroge une base de données
- face aux files de messages (RabbitMQ, Redis) : Aucune infrastructure supplémentaire. PostgreSQL est déjà là. Pour mon volume (~50 commandes/jour), c'est parfait
- face au WebSocket : Aucune connexion persistante à maintenir. Une interrogation toutes les 5 secondes convient tout à fait pour l'impression d'étiquettes — un délai de 5 secondes est imperceptible
Résultats en production
Ce système tourne en production depuis plus d'un an. Il traite de manière fiable les commandes d'impression, les demandes d'enrichissement et les déclenchements d'extraction. La bascule a été testée (involontairement) à plusieurs reprises lorsque mon PC local redémarrait — le backend redémarre, récupère le verrou et reprend le traitement en moins de 60 secondes.
Le modèle est open source : github.com/AspiranteD/cloud-local-command-bridge