VA
Comment lire ce livre ? Qui sommes nous ?
I. PAGES WEB
II. SERVEUR WEB
III. BASES DE DONNEES
IV. INTERACTION FRONT-BACK
Annexe I. ENSEIGNEMENT NSI
Annexe II. LES FRAMEWORKS

Chapitre 8 - Lien entre Serveur et Base de données

Une application web moderne est constituée d'un serveur web qui répond aux requêtes envoyées par les clients et d'une base de données aide le serveur web en stockant l'information utile et en facilitant les accès en lecture et en écriture.

Le serveur web et la base de données sont donc deux composants distincts de l'application. Ce chapitre présente la façon dont mettre en oeuvre leur connexion et explique comment réaliser leurs interactions.

Connecter le serveur avec la base de données

Par souci de simplicité nous considérons une application web constituée d'un unique serveur web et d'une unique base de données (les applications complexes peuvent être constituées de plusieurs serveurs et de plusieurs bases de données). Le serveur web et la base de données sont donc connectés mais il faut comprendre que c'est le serveur web qui initie la connexion. En effet, le serveur web envoie des requêtes SQL à la base de données qui les exécute et renvoie les réponses. Grâce aux réponses transmises par la base de données, le serveur web peut alors répondre aux requêtes des navigateurs web et générer les pages web qu'ils s'attendent à recevoir. Dit autrement, la base de données est dans l'attente de requêtes du serveur web qui visent soit à lire des données dans la base de données, soit à écrire des données.

L'envoi de requêtes SQL du serveur web vers la base de données se fait grâce à un protocole de communication. Il n'existe malheureusement pas de standard pour ce protocole et chaque base de données propose son propre protocole. Fort heureusement toutes les bases de données proposent des drivers qui supportent la connexion à la base de données et l'envoi de requêtes SQL, et ce pour n'importe quel langage de programmation.

Définition
Toutes les bases de données proposent leur protocole de communication. Elles proposent aussi des drivers dans différents langages de programmation pour faciliter l'intégration de leur protocole dans les applications.

Dans ce livre, nous avons fait le choix d'utiliser la base de données Postgres et de coder notre serveur en JavaScript. Postgres est une base de données très utilisée, OpenSource et disponible sur tous les systèmes d'exploitation. Pour exploiter Postgres dans notre application, nous devons donc utiliser un driver JavaScript pour Postgres. Il existe plusieurs drivers possibles qui jouent ce rôle. Nous avons choisi d'utiliser le driver pg qui est un des plus populaires (https://github.com/brianc/node-postgres).

Pour utiliser le driver pg pour Javascript, il faut le télécharger et l'importer dans le code du serveur web. Le téléchargement du driver peut se faire en utilisant npm qui est un gestionnaire de bibliothèques pour Javascript. L'utilisation de npm nécessite une initialisation du projet à l'aide de la commande suivante dans votre terminal :

npm init

L'initialisation va poser plusieurs questions pour renseigner le nom du projet, son auteur, sa licence, etc. Lorsque le projet sera initialisé, un fichier package.json sera créé et contiendra toutes les informations du projet. On pourra alors effectuer le téléchargement du driver pg grâce à la commande suivante :

npm install pg --save

Le driver pg sera alors téléchargé et stocké dans le répertoire node_modules. Il pourra être utilisé dans le code du serveur web.

Le Code 8.1 présente la création d'une connexion avec la base de données Postgres en utilisant le driver pg. La première ligne permet d'importer le driver pg dans le code du serveur web. L'importation se fait grâce à la fonction require qui est une fonction JavaScript permettant d'importer des modules. Cette fonction retourne une classe nommée Client qui sera utilisée pour créer une connexion avec la base de données.

Les lignes 3 à 8 permettent de configurer une connexion avec la base de données. Le constructeur (new) de la classe Client prend en paramètre un objet qui contient les informations nécessaires pour se connecter à la base de données. Dans notre cas, nous devons renseigner le nom de l'utilisateur, son mot de passe, le nom de la base de données et le port utilisé par la base de données. Ces informations dépendent du contexte dans lequel s'exécute votre application et doivent être adaptées en fonction de la configuration de votre base de données. Dans notre cas, nous utilisons une base de données Postgres qui est installée en local sur notre machine. Le nom de l'utilisateur est postgres, le mot de passe est root, le nom de la base de données est application_image et le port utilisé par la base de données est 5432 (port par défaut). L'appel au constructeur retourne un objet nommé client qui permettra de réaliser la connexion avec la base de données.

