NodeJS & MongoDB

NodeJS & MongoDB

Présentation

NodeJS & MongoDB

Présentation

NodeJS est une librairie standard de Javascript qui permet d'écrire des applications sur notre machine, notamment de serveurs

Vous pouvez retrouver la documentation officiel ici

NodeJS & MongoDB

Présentation

MongoDB est une base de données (noSQL) qui enregistre des documents JSON mais ne permet pas de relation !

Vous pouvez retrouver la documentation officiel ici

NodeJS & MongoDB

Les premiers pas

NodeJS & MongoDB

Premiers pas

Le point d'entrée d'une application Javascript (nodejs, DOM, etc ...) c'est le fichier package.json

Ce fichier contient les informations de notre programme, ainsi que ces dépendances. Il est généré grâce à NPM avec la commande

npm init -y

NodeJS & MongoDB

{
  "name": "tuto",
  "version": "1.0.0",
  "description": "Nodejs tutorielle",
  "main": "src/index.js",
  "type": "module",
  "scripts": {
    "start": "nodemon src/index.js",
    "test": "jest --watch --setupFiles dotenv/config",
    "preinstall": "node bin/copy-past-env.js"
  },
  "dependencies": {
    "cross-env": "^7.0.3",
    ...
  }
}

Voici à quoi ressemble un package.json

NodeJS & MongoDB

{
  "name": "tuto",
  "version": "1.0.0",
  "description": "Nodejs tutorielle",
  "main": "src/index.js",
  "type": "module",
  "scripts": {
    "start": "nodemon src/index.js",
    "test": "jest --watch --setupFiles dotenv/config",
    "preinstall": "node bin/copy-past-env.js"
  },
  "dependencies": {
    "cross-env": "^7.0.3",
    ...
  }
}

Voici les informations de votre projet

(type: module correspond à la nouvelle syntax es6 pour les imports et exports)

NodeJS & MongoDB

{
  "name": "tuto",
  "version": "1.0.0",
  "description": "Nodejs tutorielle",
  "main": "src/index.js",
  "type": "module",
  "scripts": {
    "start": "nodemon src/index.js",
    "test": "jest --watch --setupFiles dotenv/config",
    "preinstall": "node bin/copy-past-env.js"
  },
  "dependencies": {
    "cross-env": "^7.0.3",
    ...
  }
}

Tout les scripts de démarrage et de test de notre application.

Pour lancer un scripts il suffit de faire

npm run <nomDuScript>

NodeJS & MongoDB

{
  "name": "tuto",
  "version": "1.0.0",
  "description": "Nodejs tutorielle",
  "main": "src/index.js",
  "type": "module",
  "scripts": {
    "start": "nodemon src/index.js",
    "test": "jest --watch --setupFiles dotenv/config",
    "preinstall": "node bin/copy-past-env.js"
  },
  "dependencies": {
    "cross-env": "^7.0.3",
    ...
  }
}

Ces lignes correspondent au dépendances, ce sont des librairies (package) installé depuis "internet" afin que notre projet fonctionne corréctement

NodeJS & MongoDB

# Instale un paquet, et rajoute une ligne dans "dependencies"
# de notre package.json
$ npm install fastify

# Install tout les paquets spécfifié dans le package.json
$ npm install

# Supprime une dépendance
$ npm remove fastify

# Lance le script "start"
$ npm start

# Lance le script "test"
$ npm test

Il est possible à tout moment d'utiliser NPM pour différentes actions :

NodeJS & MongoDB

Fastify

NodeJS & MongoDB

Fastify c'est une librairie (package) qui permet de construire des API web facilement

 

Les avantages de fastify sont les suivants :

- rapide

- léger

- il comprend les json schémas

- extensible

vous retrouverez la documentation de fastify en ligne !

NodeJS & MongoDB

Pour installer fastify, rien de plus simple

# Installation de fastify
$ npm install fastify

NodeJS & MongoDB

Utiliser fastify

// Importation du package "fastify"
import fastify from 'fastify'

// Création d'une application. L'option
// logger permet d'afficher dans la console
// les logs du serveur
const app = fastify({ logger: true })

// On démarre l'application sur un port
// et un Host donnée :
app.listen(4545, 'localhost')

NodeJS & MongoDB

Attacher des routes à fastify

// Déclaration d'une GET /hello
app.get('/hello', { options }, async () => {
  return 'Hello World'
})

