← Tous les cours / Raccourcisseur d'URL serverless sur AWS Cours
00 — VUE D'ENSEMBLE

Un raccourcisseur d'URL sans serveur

Tu as déjà partagé un lien raccourci — un bit.ly glissé dans un message, une URL à rallonge réduite à cinq caractères. Mais qu'est-ce qui se passe vraiment quand on clique dessus ? C'est précisément ce qu'on va construire.

Imagine la scène. Tu tombes sur un article qui vaut le détour, tu veux l'envoyer à quelqu'un, mais l'URL est un monstre de deux cents caractères bourré de paramètres de suivi. Tu la colles dans un raccourcisseur, et d'un coup elle devient court.ly/aZ3kP : propre, court, partageable. Des semaines plus tard, des milliers de personnes auront cliqué sur ce lien sans jamais se douter de ce qui se joue derrière ces cinq caractères.

Ce « derrière », c'est tout l'objet de ce cours. Et il faut le dire tout de suite, parce que ça va te rassurer : le cœur du problème est d'une simplicité désarmante. Au fond, un raccourcisseur ne fait que tenir un carnet d'adresses. Quand tu raccourcis une URL, il note quelque part que aZ3kP correspond à ton adresse longue. Quand un visiteur ouvre le lien court, il relit cette note et l'envoie vers la vraie destination. Voilà. Tout le reste — la sécurité, la montée en charge, la surveillance — n'est que de l'ingénierie soigneusement disposée autour de cette idée minuscule.

Alors pourquoi y consacrer un cours entier ? Parce que la façon dont tu héberges ce carnet d'adresses change absolument tout. L'approche classique consiste à louer un serveur qui tourne en permanence, attend les requêtes, et que tu paies même à trois heures du matin quand personne ne crée le moindre lien. Ça marche — mais ça te laisse une machine sur les bras : à entretenir, à mettre à jour, à surveiller. On va emprunter le chemin inverse, celui du serverless.

Serverless
Le nom est trompeur : il y a évidemment des serveurs, mais c'est le fournisseur cloud qui s'en occupe entièrement. Toi, tu écris ton code et tu décris ta configuration ; tu ne provisionnes aucune machine, tu n'en patches aucune, et tu paies à la requête plutôt qu'à l'heure. Quand rien ne tourne, tu ne paies rien.

Pour bâtir notre raccourcisseur en serverless, on va faire travailler ensemble cinq services AWS. Chacun a un seul métier, et ils se passent le relais comme les postes d'une chaîne de montage. Le plus simple, pour les avoir en tête dès maintenant, c'est de voir d'un coup d'œil qui s'occupe de quoi :

ServiceSon métier dans le projet
DynamoDBTient le carnet d'adresses : quel code court mène à quelle URL longue
IAMDonne au code le droit de toucher la base, sans jamais manipuler de mot de passe
LambdaExécute la logique : fabriquer un code, rediriger un visiteur
API GatewayLa porte d'entrée publique : elle route, vérifie et limite le trafic
CloudWatchGarde la trace de tout ce qui se passe, pour comprendre quand ça coince

Quand tu raccourcis une URL, ta requête traverse cette chaîne de gauche à droite : elle entre par la porte, déclenche le code, qui écrit dans le carnet d'adresses.

Navigateur
API Gateway
Lambda
DynamoDB
💡 Ce qu'il faut garder en tête : chacune de ces briques est indépendante et gérée par AWS. Le jour où ton lien devient viral et que des millions de personnes cliquent en même temps, rien ne s'écroule — AWS multiplie tout seul les copies de ton code pour absorber la vague. C'est ça, une architecture événementielle : du code qui se réveille quand un événement arrive, et qui dort le reste du temps.

Pour ancrer ce trajet avant d'ouvrir chaque service un par un, le simulateur ci-dessous fait voyager une requête dans toute la chaîne. Lance les deux scénarios et regarde où passe l'information.

Clique sur un scénario et suis la requête à travers le système :

01 — DYNAMODB

DynamoDB, la table de hachage du cloud

Notre raccourcisseur n'a qu'une seule question à poser à sa base, encore et encore : « à quelle URL mène ce code ? ». Pour un besoin aussi étroit, sortir une base SQL reviendrait à louer un camion pour transporter une lettre.