Les lignes 10 à 17 permettent de mettre en place la connexion avec la base de données. La méthode connect appelée sur l'objet client permet de se connecter à la base de données. Comme cette fonction peut prendre du temps, elle retourne donc une promesse Javascript qui est un objet possédant deux fonctions then et catch qui seront exécutées respectivement en cas de succès ou d'échec. Dans notre cas, nous nous contentons d'afficher un message de succès ou d'échec. Notons qu'en cas de succès, il sera alors possible d'utiliser l'objet client pour envoyer des requêtes à la base de données.

const { Client } = require('pg');

const client = new Client({
    user: 'postgres', 
    password: 'root', 
    database: 'application_image',
    port : 5432 
});

client.connect()
.then(() => {
    console.log('Connected to database');
})
.catch((e) => {
    console.log('Error connecting to database');
    console.log(e);
}); 

Code 8.1 : Connexion à la base de données Postgres.

Le Code 8.2 présente un exemple de requête SQL envoyée à la base de données. La requête est envoyée grâce à la méthode query de l'objet client. Cette méthode prend en paramètre une chaîne de caractères qui contient la requête SQL à exécuter. Dans notre exemple nous envoyons la requête SELECT * FROM images qui permet de récupérer toutes les images de la table.

client.query('SELECT * FROM images')
.then((res) => {
    console.log(res.rows);
})
.catch((e) => {
    console.log('Error executing query');
    console.log(e);
}); 

Code 8.2 : Envoi d'une requête à la base de données Postgres.

La méthode query retourne elle aussi une promesse. Si la requête est un succès, alors la fonction then est exécutée. Celle-ci prend en paramètre un objet (res) qui contient le résultat de la requête. Cet objet est complexe car il contient plusieurs propriétés. L'une d'entre elles, la propriété rows est un tableau qui représente les lignes de la table résultat. Le Code 8.3 présente un exemple de code montrant la structure de l'objet res. On voit que cet objet a une propriété rows qui est un tableau d'objets représentant les lignes de la table résultat. Cet exemple de code illustre aussi (dernière ligne) la façon d'accéder à une valeur particulière du résultat (i.e. le nom du fichier de la deuxième image).

// résultat obtenu après client.query('SELECT * FROM images')
// on suppose que la requête retourne trois images
let res = {
    rows : [
        {
            id: 1,
            nom:"Portrait Bleu",
            fichier: "Image1.jpg"
        },
        {
            id: 2,
            nom:"Marcheur",
            fichier: "Image2.jpg"
        },
        {
            id: 3,
            nom:"Sol Rouge",
            fichier: "Image3.jpg"
        }
    ]
};
let nomFichierDeuxiemeImage = res.rows[1].fichier;

Code 8.3 : Description de l'objet obtenu en résultat de l'exécution d'une requête.

Le Code 8.4 présente le même exemple de requête que le code 8.2 mais utilise le mécanisme await proposé par Javascript pour faciliter la manipulation des promesses. Ce mot clé permet de dire au moteur d'exécution Javascript d'attendre la fin de l'exécution de la fonction appelée avant de continuer. Le code Javascript est ainsi exécuté de manière synchrone,. Cela simplifie l'écriture du code et sa compréhension. Il faut noter pour autant que le mécanisme await doit être utilisé dans un contexte explicitement asynchrone (le mot clé async doit être explicite dans le code) ce que ne montre pas notre exemple. La ligne 53 du code du serveur web montre comment expliciter un contexte asynchrone (on voit le mot clé async dans la signature de la fonction qui contient le code où se trouve le mot clé await).

let res = await client.query('SELECT * FROM images') //La ligne suivante sera exécutée après la fin de l'exécution de la requête
console.log(res.rows);

Code 8.4 : Envoi de requêtes à la base de données Postgres en utilisant le mécanisme await.

Applications web et lecture des données

Nous illustrons la connexion entre un serveur web et une base de données grâce à notre application de gestion d'images. La base de données de cette application se nomme application_image, elle contient la table images qui précise les informations relatives aux images (leur nom et leur fichier). Le Tableau 8.1 montre le contenu de la table images.