// Déclaration d'une POST /hello
app.post('/hello', { options }, async () => {
  return 'Hello World'
})

// Déclaration d'une PATCH /hello
app.patch('/hello', { options }, async () => {
  return 'Hello World'
})

// Déclaration d'une PUT /hello
app.put('/hello', { options }, async () => {
  return 'Hello World'
})

// Déclaration d'une DELETE /hello
app.delete('/hello', { options }, async () => {
  return 'Hello World'
})

NodeJS & MongoDB

Attacher des routes avec paramètres

// Déclaration d'une GET /hello
app.get('/hello/:name', { options }, async (request) => {
  // Accéde au paramètre de la route nommé "name"
  return `Hello ${request.params.name}`
})

Vous pouvez en apprendre plus sur les paramètres de routes juste ici

NodeJS & MongoDB

Request et Reply

NodeJS & MongoDB

En fastify il est possible de récupérer les informations de la requête grâce à l'objet request. Il est passé en premier paramètre à toutes nos routes

app.post('/categories', async (request) => {
  // Nous pouvons accéder au body de la request
  const titre = request.body.titre
  
  // On peut accéder aux headers de la request
  const contentType = request.headers.contentType
  
  // Les query strings
  const page = request.query.page
})

NodeJS & MongoDB

En fastify il est possible de récupérer des paramètres de notre route

app.patch('/categories/:id', async (request) => {
  // Nous pouvons accéder au paramètre de la route
  const id = request.params.id
})

NodeJS & MongoDB

En fastify il est possible de définir des schemas (json schemas)

Ces schemas nous permettent de de définir la forme d'une Requête et d'une Réponse HTTP

NodeJS & MongoDB

Schematiser le "body" d'une requête

app.post('/categories', {
  schema: {
    body: {
      type: 'object',
      required: [ 'titre' ],
      properties: {
        titre: {
          type: 'string'
        }
      }
    }
  }
}, async () => {
  
})

NodeJS & MongoDB

Schematiser les query string

app.get('/categories', {
  schema: {
    querystring: {
      type: 'object',
      properties: {
        page: {
          type: 'number'
        },
        limit: {
          type: 'number'
        }
      }
    }
  }
}, async () => {
  
})

NodeJS & MongoDB

Schematiser les réponses

app.get('/categories', {
  schema: {
    response: {
      200: {
        type: 'array',
        items: {
          type: 'object',
          required: [ '_id', 'titre' ],
          properties: {
            _id: { type: 'string' },
            titre: { type: 'string'},
          },
        },
      }
    }
  }
}, async () => {
  
})

NodeJS & MongoDB

MongoDB

NodeJS & MongoDB

MongoDB est une base de données non relationel

Elle n'utilise pas SQL mais plutôt se base sur le format JSON

L'api de mongo est utilisable nativement en javascript

NodeJS & MongoDB

Les avantages d'une tel de base données sont les suivants:

- Rapide en écriture et lécture

- Simple à prendre en main

- N'échoue jamais (ne sort quasiment jamais d'erreur)

- Natif en javascript

NodeJS & MongoDB

Les désavantages d'une tel de base données sont les suivants:

- Non consistant (nous ne pouvons pas nous assurer de la forme de nos données)