Avant d'écrire la moindre ligne de logique, il faut décider où notre raccourcisseur va ranger ses correspondances. Et pour bien choisir, il suffit de regarder honnêtement ce qu'on lui demandera. La réponse est presque décevante de simplicité : on lui posera toujours la même question — « à quelle URL longue mène le code aZ3kP ? » — et on attendra toujours le même genre de réponse. Jamais de jointure, jamais de filtre alambiqué. Juste une clé, et la valeur qui va avec.

Face à un besoin aussi resserré, une base relationnelle classique est franchement surdimensionnée. Elle t'obligerait à provisionner un serveur, à gérer un pool de connexions, et à régler la facture même les nuits où pas une seule URL n'est raccourcie. On ne cherche pas une base universelle ; on cherche un bon vieux dictionnaire, mais hébergé dans le cloud et capable d'encaisser n'importe quelle charge.

DynamoDB
Une base NoSQL entièrement gérée par AWS. Le plus simple est de l'imaginer comme une table de hachage — un HashMap — devenue géante et persistante : chaque élément est rangé sous une clé unique, et le retrouver par cette clé prend de l'ordre de la milliseconde, que la table contienne mille entrées ou un milliard.

Là où une base SQL t'impose des tables aux colonnes figées, reliées par des clés étrangères, DynamoDB se contente de stocker des items repérés par leur clé. Chez nous, cette clé sera tout naturellement le code court : c'est ce qu'on appelle la clé de partition, et c'est elle qui rend la lecture instantanée.

aZ3kP
DynamoDB
https://exemple.com/…

Sur notre cas précis, le contraste avec une base relationnelle est net :

BesoinBase SQLDynamoDB
Lire par cléPossible, mais via un serveurNatif, ~1 ms
Serveur à gérerOuiAucun
Coût au reposTu paies en continu0 en mode on-demand
Montée en chargeManuelle, délicateAutomatique
💡 Le mode on-demand, ton meilleur allié ici. Plutôt que de réserver à l'avance une capacité fixe — et de la payer qu'elle serve ou non —, tu laisses DynamoDB facturer chaque lecture et chaque écriture réellement effectuée. Trafic en berne cette nuit ? La facture suit. C'est le réglage par défaut dès que la charge est difficile à prévoir, ce qui est exactement le cas d'un lien dont tu ignores s'il fera dix ou dix millions de visites.

Dans le TP, tout cela tient en quelques gestes : tu crées une table dédiée aux liens, tu désignes le code court comme clé de partition, et tu actives la capacité on-demand. À partir de là, cette table devient la source de vérité de toute l'application — c'est elle qui sait, et elle seule.

Le simulateur ci-dessous donne à voir cette table de hachage. Insère quelques liens, puis cherches-en un par son code : la lecture par clé tombe immédiatement, sans avoir à parcourir les autres lignes.

Insère des liens, puis retrouve-en un par son code court :

02 — IAM & RÔLES

IAM : un badge, jamais un mot de passe

Maintenant qu'on tient une base, une question gênante surgit : qui a le droit d'y écrire ? La tentation de glisser une clé secrète dans le code est forte — et c'est précisément le piège qu'AWS te permet d'éviter.

On tient notre base de données. Reste une question qu'on préfère souvent repousser, et qu'on a tort de repousser : qui, au juste, a le droit d'y toucher ? Dans un instant, notre code va lire et écrire dans DynamoDB, et il va devoir prouver qu'il en a l'autorisation.

Le réflexe du débutant — on est tous passés par là — consiste à écrire directement une clé d'accès et une clé secrète AWS dans le code. Ça tient le temps d'une démo. Puis le code finit dans un dépôt Git public, dans un log, sur une capture d'écran partagée trop vite, et la clé part avec lui. À cet instant, ce n'est plus ta petite fonction qui est compromise : c'est ton compte AWS tout entier.

⚠️ Une clé en dur, c'est un mot de passe sur un post-it collé à la porte d'entrée. Le vrai danger n'est pas seulement qu'on puisse la lire : c'est qu'une fois lue, elle ouvre tout, indéfiniment. Il existe heureusement une manière pour ton code de prouver son identité sans jamais détenir le moindre secret.

Cette manière, c'est IAM, le service qui gère l'identité et les accès dans AWS — son service de sécurité, en somme. Et l'outil qui nous intéresse ici porte un nom simple : le rôle.

