Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Séance 1 sur 39 livrablesQuiz corrigé

BrickFinder Le back qui voit

On construit, de zéro, le cerveau d'une appli qui regarde une photo de briques et en dresse l'inventaire. Aujourd'hui : le serveur qui parle à une IA de vision et renvoie un inventaire propre.

Par Josselyn C., prof de BTS SIO. Publié le , mis à jour le .

On construit
un back-end Node/Express qui reçoit une image → appelle Claude (vision) → renvoie du JSON vérifié
On installe
Node, Express, multer, dotenv, Zod — tout, pas à pas
Pré-requis
aucun. On part de « c'est quoi un back-end ? ». Un ordinateur, une connexion, la clé d'API fournie par le prof.

Milestone 1 sur 3. À la fin : tu envoies une photo à ton serveur, il te renvoie l'inventaire des pièces en JSON.

Le projet : BrickFinder

Tu connais peut-être Brickit : tu photographies ton tas de briques en vrac, et l'appli te dit ce que tu peux construire. On va bâtir une version à nous, BrickFinder, de zéro, en 3 séances. Aujourd'hui, on pose les fondations : le back-end qui « voit » les pièces.

Ce qu'on construit en tout (les 3 milestones)

Schéma : FRONT · React · Milestone 3 · BACK · Node / Express · Milestones 1 & 2 · IA vision (Claude) · lire la photo · Milestone 1 · API Rebrickable · catalogue des sets · Milestone 2 · photo

Ce que TON back saura faire ce soir (Milestone 1)

Ce que tu dois voir
Tu envoies une photo de briques à ton serveur
        ↓
Il la donne à l'IA de vision (Claude)
        ↓
Il te renvoie :  [ { "type":"brique2x4", "couleur":"rouge", "quantite":5 }, ... ]
                 (du JSON propre, vérifié, prêt pour la suite)
MilestoneCe qu'on construitQuand
1 · Le back qui voitserveur + upload + appel IA vision + JSON vérifiéaujourd'hui
2 · Le catalogue & le cerveauMongoDB + API Rebrickable + moteur de faisabilitéséance 2
3 · Le front & la sécuritéinterface React + sécurisation + finitionsséance 3

0. C'est quoi un « back-end » ?

Une appli web, c'est deux programmes qui se parlent.