- Non relationnelle (il est impossible d'attacher un document à un autre document ou alors très lent)

- Toute la logique est applicative (aucune logique dans la base de données)

NodeJS & MongoDB

Pour utiliser mongodb il est recommandé d'installer le driver nodejs

# Installation du driver mongodb pour nodejs
$ npm install mongodb

NodeJS & MongoDB

Pour utiliser mongo il nous faut nous connecter à la base pour cela nous utilisons une URL de connexion

import mongo from 'mongodb'

async function start() {
  // Connéction à la base de données,
  // de recevons un client
  const client = await mongo.MongoClient.connect(
    'mongodb://<username>:<password>@<host>:<port>'
  )
  
  // Nous pouvons facilement récupérer une base de données
  const db = client.db('nomDeLaBDD')
}

NodeJS & MongoDB

Afin d'utiliser la base de données dans nos routes fastify, nous pouvons "décorer" notre application :

import fastify from 'fastify'
import mongo from 'mongodb'

async function start() {
  // Création d'une application fastify
  const app = fastify({ logger: true })
  
  // Connéction à la base de données,
  // de recevons un client
  const client = await mongo.MongoClient.connect(
    'mongodb://<username>:<password>@<host>:<port>'
  )
  
  // Nous pouvons facilement récupérer une base de données
  const db = client.db('nomDeLaBDD')
  
  // Nous décorons l'application en ajoutant la clef
  // "db"
  app.decorate('db', db)
  
  // Pour accéder à la base de données il suffit de faire:
  // app.db
}

NodeJS & MongoDB

En mongo il est possible d'éfféctuer une recherche, vous pouvez utiliser toute une série d'opérateur

// Récupération de tout les documents d'une collection
const documents = await db
  .collection('nomDeLaCollection')
  .find()
  .toArray()

// Nous pouvons filtrer ces documents
const doc2 = await db
  .collection('books')
  .find({ titre: 'Harry Potter' })
  .toArray()

const dc3 = await db
  .collection('books')
  .find({ titre: { $regex: /^Harry/ } })
  .toArray()

NodeJS & MongoDB

Il est possible de récupérer un seul document

import mongo from 'mongodb'

// Nous récupérons un document par son ID
const doc = await db.collection('books').findOne({
  // Ne pas oublié d'utiliser mongo.ObjectId
  // dans nos recherche, sinon l'id ne peut correspondre
  _id: mongo.ObjectId('KKDHFKS728238')
})

// Nous récupérons un document par l'un de ces champs
const john = await db.collection('users').findOne({
  email: "john.doe@mail.com"
})

NodeJS & MongoDB

Il est possible d'insérer une ou plusieurs données

const result = await db.collection('categories').insertOne({
  titre: 'Ma catégorie'
})

// Ici result est un objet qui contient de statistique
// sur l'insertion de notre / nos documents :
result.insertedId // Retourne l'id du document tout just inséré
result.insertedCount // Retourne le nombre de document inséré

// Il est possible d'insérer plusieurs résultat à la fois
const result = await db.collection('categories').insertMany([
  { titre: 'Nature' },
  { titre: 'Science' },
])

// Nous pouvons récupérer tous les ids insérés
// dans la base de données sous forme de tableaux
// de string
result.insertedIds

NodeJS & MongoDB

Il est possible de mettre à jour des documents

import mongo from 'mongodb'

const result = await db.collection('categories').updateOne(
  // Le premier paramètre correspond aux filtres
  // de l'élément que nous voulons mettre ç jour
  // Ici l'élément avec l'id données
  { _id: mongo.ObjectId('DJFKDH275623') },
  // Le deuxième paramètre correspond au champs que nous
  // mettons à jour
  { $set: { titre: 'Mise à jour du titre', description: 'coucou' } },
)

NodeJS & MongoDB

Il est possible de supprimer des documents

import mongo from 'mongodb'

const result = await db.collection('categories').deleteOne(
  // Le premier paramètre correspond aux filtres
  // de l'élément que nous voulons mettre ç jour
  // Ici l'élément avec l'id données
  { _id: mongo.ObjectId('DJFKDH275623') },
)

NodeJS & MongoDB

Il est possible de personnalisé la réponse HTTP grâce à l'objet Reply. Il est envoyé en second paramètre de toutes nos routes

app.post('/categories', async (request, reply) => {
  // Nous pouvons changer le status code
  reply.code(201)
  // Nous pouvons ajouter des en-tête HTTP
  reply.header('Powered-By', 'fastify')
})

NodeJS & MongoDB

Les plugins

NodeJS & MongoDB

Les plugins sont un système permettant de séparer notre code en plusieurs modules (fichier js)

// src/routes/categories.js

export default async function categoriesPlugin(app) {
  app.get('/categories', async () => {
    ...
  })
}

NodeJS & MongoDB

On exporte un plugin fastify :

Le premier paramètre correspond à l'application fastify

// src/routes/categories.js

export default async function categoryPlugin(app, opts) {
  app.get('/categories', async () => {
    ...
  })
}

NodeJS & MongoDB

On exporte un plugin fastify :

Le second paramètre correspond aux options du plugins

// src/routes/categories.js

export default async categoryPlugin(app, opts) {
  app.get('/categories', async () => {
    ...
  })
}

NodeJS & MongoDB

Ici nous pouvons utiliser fastify et déclarer des routes, schemas, decorators ...

Attention ! Done doit être appelé à la fin du plugin !

// src/routes/categories.js

export default async function categoryPlugin(app, opts) {
  app.get('/categories', async () => {
    ...
  })
}

NodeJS & MongoDB

Une fois notre plugin écrit, il est possible dans l'enregistrer :

// src/index.js
import fastify from 'fastify'
import monPlugin from './routes/categories.js'

async function start() {
  const app = fastify({ logger: true })
  
  // Nous enregistrons notre plugin
  app.register(monPlugin)
}

NodeJS & MongoDB

Nous enregistrons un plugin avec les options "opt1, opt2, opt3"

// src/index.js
import fastify from 'fastify'
import monPlugin from './routes/categories.js'

async function start() {
  const app = fastify({ logger: true })
  
  // Nous enregistrons notre plugin
  app.register(monPlugin, {
    opt1: true,
    opt2: 'foo',
    opt3: 'bar',
  })
}

NodeJS & MongoDB

Les Schemas

NodeJS & MongoDB

En fastify il est possible de déclarer des schemas réutilisable

Pour cela, il faut installer fastify-plugin afin de pouvoir déclarer des schémas globaux à toute notre application

NodeJS & MongoDB

Installation de fastify plugin

$ npm install fastify-plugin

NodeJS & MongoDB

Nous pouvons écrire un plugin fastify pour nos schémas :

// src/schemas/categories.js

export default (app, opts, done) => {
  app.addSchema({
    $id: 'category',
    type: 'object',
    required: [ 'titre' ],
    properties: {
      titre: { type: 'string' },
    },
  })

  done()
}

NodeJS & MongoDB

Une fois écrit, notre plugin doit être enregistré :

// src/index.js
import fastify from 'fastify'
import fp from 'fastify-plugin'
import categorySchema from './schemas/categories.js'

async function start() {
  const app = fastify({ logger: true })
  
  app.register(fp(categorySchema))
}

NodeJS & MongoDB

Il nous faut pas oublier de "wrapper" notre plugin dans "fp", sinon nos schémas ne seront pas accessible depuis l’extérieur du plugin

// src/index.js
import fastify from 'fastify'
import fp from 'fastify-plugin'
import categorySchema from './schemas/categories.js'

async function start() {
  const app = fastify({ logger: true })
  
  app.register(fp(categorySchema))
}

NodeJS & MongoDB

Pour réutiliser un schema, il suffit de renseigner la clefs  "$ref"

// src/routes/categories

export default (app, opts, done) => {
  app.post(
    '/categories',
    { schema: { body: { $ref: 'category' } } },
    async () => {
      ..
    }
  )
  
  done()
}

NodeJS & MongoDB

On renseigne le schéma avec l'id "category" sur notre body

// src/routes/categories

export default (app, opts, done) => {
  app.post(
    '/categories',
    { schema: { body: { $ref: 'category' } } },
    async () => {
      ..
    }
  )
  
  done()
}

NodeJS & MongoDB

NodeJS & MongoDB

Nodemon est un petit outil javascript (package) qui nous permet de redémarrer notre serveur à chaque modification de fichier !

NodeJS & MongoDB

Installation

$ npm install nodemon

NodeJS & MongoDB

Mise en place

// package.json

{
  ...
  "scripts": {
    "start": "nodemon src/index.js",
    ...
  },
  ...
}

NodeJS & MongoDB

Nous spécifions "nodemon" suivie de notre fichier principal d'application

// package.json

{
  ...
  "scripts": {
    "start": "nodemon src/index.js",
    ...
  },
  ...
}

NodeJS & MongoDB

La Configuration

NodeJS & MongoDB

La configuration nous permet de définir des données variable en fonction des machines !

En fonction de notre poste d'installation ou du serveur et bien certaines données doivent changé (ex: l'url de connexion à la base de données)