ID Nom Fichier
1 Portrait Bleu Image1.jpg
2 Marcheur Image2.jpg
3 Sol Rouge Image3.jpg
4 Bulles chaudes Image4.jpg
5 Nature morte Image5.jpg
6 Grotte vivante Image6.jpg
7 Travail Soir Image7.jpg
8 Citron Salade Image8.jpg
9 Flou artistique Image9.jpg

Tableau 8.1 : Table des images utilisée comme exemple pour illustrer la connexion entre un serveur web et une base de données.

Le Code 8.5 présente le code du serveur web qui permet d'afficher le mur d'images en consultant la table des images. Les lignes 1 à 3 permettent d'importer les modules nécessaires au fonctionnement du serveur web. Les lignes 5 à 7 permettent de créer le serveur web. Les lignes 9 à 23 permettent de créer une connexion avec la base de données. Les lignes 25 à 67 permettent de gérer les requêtes reçues par le serveur web. Les lignes 26 à 41 permettent de gérer les requêtes qui demandent des ressources statiques (fichiers HTML, CSS, JS, images, etc.).

Nous nous intéressons plus particulièrement aux lignes 42 à 66 qui gèrent l'affichage du mur d'images. Il s'agit ici de créer une page HTML qui contient autant de balises <img> qu'il y a d'images dans la base de données. Pour cela, il faut d'abord envoyer une requête à la base de données pour récupérer les fichiers des images. La requête SQL est intégrée au code JS comme une chaîne de caractères (ligne 44). La ligne 45 permet d'exécuter la requête et de récupérer le résultat dans l'objet sqlResult. Il faut ensuite parcourir toutes les lignes de ce résultat pour récupérer les fichiers des images (ligne 46). Nous utilisons ici la fonction map sur le champ rows de l'objet sqlResult, ce qui permet de parcourir toutes les lignes du résultat et de retourner un tableau contenant uniquement les fichiers des images. La variable fichiersImage contient donc un tableau de chaînes de caractères qui correspond aux fichiers des images. C'est ce tableau qui est utilisé dans la suite du code pour générer les balises <img> (boucle for des lignes 62 à 66). Ainsi, la page HTML générée et retournée au navigateur (ligne 70) contient bien autant de balises <img> qu'il y a d'images dans la base de données.

const fs = require("fs");
const http = require("http");
const { Client } = require('pg');

const host = 'localhost';
const port = 8080;
const server = http.createServer();

const client = new Client({
    user: 'postgres', 
    password: 'root', 
    database: 'application_image',
    port : 5432 
});

client.connect()
.then(() => {
    console.log('Connected to database');
})
.catch((e) => {
    console.log('Error connecting to database');
    console.log(e);
}); 

server.on("request", async (req, res) => {
    if (req.url.startsWith('/public/')) {
        try {
            const ressource = fs.readFileSync("./" + req.url);
            res.end(ressource);
        } catch (error) {
            let html = '<html><body><h1>404 Error</h1>';
            html += '<p>Page not found</p>';
            html += '<a href="/">Retour à l\'accueil</a>';
            html += '</body></html>';
            res.statusCode = 404;
            res.end(html);
        } 
    } else if (req.url === '/mur-images') {
        try {
            const sqlQuery = 'SELECT fichier from images'; 
            const sqlResult = await client.query(sqlQuery); 
            const fichiersImage = sqlResult.rows.map(row => row.fichier);
            
            let pageHTML = "<!DOCTYPE html><html>"
            pageHTML += '<head></head>';
            pageHTML += '<body>';
            pageHTML += '<h1>Mur d\'images</h1>';
            pageHTML += '<div id="mur-images">';
            for (let i = 0 ; i < fichiersImage.length ; i++) {
                const fichierSmallImage = fichiersImage[i].split('.')[0] + '_small.jpg';
                const img = '<img src="/public/'+fichierSmallImage+'" />';
                pageHTML += '<a href="/image/'+(i+1)+'" >' + img + '</a>';
            }
            pageHTML += '</div>';
            pageHTML += "</body></html>";
            res.end(pageHTML);
        } catch (e) {
            console.log(e);
            res.end(e);
        } 
    } else {
        const index = fs.readFileSync("./public/index.html", "utf-8");
        res.end(index);
    }
})

