3.4SQL avec Node (PostgreSQL/MySQL, Prisma/Sequelize)
À côté de MongoDB (NoSQL), l'autre grande famille de bases de données est le SQL / relationnel — souvent le choix privilégié pour les données structurées et fortement relationnelles. Les bases relationnelles les plus utilisées avec Node sont PostgreSQL (très apprécié, puissant, open source — souvent le premier choix) et MySQL/MariaDB. Le modèle relationnel organise les données en tables (avec des colonnes typées), les enregistrements en lignes, et établit des relations formelles entre tables via des clés étrangères (une commande référence un utilisateur, un produit appartient à une catégorie…). Le langage pour interroger ces bases est le SQL (Structured Query Language) : SELECT (lire), INSERT (créer), UPDATE (modifier), DELETE (supprimer), avec des jointures (JOIN) pour combiner des tables liées. Les bases relationnelles offrent des garanties fortes (schéma strict, transactions ACID pour la cohérence) qui les rendent idéales pour les données à intégrité critique.
Comment utiliser une base SQL depuis Node ? Plusieurs niveaux d'abstraction. Le plus bas : un driver/client brut (comme pg pour PostgreSQL) où l'on écrit du SQL directement dans des chaînes — puissant mais verbeux, et attention aux injections SQL (une faille critique : ne JAMAIS concaténer des données utilisateur dans une requête SQL — utiliser des requêtes paramétrées). Plus haut niveau : un ORM (Object-Relational Mapper) qui fait correspondre les tables à des objets JavaScript et génère le SQL pour vous. Les ORM populaires avec Node : Prisma (moderne, très apprécié — on définit un schéma dans un fichier, Prisma génère un client typé et sûr ; excellent avec TypeScript) et Sequelize (plus ancien, éprouvé, avec des modèles et des méthodes). Avec un ORM, on manipule des objets et des méthodes (prisma.user.create({...}), prisma.user.findMany({...})) plutôt que du SQL brut — plus lisible, plus sûr (protection contre les injections), plus productif. Prisma se distingue par son approche moderne : un schéma déclaratif (le fichier schema.prisma décrit vos modèles et relations), des migrations (versionner l'évolution du schéma de base de données), et un client généré et typé (autocomplétion, sécurité des types — un atout majeur avec TypeScript, section 5-1). Le choix SQL vs NoSQL (voir 3-3) dépend de vos données : le relationnel brille pour les données structurées, fortement liées, à intégrité critique. Le choix driver brut vs ORM dépend du contexte : un ORM (Prisma, Sequelize) est recommandé pour la plupart des applications (productivité, sécurité, lisibilité), tandis que le SQL brut garde sa place pour des requêtes très pointues ou des besoins de contrôle fin. Maîtriser au moins une approche relationnelle (idéalement un ORM moderne comme Prisma) complète votre boîte à outils de persistance : vous savez alors travailler avec les DEUX grandes familles de bases (documentaire et relationnelle), et choisir la bonne selon le projet — une compétence complète et recherchée. Que vous utilisiez SQL ou NoSQL, l'essentiel est de savoir modéliser des données, faire du CRUD, gérer les relations et écrire des requêtes efficaces et sûres.
Vocabulaire de la section
- SQL / relationnel
- Bases organisant les données en tables (colonnes typées), lignes et relations via clés étrangères ; PostgreSQL, MySQL. Garanties fortes (schéma strict, transactions ACID).
- SQL (langage)
- Structured Query Language : SELECT (lire), INSERT (créer), UPDATE (modifier), DELETE (supprimer), JOIN (combiner des tables liées).
- Injection SQL
- Faille critique : ne JAMAIS concaténer des données utilisateur dans une requête SQL ; utiliser des requêtes paramétrées (les ORM protègent automatiquement).
- ORM (Prisma, Sequelize)
- Object-Relational Mapper faisant correspondre tables et objets JS et générant le SQL ; manipulation d'objets/méthodes plutôt que de SQL brut (lisible, sûr, productif).
- Prisma (moderne)
- ORM apprécié : schéma déclaratif (schema.prisma), migrations (versionner le schéma), client généré et TYPÉ (autocomplétion, sécurité des types — excellent avec TypeScript).
Comment se protéger de l'INJECTION SQL ?
En pratique — Persister avec une base SQL et un ORM
- Comprenez le modèle relationnel : tables, colonnes typées, lignes, relations par clés étrangères ; et les opérations SQL de base (SELECT, INSERT, UPDATE, DELETE, JOIN).
- Retenez l'impératif de sécurité : ne JAMAIS concaténer des données utilisateur dans du SQL (injection) — requêtes paramétrées ou ORM.
- Découvrez un ORM moderne : définissez un schéma (Prisma : schema.prisma) et faites un CRUD via des méthodes (prisma.user.create/findMany/update/delete).
- Explorez les migrations Prisma (versionner l'évolution du schéma) et l'intérêt du client typé (autocomplétion, sécurité des types avec TypeScript).
Points clés à retenir
- SQL / RELATIONNEL (PostgreSQL, MySQL) : données en TABLES/LIGNES avec relations par CLÉS ÉTRANGÈRES ; garanties fortes (schéma strict, transactions ACID). Idéal pour données structurées et fortement liées.
- Langage SQL : SELECT/INSERT/UPDATE/DELETE + JOIN. Sécurité CRITIQUE : jamais concaténer des données utilisateur (INJECTION SQL) — requêtes paramétrées.
- Un ORM (PRISMA, Sequelize) mappe tables ↔ objets JS et génère le SQL : on manipule des objets/méthodes (lisible, sûr contre les injections, productif) plutôt que du SQL brut.
- PRISMA (moderne) : schéma déclaratif + migrations + client TYPÉ (autocomplétion, sécurité des types, excellent avec TypeScript). Maîtriser SQL + NoSQL = boîte à outils de persistance complète.
Questions fréquentes
Driver SQL brut ou ORM (Prisma/Sequelize) : que choisir, et pourquoi les ORM sont-ils populaires ?
Pour la plupart des applications, un ORM (comme Prisma) est recommandé pour sa productivité, sa sécurité et sa lisibilité, mais le SQL brut garde sa place pour des besoins spécifiques — comprendre les compromis vous aide à choisir. Ce qu'est chaque approche : (1) Driver/client brut (ex. pg pour PostgreSQL) : vous écrivez le SQL directement (dans des chaînes) et l'envoyez à la base. Contrôle total, mais vous gérez tout à la main. (2) ORM (Prisma, Sequelize) : une couche qui fait correspondre les tables à des objets/modèles JavaScript et GÉNÈRE le SQL pour vous. Vous manipulez des objets et des méthodes (prisma.user.findMany({ where: {...} })) au lieu d'écrire du SQL. Pourquoi les ORM sont populaires (leurs avantages) : (1) Productivité : écrire prisma.user.create({ data: {...} }) est plus rapide et plus lisible que composer une requête SQL INSERT à la main. Les opérations courantes (CRUD, requêtes filtrées, relations) sont concises. (2) SÉCURITÉ contre les injections SQL : c'est un avantage MAJEUR. L'injection SQL (une faille critique où un attaquant injecte du SQL malveillant via des données utilisateur) est un risque grave avec le SQL brut mal fait (concaténation de données utilisateur dans une requête). Les ORM utilisent des requêtes paramétrées automatiquement — ils échappent/paramètrent les données, éliminant cette classe de failles par défaut. Un énorme gain de sécurité « gratuit ». (3) Lisibilité et maintenabilité : le code ORM (objets, méthodes) est souvent plus lisible et cohérent que des chaînes SQL éparpillées. (4) Abstraction de la base : un ORM peut (dans une certaine mesure) fonctionner avec différentes bases SQL, facilitant un changement de base (moins de SQL spécifique). (5) Fonctionnalités intégrées : gestion des relations (récupérer des données liées facilement), migrations (versionner l'évolution du schéma), typage (Prisma génère un client TYPÉ — autocomplétion et sécurité des types, excellent avec TypeScript), validation, etc. (6) Modélisation claire : Prisma a un schéma déclaratif (schema.prisma) qui décrit vos modèles et relations en un endroit — clair et maintenable. Les avantages du SQL BRUT (quand l'ORM montre ses limites) : (1) Contrôle total et requêtes pointues : pour des requêtes très complexes, optimisées, ou utilisant des fonctionnalités spécifiques de la base, le SQL brut donne un contrôle que l'ORM ne permet pas toujours (les ORM peuvent générer du SQL sous-optimal sur des cas complexes). (2) Performance fine : sur des requêtes critiques, écrire le SQL à la main permet d'optimiser précisément. (3) Transparence : vous savez EXACTEMENT quelle requête s'exécute (avec un ORM, le SQL généré est parfois opaque, et un ORM mal utilisé peut générer des requêtes inefficaces — le fameux problème N+1). (4) Pas de couche supplémentaire : moins de dépendances, moins de « magie ». Comment choisir : (1) Pour la PLUPART des applications → un ORM moderne (Prisma recommandé) : productivité, sécurité contre les injections, lisibilité, typage — les gains l'emportent largement pour le développement d'applications courant. C'est le choix par défaut sensé. (2) Pour des requêtes TRÈS complexes ou critiques en performance → du SQL brut ponctuel (la plupart des ORM, dont Prisma, permettent d'exécuter du SQL brut quand nécessaire — on n'est pas enfermé : ORM pour le courant, SQL brut pour les cas pointus). (3) Une bonne approche courante : ORM pour 95% des opérations (le CRUD et les requêtes standard), SQL brut pour les 5% de requêtes complexes/optimisées. Points de vigilance avec les ORM : (1) comprenez le SQL SOUS-JACENT (un ORM ne dispense pas de comprendre les bases relationnelles et le SQL — sinon vous générez des requêtes inefficaces sans le savoir). (2) Attention au problème N+1 (un ORM mal utilisé qui fait une requête par élément d'une liste au lieu d'une seule — dévastateur pour la performance) — apprenez à charger les relations efficacement. (3) L'ORM est un OUTIL, pas une excuse pour ignorer les bases de données — les meilleurs développeurs comprennent le SQL ET utilisent un ORM intelligemment. Recommandation : (1) apprenez les BASES du SQL et du modèle relationnel (indispensable, transversal) ; (2) utilisez un ORM moderne (Prisma) pour la productivité et la sécurité dans vos applications ; (3) sachez descendre au SQL brut pour les cas pointus ; (4) comprenez ce que l'ORM génère (ne le traitez pas comme une boîte noire magique). En résumé : les ORM (Prisma) sont populaires et recommandés pour leur productivité, leur sécurité (injections) et leur lisibilité — le bon choix par défaut ; le SQL brut garde sa place pour les requêtes complexes/critiques. Utilisez un ORM en comprenant le SQL sous-jacent — c'est la combinaison gagnante : la productivité et la sécurité de l'ORM, avec la maîtrise des bases relationnelles qui vous permet de l'utiliser efficacement et de descendre au SQL quand il le faut.
L'injection SQL est mentionnée comme une faille critique : qu'est-ce que c'est et comment s'en protéger ?
L'injection SQL est l'une des failles de sécurité les plus classiques et dangereuses des applications web, et la comprendre (avec sa protection) est ESSENTIEL — c'est un savoir de sécurité fondamental pour tout développeur qui touche à une base de données. Ce qu'est l'injection SQL : c'est une attaque où un attaquant INJECTE du code SQL malveillant via les données qu'il envoie à votre application (un champ de formulaire, un paramètre d'URL…), et où ce code se retrouve EXÉCUTÉ par votre base de données. Comment ? Si vous construisez une requête SQL en CONCATÉNANT directement des données utilisateur, l'attaquant peut « sortir » du contexte de donnée pour injecter du SQL. Exemple simplifié du DANGER : imaginez une requête de connexion construite ainsi (MAUVAIS) : "SELECT * FROM users WHERE email = '" + emailSaisi + "' AND password = '" + mdpSaisi + "'". Si l'attaquant saisit comme email ' OR '1'='1 (avec une syntaxe adaptée), la requête devient SELECT * FROM users WHERE email = '' OR '1'='1' AND ... — la condition '1'='1' est toujours vraie, et l'attaquant peut contourner l'authentification, voire (avec des injections plus élaborées) LIRE toute la base, la MODIFIER, ou la DÉTRUIRE ('; DROP TABLE users; --). Les conséquences sont catastrophiques : vol de données (mots de passe, données personnelles), contournement d'authentification, modification/destruction de données, compromission complète. L'injection SQL figure régulièrement parmi les failles web les plus critiques et les plus exploitées. La cause profonde : mélanger du CODE (la requête SQL) et des DONNÉES (les entrées utilisateur) par concaténation, de sorte que des données puissent être interprétées comme du code. Comment s'en PROTÉGER : (1) Les REQUÊTES PARAMÉTRÉES (prepared statements) — la protection fondamentale. Au lieu de concaténer les données dans la requête, on utilise des « paramètres » (des emplacements) et on passe les données SÉPARÉMENT. La base traite alors les données comme des DONNÉES pures (jamais comme du code SQL), quelle que soit leur contenu. Exemple (BON, avec le driver pg) : client.query('SELECT * FROM users WHERE email = $1', [emailSaisi]) — $1 est un paramètre, emailSaisi est passé à part, et même si l'utilisateur saisit du SQL malveillant, il sera traité comme une simple chaîne, pas exécuté. C'est LA solution : ne jamais concaténer, toujours paramétrer. (2) Utiliser un ORM (Prisma, Sequelize). Les ORM utilisent des requêtes paramétrées AUTOMATIQUEMENT sous le capot — quand vous écrivez prisma.user.findMany({ where: { email } }), l'ORM paramètre correctement, vous protégeant par défaut contre l'injection SQL. C'est un avantage de sécurité majeur des ORM (mentionné dans la FAQ précédente) : en les utilisant normalement, vous êtes protégé sans effort. (3) VALIDER et assainir les entrées (défense en profondeur) : valider que les données ont le format attendu (section 4-4) réduit la surface d'attaque — mais ce n'est PAS suffisant seul (la protection principale reste les requêtes paramétrées). (4) Principe du moindre privilège : le compte de base de données utilisé par l'application ne devrait avoir que les droits nécessaires (limite les dégâts en cas de faille). La règle absolue à retenir : ne JAMAIS construire une requête SQL en concaténant des données utilisateur. Toujours utiliser des requêtes paramétrées (ou un ORM qui le fait pour vous). C'est non négociable. Un développeur qui concatène des entrées dans du SQL crée une faille critique, quasi certainement exploitable. Points importants : (1) L'injection SQL concerne le SQL, mais des attaques SIMILAIRES existent pour d'autres systèmes (injection NoSQL avec MongoDB si on construit des requêtes à partir d'entrées non validées, injection de commandes, etc.) — le principe général est le même : ne jamais faire confiance aux entrées utilisateur, ne jamais les mêler à du code/des commandes sans précaution. (2) C'est un cas particulier d'un principe de sécurité universel : « ne jamais faire confiance aux entrées utilisateur » — toujours les traiter comme potentiellement malveillantes, les valider, et les manipuler de façon sûre (paramétrage, échappement selon le contexte). (3) La sécurité (section 4-2) est un aspect essentiel du développement back-end — l'injection SQL n'est qu'une des failles à connaître (avec l'authentification, XSS, CSRF, etc.). En résumé : l'injection SQL est une faille critique où un attaquant injecte du SQL malveillant via des données utilisateur concaténées dans une requête, avec des conséquences graves (vol/destruction de données, contournement d'auth). La protection : requêtes PARAMÉTRÉES (jamais de concaténation) — et les ORM le font automatiquement. C'est un savoir de sécurité fondamental : ne jamais faire confiance aux entrées, toujours paramétrer. Le connaître et l'appliquer systématiquement est une responsabilité de tout développeur — une faille d'injection SQL peut ruiner une application et compromettre les données de ses utilisateurs. La sécurité n'est pas optionnelle.