NodeJS & MongoDB

La configuration s’effectue via des variables d’environnement afin de respecter les « Twelve Factors App »

NodeJS & MongoDB

Ces variables d’environnement sont déclaré dans un fichier nommé '.env' à la racine de notre projet

Attention !

- Ce fichier ne doit jamais être versionné

- On peut cependant versionné un fichier standard `.env.dist`

NodeJS & MongoDB

Afin d'utiliser ce fichier `.env` en nodejs il nous installer le package dotenv

$ npm install dotenv

NodeJS & MongoDB

Une fois le paquet installé, il faut l'importer et lancé la fonction "config"

// src/index.js

import dotenv from 'dotenv'

dotenv.config()

// Nous pouvons maintenant accéder aux
// variables déclarer dans le fichier `.env`
// grâce à
process.env.MA_VARIABLE

NodeJS & MongoDB

Authentification Via JWT

NodeJS & MongoDB

L'auth. JWT fonctionne grâce à un sytstème de token (le fameux JSON Web Token)

NodeJS & MongoDB

Afin de mettre en place une authentification JWT il nous un plugin fastify : fastify-jwt

$ npm install fastify-jwt

NodeJS & MongoDB

Afin d'utiliser le plugin il nous l'enregistrer dans notre application :

// src/index.js
import fastify from 'fastify'
import fastifyJwt from 'fastify-jwt'