server.listen(port, host, () => {
    console.log(`Server running at http://${host}:${port}/`);
});

Code 8.5 : Serveur connecté à la base de données permettant d'afficher le mur d'images.

Le code du serveur que nous venons de présenter permet d'afficher le mur d'images après avoir lu les informations contenues dans la base de données (voir figure 8.1 ). Dans notre exemple la table des images contient neuf images. La page du mur d'images contient donc neuf balises <img> qui permettent l'affichage des neuf images.

Alt Text

Figure 8.1 : Le mur d'images avec une lecture des informations contenues dans la base de données.

Avec ce premier exemple, nous illustrons l'articulation entre la gestion des requêtes envoyées par les navigateurs, la génération des pages HTML et les interactions avec la base de données. On peut voir dans le code que nous avons fait le choix de connecter la base de données lors du démarrage du serveur. Ensuite, nous envoyons une requête à la base de données à chaque fois que le serveur reçoit une requête pour afficher le mur d'images. Ce choix sera discuté dans la suite de ce chapitre et nous verrons comment améliorer les performances du serveur.

Applications web et écriture des données

Pour illustrer la connexion entre un serveur web et une base de données en mode écriture nous considérons la table commentaires qui permet d'ajouter des commentaire à des images. Cette table contient trois colonnes : l'id du commentaire, le texte du commentaire et une référence vers l'id de l'image commentée. Le Tableau 8.2 illustre le contenu de cette table avec trois commentaires dont deux sur l'image 1.

ID Commentaire Image
1 Très beau 1
2 J'aime beaucoup 2
3 Belles couleurs 1

Tableau 8.2 : La table de commentaires utilisée comme exemple pour illustrer la connexion entre un serveur web et une base de données en mode écriture.

Le Code 8.6 présente le code du serveur permettant d'afficher le formulaire de saisie d'un commentaire et de l'ajouter dans la base de données. Nous nous intéressons aux lignes 25 à 66 qui gèrent l'affichage du formulaire (via la requête GET) ainsi que sa soumission (via la requête POST). Ligne 51 présente la création de la requête SQL permettant d'ajouter un commentaire dans la base de données. Cette requête est construite à partir des informations contenues dans le corps de la requête POST envoyée par le navigateur. On voit que cette requête est construite simplement par concaténation de chaînes de caractères (`INSERT INTO commentaires (texte, id_image) VALUES ('${texte}', ${id_image});`). Dès que cette requête est exécutée (ligne 52), le serveur renvoie une réponse au navigateur lui informant que le commentaire a été ajouté dans la base de données. Contrairement à l'exemple précédent nous n'utilisons pas la valeur du retour de la requête SQL. En effet, dans le cas d'une erreur, c'est le code compris dans le bloc catch qui sera exécuté.

const fs = require("fs");
const http = require("http");
const { Client } = require('pg');

const host = 'localhost';
const port = 8080;
const server = http.createServer();

const client = new Client({
    user: 'postgres', 
    password: 'root', 
    database: 'application_image',
    port : 5432 
});

client.connect()
.then(() => {
    console.log('Connected to database');
})
.catch((e) => {
    console.log('Error connecting to database');
    console.log(e);
}); 

server.on("request", async (req, res) => {
    if (req.url === '/commentaires') {
        if (req.method === 'GET') {
            let pageHTML = "<!DOCTYPE html><html>"
            pageHTML += `<head></head>`;
            pageHTML += "<body>";
            pageHTML += '<h1>Commentez une image</h1>';
            pageHTML += '<form method="POST" action="/commentaires">';
            pageHTML += '<label>texte</label>';
            pageHTML += '<input type="text" name="texte" />';
            pageHTML += '<label>id_image</label>';
            pageHTML += '<input type="text" name="id_image" />';
            pageHTML += '<input type="submit" value="Envoyer" />';
            pageHTML += '</form>';
            pageHTML += "</body></html>";
            res.end(pageHTML);
        } else {
            let data;
            req.on('data', (chunk) => {
                data += chunk.toString();
            });
            req.on('end', async () => {
                const texte = data.split('&')[0].split('=')[1];
                const id_image = data.split('&')[1].split('=')[1];
                try {
                    const sqlQuery = `INSERT INTO commentaires (texte, id_image) VALUES ('${texte}', ${id_image});`; 
                    await client.query(sqlQuery); 
                    
                    let pageHTML = "<!DOCTYPE html><html>"
                    pageHTML += '<head></head>';
                    pageHTML += '<body>';
                    pageHTML += '<h1>Commentaire</h1>';
                    pageHTML += '<div >';
                    pageHTML += '<p>Ok, le commentaire est dans la base </p>';
                    pageHTML += '</div>';
                    pageHTML += "</body></html>";
                    res.end(pageHTML);
                } catch (e) {
                    console.log(e);
                    res.end(e);
                }
            });
        }
    } else {
        const index = fs.readFileSync("./public/index.html", "utf-8");
        res.end(index);
    }
})