Rôle IAM
Ce n'est pas un compte, ni une personne : c'est un badge. Un ensemble de permissions qu'un service peut « endosser » le temps d'agir. Quand ta fonction endosse un rôle, AWS lui remet des identifiants temporaires qui l'autorisent à faire exactement ce que le rôle prévoit — et rien de plus.

L'image du badge n'est pas qu'une jolie métaphore, elle est exacte jusque dans le détail. Un visiteur dans une entreprise ne reçoit pas la combinaison du coffre ; on lui tend un badge qui ouvre uniquement les portes dont il a besoin, et qui cesse de fonctionner le soir venu. Ta fonction vit la même chose : elle ne connaît aucun secret, elle porte un badge, et AWS la laisse passer aussi longtemps que ce badge le permet.

Lambda
endosse le rôle
identifiants temporaires
DynamoDB

Un rôle se garnit de policies, ces documents qui énumèrent les actions permises — par exemple dynamodb:PutItem pour écrire, ou dynamodb:GetItem pour lire. Dans le TP, tu crées un rôle dédié à tes fonctions, tu y attaches une policy qui ouvre l'accès à DynamoDB, et tu assigneras ce rôle aux fonctions au moment de les créer.

🔑 N'accorde que le strict nécessaire. Si une fonction se contente de lire, ne lui donne pas le droit d'écrire ni de supprimer « au cas où » — ce « au cas où » est justement la porte par laquelle les ennuis entrent le jour où quelque chose dérape. Réduire chaque rôle au minimum vital porte un nom dans le métier, le moindre privilège, et c'est l'un des réflexes de sécurité les plus rentables du cloud.

Le simulateur ci-dessous rend ce principe tangible. Décide des permissions que contient la policy, puis demande à la fonction d'agir : elle ne réussit que si son rôle l'y autorise vraiment.

Règle les permissions du rôle, puis tente une action :

03 — LAMBDA

Lambda : du code qui ne tourne que sur demande

Il reste à écrire le code qui fait réellement le travail. Un serveur qui tourne jour et nuit ferait l'affaire — mais pourquoi payer une machine à ne rien faire pendant les heures où personne ne crée de lien ?

Notre base attend, son gardien est en place ; il manque encore l'essentiel : le code qui fait le travail. On pourrait suivre la voie classique et démarrer un serveur web — un Flask, un Express — qui guette les requêtes. Mais ce serveur tournerait sans interruption, à brûler de l'argent à trois heures du matin alors que pas une âme ne raccourcit d'URL. Ce qu'on veut, c'est exactement l'inverse : du code qui ne s'exécute qu'à l'instant précis où une requête arrive, et qui s'efface aussitôt après.

AWS Lambda
Le service de calcul serverless d'AWS. Tu y déposes ton code, et Lambda se charge de tout le reste : il le réveille quand un événement le déclenche, l'exécute, puis l'éteint dans la foulée. Tu ne paies que les millisecondes réellement consommées, et la montée en charge est automatique — d'un utilisateur à un million, AWS lance autant de copies de ton code qu'il en faut pour suivre.

Pour notre raccourcisseur, deux fonctions suffisent, et on a tout intérêt à les garder séparées : chacune n'a ainsi qu'une seule raison d'exister.

FonctionDéclencheurCe qu'elle fait
createPOST /shortenReçoit une URL, fabrique un code unique, l'enregistre
redirectGET /{code}Cherche le code, renvoie une redirection vers l'URL

La fonction create cache une difficulté qu'on ne voit pas au premier abord. Imagine deux personnes qui raccourcissent une URL au même millième de seconde, et dont les codes générés se télescopent : comment garantir que chaque code reste unique, sans poser de verrou ni monter une transaction compliquée ? L'astuce consiste à confier la vérification à la base elle-même.

Écriture conditionnelle
On formule l'écriture comme une condition adressée à DynamoDB : « enregistre cet item seulement si ce code n'existe pas encore » (attribute_not_exists). Si le code est déjà pris, la base refuse net et signale l'erreur ; la fonction l'attrape, tire un nouveau code et retente. L'unicité est garantie, sans le moindre risque de collision silencieuse.

La fonction redirect, elle, est presque triviale — mais c'est la plus sollicitée des deux. Elle cherche le code dans la base, et si elle le trouve, elle ne renvoie surtout pas une page de texte : elle répond par un code HTTP de redirection, ce petit signal qui dit au navigateur de filer sans attendre vers l'URL d'origine.