Schéma : FRONT · le navigateur · BACK · ton serveur Node · 1. requête (« voici une photo ») · 2. réponse (« voici l'inventaire »)

Le front demande (une requête), le back répond (une réponse). Tout le Milestone 1 consiste à construire un back qui sait répondre à une requête du type « tiens, une photo — rends-moi l'inventaire ».

1. Les outils : terminal, Node, npm

Le terminal

Le terminal (ou « console ») est une fenêtre où tu tapes des commandes au lieu de cliquer. C'est là qu'on lance les programmes de dev.

C'est quoi Node ?

JavaScript, d'habitude, tourne dans le navigateur. Node.js, c'est ce qui permet de faire tourner du JavaScript en dehors du navigateur — sur un serveur, dans ton terminal. C'est le moteur de notre back.

Installer Node

Va sur nodejs.org, télécharge la version LTS (la stable), installe-la (Suivant → Suivant). Puis ferme et rouvre ton terminal, et vérifie :

powershell
node -v
Ce que tu dois voir
v22.9.0

Puis vérifie npm (installé en même temps) :

powershell
npm -v
Ce que tu dois voir
10.8.3

Flag A1

Checkpoint

Rends : la sortie de node -v et npm -v sur ta machine (capture ou recopie). Confirme que ta version de Node est ≥ 18.

0 sur 9 dans cette étape

2. Créer le projet

Le dossier du projet

On crée un dossier pour BrickFinder et on se place dedans. Dans le terminal :

powershell
mkdir brickfinder
cd brickfinder

Ouvrir le projet dans VS Code

Si tu as VS Code, tape (toujours depuis le dossier) :

powershell
code .

Initialiser le projet (package.json)

powershell
npm init -y

Activer les modules modernes

Ouvre package.json dans VS Code et ajoute une ligne "type": "module" (ça nous permet d'écrire import au lieu de l'ancien require) :

{
  "name": "brickfinder",
  "version": "1.0.0",
  "type": "module",
  "scripts": { "test": "echo \"no test\"" }
}

Premier fichier, premier lancement

Crée un fichier index.js (dans VS Code : clic droit → Nouveau fichier) avec :

console.log("BrickFinder démarre !")

Lance-le :

powershell
node index.js
Ce que tu dois voir
BrickFinder démarre !

Flag A2

Checkpoint

Modifie le message pour qu'il affiche BrickFinder par [ton prénom], relance. Rends : la sortie exacte de ton terminal.

0 sur 9 dans cette étape

3. Ton premier serveur (Express)

C'est quoi un serveur ?

Un serveur est un programme qui attend des requêtes et y répond. Il écoute sur un port (un numéro de « porte », ici 3000). Express est la librairie qui rend tout ça simple en Node.

Installer Express

powershell
npm install express
Ce que tu dois voir
added 69 packages in 2s

Écrire le serveur

Remplace le contenu de index.js par :

python
import express from "express"

const app = express()

app.get("/", (req, res) => {
  res.send("BrickFinder tourne !")
})

app.listen(3000, () => {
  console.log("Serveur prêt sur http://localhost:3000")
})

Lancer et tester

powershell
node index.js
Ce que tu dois voir
Serveur prêt sur http://localhost:3000

Le terminal ne rend pas la main : c'est normal, le serveur tourne. Ouvre ton navigateur sur http://localhost:3000 → tu dois voir « BrickFinder tourne ! ».

Arrêter / relancer sans douleur : nodemon

Pour arrêter le serveur : Ctrl + C dans le terminal. Relancer à la main à chaque modif, c'est pénible. On installe nodemon, qui redémarre tout seul à chaque sauvegarde :

powershell
npm install -D nodemon
powershell
npm run dev

Flag B1

Production

Ajoute une route GET /ping qui répond pong. Teste-la sur http://localhost:3000/ping. Rends : ton code de la route + une capture du navigateur affichant « pong ».

0 sur 9 dans cette étape

4. Renvoyer du JSON (une mini-API)

Jusqu'ici on renvoie du texte (res.send). Pour une appli, on renvoie plutôt du JSON — le format que les machines s'échangent (tu l'as vu avec les 1res années : { "cle": valeur }). Un serveur qui renvoie du JSON, on appelle ça une API.

Une route qui renvoie du JSON

Ajoute dans index.js, avant le app.listen :

app.get("/api/version", (req, res) => {
  res.json({ app: "BrickFinder", version: 1 })
})

Sauvegarde (nodemon redémarre), ouvre http://localhost:3000/api/version :

Ce que tu dois voir
{"app":"BrickFinder","version":1}

Flag B2

Production

Crée un endpoint GET /api/sante qui renvoie { "ok": true, "heure": ... } avec l'heure actuelle (indice : new Date().toISOString()). Rends : le JSON renvoyé par ton navigateur.

0 sur 9 dans cette étape

5. Recevoir une image (upload)

Notre serveur doit recevoir une photo envoyée par le client. Un fichier ne se transmet pas comme un simple texte : il voyage dans un format spécial appelé multipart/form-data. La librairie multer s'occupe de le décoder pour nous.

Installer multer

powershell
npm install multer

Configurer multer

En haut de index.js (sous les autres import) :

python
import multer from "multer"

const upload = multer({ storage: multer.memoryStorage() })

L'endpoint qui reçoit la photo

Ajoute (avant app.listen) :

app.post("/api/inventaire", upload.single("photo"), (req, res) => {
  console.log("Reçu :", req.file.originalname, "-", req.file.size, "octets")
  res.json({ recu: true, taille: req.file.size })
})

Tester un POST avec un fichier

Un POST avec fichier ne se teste pas dans la barre d'adresse (qui ne fait que des GET). Deux façons :

OutilComment
Thunder Client (extension VS Code)installe-la → New Request → méthode POST → URL http://localhost:3000/api/inventaire → onglet Body → Form → ajoute un champ Form File nommé photo → choisis une image → Send
curl (terminal)curl -F "photo=@ma-photo.jpg" http://localhost:3000/api/inventaire

Résultat attendu (dans la réponse et dans le terminal du serveur) :

Ce que tu dois voir
{"recu":true,"taille":84213}

(terminal) Reçu : ma-photo.jpg - 84213 octets

Flag B3

Production

Fais en sorte que la réponse renvoie aussi le type du fichier (indice : req.file.mimetype, ex. image/jpeg). Rends : le JSON renvoyé pour une photo de ton choix.

0 sur 9 dans cette étape

6. Les secrets : la clé d'API

Pour parler à l'IA de vision, il faut une clé d'API : un mot de passe secret qui identifie (et facture) les appels. Le prof te la fournit. Règle d'or : une clé ne se met jamais en dur dans le code, ne se commit jamais, et ne part jamais vers le front.

Le fichier .env

Crée un fichier nommé .env (avec le point) à la racine du projet :

ANTHROPIC_API_KEY=sk-ant-la-cle-donnee-par-le-prof

Lire le .env dans le code (dotenv)

powershell
npm install dotenv

Tout en haut de index.js (première ligne) :

python
import "dotenv/config"

Ne JAMAIS partager ses secrets : .gitignore

Crée un fichier .gitignore à la racine :

node_modules
.env

Vérifie vite que la clé est bien lue (ajoute temporairement) :

console.log("Clé chargée ?", process.env.ANTHROPIC_API_KEY ? "oui" : "NON")
Ce que tu dois voir
Clé chargée ? oui

Flag D1

Investigation

Cherche (doc, cours) : que pourrait faire un attaquant qui récupère une clé d'API laissée dans du code front (côté navigateur) ? Rends : 2 conséquences concrètes, et la règle en une phrase. (On y reviendra au Milestone 3.)

0 sur 9 dans cette étape

7. Appeler l'IA de vision

Le cœur du Milestone. On va envoyer l'image reçue à Claude (une IA qui sait lire des images) et lui demander l'inventaire en JSON. On construit l'appel étape par étape.

On transforme l'endpoint du chapitre 5 en async, et on le construit en 4 étapes.

Étape 1 — encoder l'image

const imageBase64 = req.file.buffer.toString("base64")
const type = req.file.mimetype   // ex. "image/jpeg"

Étape 2 — le corps de la requête

const corps = {
  model: "claude-sonnet-5-5",
  max_tokens: 1024,
  messages: [{
    role: "user",
    content: [
      { type: "image", source: { type: "base64", media_type: type, data: imageBase64 } },
      { type: "text", text: "Identifie les pièces de construction sur la photo. Pour chaque type, donne son nom standard en anglais facon LEGO (ex. 'Brick 2 x 4', 'Plate 1 x 2', 'Tile 1 x 1'), sa couleur standard en anglais (ex. 'Red', 'Blue', 'Yellow'), et la quantite. Reponds UNIQUEMENT en JSON : un tableau d'objets {type, couleur, quantite}. Aucun texte autour." }
    ]
  }]
}

Étape 3 — envoyer à Claude

const reponse = await fetch("https://api.anthropic.com/v1/messages", {
  method: "POST",
  headers: {
    "x-api-key": process.env.ANTHROPIC_API_KEY,
    "anthropic-version": "2023-06-01",
    "content-type": "application/json"
  },
  body: JSON.stringify(corps)
})

Étape 4 — lire la réponse

const data = await reponse.json()
const texte = data.content[0].text
res.json({ brut: texte })

N'oublie pas de marquer la fonction async :

app.post("/api/inventaire", upload.single("photo"), async (req, res) => {
  // ... les 4 étapes ci-dessus ...
})

Teste avec une vraie photo de pièces (Thunder Client / curl). Tu obtiens quelque chose comme :

Ce que tu dois voir
{"brut":"[\n  {\"type\":\"brique2x4\",\"couleur\":\"rouge\",\"quantite\":5},\n  {\"type\":\"plaque\",\"couleur\":\"bleu\",\"quantite\":3}\n]"}

Flag B4

Production (le gros morceau)

Fais fonctionner l'endpoint de bout en bout : une photo entre, l'inventaire (brut) de Claude sort. Rends : la photo utilisée + le JSON brut renvoyé par ton serveur. Compare-le à la réalité : l'IA a-t-elle bien compté ?

0 sur 9 dans cette étape

8. Fiabiliser la sortie de l'IA

Ton endpoint marche ! Mais rappelle-toi la leçon des 1res années : l'IA se trompe et peut mal formater sa réponse. Parfois elle entoure le JSON de texte (« Voici l'inventaire : … »), ou l'enveloppe dans des ```. Si on fait confiance aveuglément, le programme plante. On fiabilise.

Étape 1 — nettoyer, puis parser

const propre = texte.replace(/```json|```/g, "").trim()

let inventaire
try {
  inventaire = JSON.parse(propre)
} catch (e) {
  return res.status(422).json({ erreur: "L'IA n'a pas renvoyé un JSON lisible" })
}

Étape 2 — valider le format (Zod)

Parser ne suffit pas : il faut vérifier que chaque pièce a bien un type, une couleur (textes) et une quantite (nombre entier positif). On ne fait jamais confiance à une donnée venue de l'extérieur — règle de sécurité.

powershell
npm install zod

En haut de index.js :

python
import { z } from "zod"

const Piece = z.object({
  type: z.string(),
  couleur: z.string(),
  quantite: z.number().int().positive()
})
const Inventaire = z.array(Piece)

Étape 3 — appliquer le schéma

const verif = Inventaire.safeParse(inventaire)
if (!verif.success) {
  return res.status(422).json({ erreur: "Le JSON ne respecte pas le format attendu" })
}

res.json({ inventaire: verif.data })

Flag C1

Debug

Teste la robustesse : modifie temporairement le prompt pour que l'IA réponde en phrases (ex. retire « UNIQUEMENT en JSON »). Observe que ton serveur renvoie proprement l'erreur 422 au lieu de planter. Rends : la réponse d'erreur obtenue + une phrase expliquant pourquoi le try/catch t'a sauvé.

0 sur 9 dans cette étape

Flag E1

Extension

Ajoute au schéma une règle : refuser une quantite supérieure à 999 (indice : .max(999)). Teste. Rends : ta ligne de schéma modifiée.

0 sur 9 dans cette étape

Bravo — ton back voit !

Récapitulons ce que ton serveur sait faire, de zéro, à la fin de ce Milestone 1 :

BriqueCe que tu sais faire
Node / npminstaller Node, créer un projet, ajouter des librairies
Expressmonter un serveur, créer des routes GET/POST, renvoyer du JSON
multerrecevoir un fichier image envoyé par un client
.env / dotenvgarder une clé d'API secrète, hors du code et de Git
fetch + IA visionenvoyer une image à Claude et récupérer sa réponse
JSON.parse + Zodparser et valider une réponse d'IA, sans jamais planter

Mini-quiz (vérifie-toi)

Exercice Réussi À revoir

  1. Quelle est la différence entre le front-end et le back-end ?
  2. Pourquoi met-on la clé d'API dans un .env et pas directement dans index.js ?
  3. À quoi sert await devant fetch(...) ?
  4. Pourquoi JSON.parse est-il entouré d'un try / catch ?
  5. Que vérifie le schéma Zod que JSON.parse ne vérifie pas ?
Voir le corrigéMasquer le corrigé de l'exercice

1. Le front = ce que l'utilisateur voit (le navigateur) ; le back = le serveur qui traite et répond, invisible.
2. Pour qu'elle reste secrète : hors du code, exclue de Git (.gitignore), jamais envoyée au front. Une clé qui fuit coûte cher.
3. À attendre la réponse de l'IA (qui prend du temps) avant de continuer.
4. Parce que si l'IA renvoie un texte qui n'est pas du JSON valide, parse lève une erreur : le try/catch l'attrape et évite le crash.
5. Que la structure est correcte : un tableau d'objets avec type/couleur (textes) et quantite (entier positif). parse dit juste « c'est du JSON », Zod dit « c'est le bon JSON ».

Tu avais trouvé ?

Tu es bloqué ? La checklist

SymptômeEssaie ça
node / npm « non reconnu »ferme et rouvre le terminal ; sinon réinstalle Node (LTS) et redémarre
Cannot find module 'express'tu as oublié npm install express (depuis le dossier du projet)
SyntaxError: Cannot use importajoute "type": "module" dans package.json
Le navigateur ne charge pas localhost:3000le serveur tourne-t-il ? (npm run dev) ; bon port ?
EADDRINUSEun serveur occupe déjà le port → Ctrl + C dans l'autre terminal, ou change de port
req.file est undefinedchamp du fichier mal nommé (doit être photo) ou body pas en Form/multipart
401 en appelant l'IAclé absente/fausse : vérifie .env et import "dotenv/config" en 1re ligne
Le serveur plante sur certaines réponses IAc'est le chapitre 8 : parse + Zod dans un try/catch

Glossaire

MotDéfinition
Front / Back-endla salle (ce qu'on voit) / la cuisine (le serveur qui traite)
Node.jsfait tourner du JavaScript hors du navigateur (notre serveur)
npmgestionnaire de librairies de Node (npm install ...)
package.jsonfiche du projet : nom, version, librairies requises
Expresslibrairie pour créer un serveur web simplement
route / endpointune adresse que le serveur sait traiter (/api/inventaire)
port / localhostla « porte » du serveur (3000) / ta propre machine
API RESTun serveur qui expose des endpoints JSON
multipart / multerformat d'envoi de fichiers / librairie qui le décode
variable d'environnement / .envun secret (clé) rangé hors du code
.gitignoreliste des fichiers que Git doit ignorer (dont .env)
async / awaitattendre le résultat d'une opération qui prend du temps
fetchenvoyer une requête sur Internet depuis le code
base64une image (ou un fichier) transformée en texte
JSON.parsetransformer du texte JSON en objet JavaScript
Zod / schémadécrire et valider la forme attendue d'une donnée
422 / 401codes d'erreur : donnée invalide / non autorisé (clé)

Compétences mobilisées — référentiel BTS SIO SLAM

Séance 1 terminé ?

Coche chaque livrable quand tu l'as : c'est la preuve que l'étape est acquise.

livrables
–
quiz réussi
–
de série
–
Passer à la séance 2 : BrickFinder Le catalogue & le cerveauVoir toutes les étapes du TP