server.listen(port, host, () => {
    console.log(`Server running at http://${host}:${port}/`);
});

Code 8.6 : Serveur connecté à la base de données permettant d'ajouter un commentaire.

Le code du serveur que nous venons de présenter permet d'afficher le formulaire de saisie d'un commentaire et d'ajouter le commentaire dans la base de données après avoir lu les informations contenues dans le corps de la requête POST envoyée par le navigateur. On voit que le code du serveur est très similaire à celui présenté dans la section précédente. Il s'agit là aussi de construire la requête SQL et d'utiliser le driver (variable client) pour réaliser son exécution.

La Figure 8.2 présente le formulaire de saisie d'un commentaire dans lequel nous avons déjà précisé le texte du commentaire et le numéro de l'image ciblée. Un click sur le bouton de soumission permet l'envoi de la requête POST au serveur, et donc l'exécution de la requête SQL permettant d'ajouter le commentaire dans la base de données.

Alt Text

Figure 8.2 : Le formulaire permettant d'ajouter un commentaire sur une image.

Nous venons de présenter une deuxième articulation entre la gestion des requêtes envoyées par les navigateurs, la génération des pages HTML et les interactions avec la base de données. Cette articulation est très similaire à celle présentée dans la section précédente mais elle permet d'illustrer le fait que le serveur peut aussi être utilisé pour modifier le contenu de la base de données. On peut voir dans le code que nous avons fait là aussi le choix d'envoyer une requête à la base de données à chaque fois que le serveur reçoit une requête pour afficher le mur d'images. Ce choix est nécessaire car chaque requête envoyée par le navigateur doit modifier le contenu de la base de données.

Performance des applications web

Dans les sections précédentes nous avons présenté la façon dont les serveurs web se connectent à une base de données et comment ils envoient des requêtes en lecture et en écriture. Les codes que nous avons présentés font deux choix qui peuvent avoir un impact sur les performance des applications web.

Le premier choix porte sur la connexion à la base de données. Dans les codes que nous avons présentés, le serveur se connecte à la base de données dès qu'il est lancé. C'est même la première chose qu'il fait, avant même d'écouter les requêtes envoyées par les navigateurs. Ce choix a l'avantage de simplifier le code du serveur car la connexion à la base de données se fait en une seule fois et ne sera plus jamais remise en cause. L'inconvénient est que le serveur est continuellement connecté à la base de données, même lorsqu'il ne reçoit pas de requête. Un autre inconvénient est que le serveur ne peut pas se reconnecter à la base de données si la connexion est perdue (si on redémarre la base de données par exemple). Un autre choix possible est de se connecter à la base de données à chaque fois que le serveur reçoit une requête. Le Code 8.7 montre comment coder cette alternative. Dans cet exemple, on voit que la connexion à la base de données est réalisée dans le code de gestion de la requête GET permettant d'afficher le mur d'images. L'inconvénient de cette alternative est que la connexion à une base de données prend du temps et que le serveur doit donc attendre que la connexion soit établie avant de pouvoir répondre à la requête. Un autre inconvénient est que la base de données risque de recevoir plusieurs demandes de connexion en même temps si le serveur reçoit plusieurs requêtes en même temps, ce qui risque de perturber son fonctionnement (en principe, une base de données accepte un nombre limité de connexions en parallèle).