💡 301 ou 302 ? Un 301 Moved Permanently est mis en cache par le navigateur : les visites suivantes sautent droit à la cible sans repasser par ta fonction — très rapide, mais tu perds le comptage des clics. Un 302 Found force le navigateur à repasser à chaque fois : tu peux alors compter les visites, au prix d'un appel Lambda systématique. Le bon choix dépend de ce que tu veux mesurer. La mise en cache d'un 301 par les navigateurs et les CDN est un sujet à part entière, traité dans le cours Reverse Proxy, CDN & Cache.

Le simulateur reproduit la fonction create et sa parade aux collisions. Pour que les conflits soient visibles à l'œil nu, l'espace des codes y est volontairement minuscule ; dans la réalité, sept caractères en base 62 ouvrent plus de trois mille milliards de combinaisons, et deux codes ne se télescopent pour ainsi dire jamais.

Raccourcis des URLs et observe la gestion des collisions :

04 — LE RÉSEAU

Le réseau d'un raccourcisseur

Un raccourcisseur, c'est du web sous sa forme la plus pure : une requête, une réponse, une redirection. Avant de poser la porte d'entrée, regardons ce qui voyage vraiment sur le fil.

Tout ce qu'on a bâti jusqu'ici — la base, le rôle, les fonctions — n'a qu'un but : répondre à des requêtes HTTP. Avant d'ouvrir la porte au public, ça vaut la peine de regarder ce qui circule vraiment sur le réseau, parce qu'un raccourcisseur est un concentré de web : une requête, une réponse, et surtout une redirection.

Le web repose sur un échange simple et à sens unique de l'initiative : ton navigateur envoie une requête, le serveur renvoie une réponse, et c'est terminé jusqu'à la prochaine. Chaque requête annonce une méthode qui dit ce qu'on veut faire. Notre service n'en emploie que deux :

Méthode & routeIntention
POST /shortenCréer un lien court
GET /{code}Suivre un lien court
Les codes de statut HTTP
Chaque réponse s'ouvre sur un nombre à trois chiffres qui résume le sort de la requête. Quatre familles à retenir : 2xx ça a marché, 3xx va voir ailleurs (redirection), 4xx la requête est en tort, 5xx le serveur a fauté.

Notre raccourcisseur en parle un petit dialecte bien à lui, que tu as déjà croisé au fil des sections :

CodeQuand il apparaît
201 CreatedLe lien court vient d'être créé
301 / 302Redirection vers l'URL longue
404 Not FoundCode court inconnu
409 ConflictCode déjà pris (écriture conditionnelle)
429 Too Many RequestsDébit dépassé, bloqué par la porte

Le cœur réseau d'un raccourcisseur, c'est la redirection. Quand un visiteur demande GET /aZ3kP, le serveur ne lui renvoie pas une page : il répond 301 accompagné d'un en-tête Location: contenant l'URL longue. Le navigateur lit cet en-tête et refait de lui-même une seconde requête vers la vraie adresse. Deux allers-retours, totalement invisibles pour l'utilisateur.

GET /aZ3kP
301 + Location
GET vers l'URL longue
💡 301 et le cache forment un duo. Un 301 est permanent, donc mémorisable : navigateurs et CDN le retiennent, et les visites suivantes sautent droit à la cible sans même toucher ton serveur. C'est ce qui rend un raccourcisseur si bon marché à grande échelle — mais c'est aussi pourquoi tu perds le comptage des clics, comme on l'a vu côté Lambda.

Cette montée en charge invisible, et la façon dont les caches et les CDN s'intercalent devant ton service, ont leur propre cours : Reverse Proxy, CDN & Cache. Et pour comprendre comment HTTP lui-même a évolué — méthodes, statuts, versions 1.1 à 3 —, va voir HTTP · De 1.1 à 3.

🔒 Et le transport ? Tes liens courts vivent en https:// : du HTTP chiffré par-dessus TLS. Sans ça, n'importe qui sur le réseau pourrait lire — voire réécrire — la redirection. Cette couche de sécurité est l'objet du cours TLS/HTTPS.
Requête/réponse, et sa limite
Ce modèle « le client demande, le serveur répond » est parfait ici : un visiteur veut une redirection, rien de plus. Mais il a une frontière nette — le serveur ne peut jamais parler le premier. Le jour où tu veux pousser des données sans qu'on te les réclame (une messagerie, des notifications), il faut une connexion permanente. C'est exactement le contraste exploré dans le cours Chat temps réel serverless sur AWS.