async function start() {
  const app = fastify({ logger: true })
  
  app.register(fastifyJwt, {
    secret: 'clef utilisé pour crypter notre token'
  })
}

NodeJS & MongoDB

On enregistre le plugin fastify-jwt en lui spécifiant le "secret" utilisé pour crtypter notre token

// src/index.js
import fastify from 'fastify'
import fastifyJwt from 'fastify-jwt'

async function start() {
  const app = fastify({ logger: true })
  
  app.register(fastifyJwt, {
    secret: 'clef utilisé pour crypter notre token'
  })
}

NodeJS & MongoDB

Grâce à ce plugin nous pouvons générer un JWT

// src/routes/users.js

export default (app, opts, done) => {
  // Création de la route d'authentification
  app.post(
    '/authenticate',
    { schema: { body: { $ref: 'credential' } } },
    async (request, reply) => {
      // On récupére et on valide l'utilisateur
      const user = // Récupération + Validation

      reply.code(201)

      return { token: app.jwt.sign(user) }
    }
  )
  
  done()
}

NodeJS & MongoDB

Ici nous cryptons l'objet user dans un token JWT

// src/routes/users.js

export default (app, opts, done) => {
  // Création de la route d'authentification
  app.post(
    '/authenticate',
    { schema: { body: { $ref: 'credential' } } },
    async (request, reply) => {
      // On récupére et on valide l'utilisateur
      const user = // Récupération + Validation

      reply.code(201)

      return { token: app.jwt.sign(user) }
    }
  )
  
  done()
}

NodeJS & MongoDB

Pour authentifier un utilisateur, rien de plus simple :

// src/routes/users.js

export default (app, opts, done) => {
  // Création de la route d'authentification
  app.get('/categories', async (request, reply) => {
    // Calide le token envoyé de le header Authorization
    await request.jwtVerify()   
  })
  
  done()
}

NodeJS & MongoDB

Vérifie que le token est présent ainsi que valide dans le header "Authorization". Si ce n'est pas le cas, le code est intérompus et une erreur http 403 est envoyé !

// src/routes/users.js

export default (app, opts, done) => {
  // Création de la route d'authentification
  app.get('/categories', async (request, reply) => {
    // Calide le token envoyé de le header Authorization
    await request.jwtVerify()   
  })
  
  done()
}

NodeJS & MongoDB

CORS

NodeJS & MongoDB

Les cors est une sécurité mise en place par les navigateurs et application mobiles afin de restreindre les requêtes au domaine en cours 

 

Par éxemple depuis le site "nodejs.org" il est impossible de faire une requête sur le site "google.fr" sans mettre en place des en-têtes HTTP CORS

NodeJS & MongoDB

Il est extrémement simple de rajouter les en-têtes HTTP CORS en fastify

Il suffit d'installer et d'utiliser le plugin fastify-cors

$ npm install fastify-cors

NodeJS & MongoDB

Nous enregistrons le plugin CORS

// src/index.js
import fastify from 'fastify'
import fastifyCors from 'fastify-cors'

async function start() {
  const app = fastify({ logger: true })
  
  app.register(fastifyCors, {
    origin: true
  })
}

NodeJS & MongoDB

Le plugin CORS acceptent des options de configuration

// src/index.js
import fastify from 'fastify'
import fastifyCors from 'fastify-cors'

async function start() {
  const app = fastify({ logger: true })
  
  app.register(fastifyCors, {
    origin: true
  })
}

NodeJS & MongoDB

By David Jegat

NodeJS & MongoDB

  • 455