server.on("request", async (req, res) => {
    if (req.url === '/mur-images') {
        try {
            const client = new Client({
                user: 'postgres', 
                password: 'root', 
                database: 'application_image',
                port : 5432 
            });

            await client.connect();

            const sqlQuery = 'SELECT fichier from images'; 
            const sqlResult = await client.query(sqlQuery); 
            const fichiersImage = sqlResult.rows.map(row => row.fichier);
            
            let pageHTML = "<!DOCTYPE html><html>"
            pageHTML += `<head></head>`;
            pageHTML += "<body>";
            pageHTML += '<h1>Mur d'images</h1>';
            pageHTML += '<div id="mur-images">';
            for (let i = 0 ; i < fichiersImage.length ; i++) {
                const fichierSmallImage = fichiersImage[i].split('.')[0] + '_small.jpg';
                const img = '<img src="/public/'+fichierSmallImage+'" />';
                pageHTML += '<a href="/image/'+(i+1)+'" >' + img + '</a>';
            }
            pageHTML += '</div>';
            pageHTML += '<script src="/public/mur.js"></script>';
            pageHTML += "</body></html>";
            res.end(pageHTML);
        } catch (e) {
            console.log(e);
            res.end(e);
        } 
    } 
})

Code 8.7 : Serveur qui se connecte à la base de données à chaque requête envoyée par le navigateur.

Le deuxième choix que nous avons fait dans les codes présentés dans les sections précédentes porte sur la façon dont le serveur envoie les requêtes à la base de données. En effet, nous avons fait le choix d'envoyer une requête à la base de données à chaque fois que le serveur reçoit une requête du navigateur. Si cela est absolument nécessaire pour les requêtes d'écriture, cela n'est pas nécessaire pour les requêtes de lecture. En effet, si les requêtes envoyées par les navigateurs sont les mêmes, il est possible de ne faire qu'une seule requête en lecture à la base de données et de stocker le résultat dans une variable (une mémoire cache). Ainsi les requêtes suivantes n'auront pas besoin d'interroger la base de données et pourront directement utiliser le résultat stocké dans le cache. Coder un tel mécanisme est bien plus compliqué car il nécessite de mémoriser les requêtes déjà envoyées par les navigateurs et de mémoriser les résultats correspondants. Cela nécessite aussi de mettre en place un mécanisme de mise à jour du cache lorsque la base de données est modifiée.

Ce qu'il faut retenir

Ce chapitre présente la façon de connecter une base de données avec un serveur web. Il faut retenir les trois points suivants :

  • Chaque base de données propose son propre protocole de communication. Il faut alors utiliser un driver qui implémente ce protocole dans un langage de programmation pour pouvoir communiquer facilement avec la base de données. Dans notre contexte nous avons fait le choix d'utiliser le driver Javascript pg qui permet de communiquer avec une base de données PostgreSQL.
  • La connexion et la gestion des interactions à une base de données sont réalisés par le serveur. Le serveur commence par créer une connexion à la base de données. Ensuite, à chaque fois qu'il reçoit une requête du navigateur, il envoie une requête à la base de données et renvoie la réponse à la requête du navigateur.
  • Plusieurs choix sont possibles pour gérer la connexion à la base de données et les requêtes envoyées à la base de données. Ces choix peuvent avoir un impact sur les performances des applications web.

Pour s'exercer

Questions de cours

  1. Est-ce que la base de données doit s'exécuter sur le même ordinateur que le serveur web ?

    • a) Oui, sinon le serveur web ne peut pas accéder aux données.
    • b) Non, il faut que le serveur web puisse accéder aux fichiers de la base de données.
    • c) Non, la base de données propose un protocole pour que n'importe quel autre programme (dont le serveur web) puisse accéder à ses services.
  2. Est-ce que toutes les bases de données sont accessibles par HTTP ?

    • a) Non, chaque base de données propose son propre protocole de communication.
    • b) Non, il faut utiliser DBTP (Data Base Transfert Protocole).
    • c) Oui, il suffit d'envoyer des requêtes GET avec les requêtes SQL dans l'URL.
  3. Qu'est-ce qu'un driver de base de données ?

    • a) Un programme qui permet à la base de données de lire et d'écrire les fichiers sur le disque dur.
    • b) Un programme qui permet d'accéder à une base de données dans un langage de programmation particulier (par exemple, il y a un driver Javascript pour la base de données Postgres).
    • c) Un programme qui permet à la base de données d'envoyer des requêtes vers le serveur web.
  4. Avec le driver pg (driver postgres en Javascript), faut-il créer une connexion avec la base de données avant d'envoyer des requêtes ?

    • a) Non, le driver propose la fonction execQuery() qui permet d'exécuter directement les requêtes.
    • b) Oui, mais uniquement si on veut envoyer plusieurs requêtes.
    • c) Oui, la connexion retourne un objet (nommé Client) qui offre des méthodes pour exécuter les requêtes.
  5. Avec le driver pg (driver postgres en Javascript), comment sont présentées les résultats d'une requête ?

    • a) Ils sont présentés par un objet complexe qui contient une propriété nommée 'rows' qui contient les résultats.
    • b) Ils sont présentés par une chaîne de caractères encodée comme une URL avec des paramètres.
    • c) Ils sont encodés en binaire pour optimiser les communications entre la base de données et le client.