Le simulateur ci-dessous rejoue ces échanges. Déclenche chaque scénario et lis la requête envoyée et la réponse renvoyée — jusqu'à la redirection que le navigateur suit tout seul.

Lance un scénario et observe l'échange HTTP :

05 — API GATEWAY

API Gateway : le portier de l'API

Exposer une fonction nue sur Internet, c'est tendre une perche au premier bot venu. API Gateway s'interpose en videur : il vérifie qui frappe à la porte, et combien de fois, avant même que ton code ne s'éveille.

Nos deux fonctions sont prêtes — et c'est précisément là qu'un danger apparaît. Tant qu'elles restaient à l'abri, tout allait bien ; dès qu'on les expose au monde, plus rien n'empêche un robot malveillant de marteler l'adresse de création un million de fois par minute. Le résultat serait double, et également désagréable : ta base s'effondre sous la charge, et ta facture AWS s'envole.

Il manque donc quelqu'un à l'entrée pour décider qui peut appeler, et à quelle cadence. Ce videur, c'est API Gateway.

API Gateway
Un service géré qui se poste en contrôleur de trafic devant tes fonctions. Toutes les requêtes passent d'abord par lui : il s'occupe du routage, de l'authentification, et surtout des plans d'usage qui fixent les limites.

Dans le TP, tu déclares une API REST avec deux routes — une pour créer un lien, une pour rediriger —, tu attaches un plan d'usage à la route de création, et tu génères une clé d'API pour tes clients.

Plan d'usage
Vois-le comme un contrat passé avec chaque client. Il fixe deux plafonds complémentaires : combien de requêtes par seconde sont tolérées — le throttling, ou limitation de débit — et combien au total sur la journée — le quota.

Les deux plafonds ne protègent pas contre la même menace, et c'est pour ça qu'ils se complètent :

ThrottlingQuota
MesureRequêtes par secondeRequêtes par jour
Protège contreLes pics soudainsL'abus sur la durée
Réponse si dépassé429 Too Many Requests403 Forbidden

Quant à la clé d'API, c'est le mot de passe attribué à chaque client. À chaque requête, API Gateway pose deux questions : la clé est-elle bien là, et n'a-t-elle pas dépassé ses limites ? À la moindre réponse négative, il bloque sur-le-champ. Ce mécanisme de débit et de quota a son propre cours : Rate Limiting.

💡 Le vrai bénéfice est invisible. Lorsqu'API Gateway rejette une requête, ta fonction Lambda ne démarre même pas — tu ne paies donc rien pour elle, et ta base ne voit jamais passer l'attaque. La défense se joue sur le pas de la porte, bien avant que l'intrus n'entre dans la maison.

Le simulateur ci-dessous est un limiteur de débit miniature. Envoie des requêtes en rafale, puis ralentis : tant que tu respectes la cadence et le quota, elles passent ; au-delà, API Gateway te renvoie sèchement — sans jamais réveiller Lambda.

Envoie des requêtes — vite, puis lentement — et observe les limites :

06 — CLOUDWATCH

CloudWatch : la boîte noire du système

Ton code s'exécute sur des machines que tu ne verras jamais. Alors quand un lien tombe en panne au beau milieu de la nuit, où vas-tu chercher la raison ? CloudWatch est l'endroit où tout est consigné.

Le système tourne enfin dans le cloud — et un dernier problème, plus sournois, pointe le bout de son nez : on n'y voit plus rien. Sur ta machine, quand un programme plante, l'erreur s'affiche sous tes yeux dans le terminal. Avec Lambda, ton code s'exécute sur des serveurs AWS auxquels tu n'as pas accès. Un visiteur clique sur un lien, ça échoue, et te voilà dans le noir : impossible de savoir pourquoi sans un endroit où regarder.

Cet endroit, où atterrissent les traces de tous tes services, c'est CloudWatch.

CloudWatch
Le service d'observabilité d'AWS. Il rassemble trois types d'informations : les logs (les lignes que ton code écrit au fil de son exécution), les métriques (des chiffres : nombre d'invocations, durée, erreurs) et les événements. C'est la boîte noire de ton application — l'équivalent de l'enregistreur de vol d'un avion, inutile tant que tout va bien, et irremplaçable le jour de l'incident.

Le mécanisme est d'une discrétion totale. Chaque ligne que ta fonction écrit — un simple print, un console.log — CloudWatch la capte au vol. Chaque requête qui échoue, il l'enregistre. Le tout est rangé par log group, un groupe par fonction, pour que tu retrouves la trace de l'une sans fouiller dans celles des autres.

🔑 Et tout cela s'active sans rien configurer — ce qui, contrairement aux apparences, n'a rien de magique. Souviens-toi du rôle IAM de la section précédente : il contient la permission logs:PutLogEvents. C'est elle, et elle seule, qui autorise Lambda à écrire dans CloudWatch. Coupe-la, et le silence se fait. Les briques que tu assembles se tiennent par la main — c'est tout l'intérêt.

En pratique, dès que tu testes tes fonctions, leur activité et leurs erreurs filent toutes seules dans les logs. Tu ouvres le log group de la fonction, et tu lis exactement ce qui s'est passé — l'occasion, par exemple, de vérifier que ta logique de collision (le fameux 409 de la section Lambda) se déclenche bien quand elle le doit.

Lambda s'exécute
logs + métriques
CloudWatch

Le simulateur ci-dessous imite un log group en direct. Déclenche des invocations, réussies ou fautives, et regarde les lignes s'empiler avec leur horodatage pendant que les métriques se mettent à jour. C'est très exactement ce que tu fixes des yeux quand quelque chose cloche en production.

Invoque la fonction et regarde les logs et métriques arriver :

07 — SYNTHÈSE

Assembler les cinq briques

Cinq services, cinq métiers. Pris séparément, tu les connais désormais ; reste à voir comment ils s'emboîtent — et à quel moment ce modèle serverless est réellement le bon choix.

Tu as vu chaque pièce fonctionner pour son compte. Le moment est venu de prendre du recul et de les regarder s'emboîter. Si tu ne devais retenir qu'une chose de chacune, ce serait celle-ci :

ServiceSa responsabilité, en une phrase
DynamoDBTient le carnet d'adresses code → URL, lisible par clé en ~1 ms
LambdaPorte la logique : fabrique les codes, gère les collisions, redirige
IAMSécurise les accès entre services par des rôles temporaires
API GatewayExpose le système au public et le protège (clé, débit, quota)
CloudWatchObserve l'ensemble : logs, métriques, erreurs

Et quand une requête de création arrive, elle les traverse tous dans l'ordre, chacun jouant sa partition au passage :

Navigateur
API Gateway
Lambda
DynamoDB
CloudWatch

C'est en suivant ce trajet du regard que la beauté du modèle saute aux yeux : nulle part tu n'as eu à gérer un serveur, ni à anticiper le moindre pic de charge. Chaque brique est gérée par AWS, indépendante de ses voisines, et facturée à l'usage réel. Le système te coûte zéro quand il dort la nuit, et encaisse un afflux soudain de visiteurs sans que tu lèves le petit doigt.

💡 Est-ce toujours le bon choix ? Pas forcément, et il faut le dire honnêtement. Le serverless brille quand la charge est imprévisible ou intermittente et que les accès restent simples — une clé, une valeur, comme ici. En revanche, si ton trafic est massif et constant jour et nuit, ou si tes requêtes deviennent vraiment relationnelles (des jointures, des transactions qui touchent plusieurs tables à la fois), un serveur dédié ou une base SQL peut redevenir plus économique et plus naturel. L'architecture épouse le besoin ; elle ne suit pas la mode.
Ce que tu emportes
Tu ne repars pas seulement en sachant comment marche un raccourcisseur d'URL. Tu repars avec le schéma mental d'une architecture serverless complète : un stockage clé-valeur, du calcul qui ne s'éveille qu'à la demande, une sécurité fondée sur des rôles plutôt que sur des secrets, une porte d'entrée qui filtre le trafic, et de l'observabilité d'un bout à l'autre.

La suite logique, c'est le capstone : reconstruire le raccourcisseur entier à partir de zéro, en câblant toi-même les cinq services. Tu as désormais la carte en tête — il ne reste plus qu'à faire le voyage.