Réponses: 1-c, 2-a, 3-b, 4-c, 5-a

Exercice 1 - Compétitions sportives

Nous allons reprendre les concepts de compétitions sportives proposés dans l'exercice 1 du chapitre 7 (voir Chapitre 7 - exercice 1).

  1. Téléchargez competitions-sportives.sql et initialisez la base de données competitions_sportives (notez que ce script n'est pas une correction de l'exercice 1 du chapitre 6).

  2. Construisez un répertoire nommé chap8-ex1.

  3. Dans le répertoire chap8-ex1, exécutez la commande npm init pour initialiser la configuration Javascript. Vous répondrez oui à toutes les questions.

  4. Exécutez la commande npm i pg --save pour télécharger et installer le driver pg

  5. Toujours dans le répertoire chap8-ex1, ajoutez le fichier server.js qui contiendra votre serveur web.

  6. Codez le serveur web pour qu'il se connecte à la base de données. Vous ferez attention à bien mettre le nom de la base de données (competitions_sportives) dans les options de la connexion. Vous afficherez un message dans la console dès que la connexion entre le serveur web et la base de données sera opérationnelle.

  7. Dans le serveur web, proposez une route GET sur /joueurs qui retourne une page web affichant la liste de tous les joueurs stockés en base de données. Vous testerez votre serveur web en demandant l'URL : http://localhost:8080/joueurs.

  8. Modifiez votre code pour que l'URL GET /joueurs retourne la liste des joueurs avec leur catégorie.

  9. Proposez un formulaire pour ajouter un joueur dans la base et mettez en place la soumission du formulaire dans le serveur web. Vous ferez en sorte que le formulaire propose une combo box pour le choix de la catégorie du joueur (cette combo box devra être alimentée par la table catégories de la base de données). Vous testerez votre formulaire en ajoutant un joueur et en vérifiant qu'il est bien ajouté en demandant l'URL : http://localhost:8080/joueurs.

Projet - mur d'images

Nous allons reprendre notre projet qui permet d'afficher des images (voir Chapitre 7, projet).

L'objectif est de connecter notre serveur avec la base de données. Si les fichiers des images restent stockés sur le disque dur, les informations relatives aux images sont stockées dans la base de données.

Vous pouvez voir le résultat attendu ici.

Récupérez le code que vous avez réalisé dans le chapitre 7 et copiez-le dans un nouveau répertoire nommé chap8-projet.

  1. Exécutez le script application-image.sql pour initialiser les données de votre base. Vous devriez avoir une base de données nommée application_image.

  2. Initialisez votre projet avec npm (il faut exécuter les commandes npm init et npm i pg --save).

  3. Modifiez votre code pour faire en sorte que la page index.html soit générée dynamiquement et que les trois images affichées par cette page soient les plus récentes en prenant en compte la propriété date stockée en base de données dans la table images. Vous vérifierez qu'un clic sur une de ces trois images renvoie bien vers la page de l'image correspondante.

  4. Modifiez votre code pour faire en sorte que la page du mur d'images soit générée à partir des informations contenues en base de données.

  5. Modifiez votre code pour faire en sorte que la génération des pages des images utilise les informations contenues en base de données. Vous veillerez à intégrer le titre de l'image dans la page et à afficher les commentaires stockés en base. La soumission d'un nouveau commentaire doit aussi mettre à jour la base.