RESSOURCES
DÉVELOPPER VOTRE PREMIÈRE API REST AVEC NODE.JS.
👉 Les ressources de la formation « Node : Développez votre première API Rest avec Express et MySQL» se trouvent bien sur cette page. Vous trouverez, comme promis :
L’application de démonstration
Lien vers l’application de démonstration.
La correction du code
Lien vers la correction complète du code.
La favicon à télécharger
Les extraits de code
app.js
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Hello, Express!'))
app.listen(port, () => console.log(`Notre application Node est démarrée sur : http://localhost:${port}`))
mock-pokemon.js
const pokemons = [
{
id: 1,
name: "Bulbizarre",
hp: 25,
cp: 5,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/001.png",
types: ["Plante", "Poison"],
created: new Date()
},
{
id: 2,
name: "Salamèche",
hp: 28,
cp: 6,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/004.png",
types: ["Feu"],
created: new Date()
},
{
id: 3,
name: "Carapuce",
hp: 21,
cp: 4,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/007.png",
types: ["Eau"],
created: new Date()
},
{
id: 4,
name: "Aspicot",
hp: 16,
cp: 2,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/013.png",
types: ["Insecte", "Poison"],
created: new Date()
},
{
id: 5,
name: "Roucool",
hp: 30,
cp: 7,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/016.png",
types: ["Normal", "Vol"],
created: new Date()
},
{
id: 6,
name: "Rattata",
hp: 18,
cp: 6,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/019.png",
types: ["Normal"],
created: new Date()
},
{
id: 7,
name: "Piafabec",
hp: 14,
cp: 5,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/021.png",
types: ["Normal", "Vol"],
created: new Date()
},
{
id: 8,
name: "Abo",
hp: 16,
cp: 4,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/023.png",
types: ["Poison"],
created: new Date()
},
{
id: 9,
name: "Pikachu",
hp: 21,
cp: 7,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/025.png",
types: ["Electrik"],
created: new Date()
},
{
id: 10,
name: "Sabelette",
hp: 19,
cp: 3,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/027.png",
types: ["Normal"],
created: new Date()
},
{
id: 11,
name: "Mélofée",
hp: 25,
cp: 5,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/035.png",
types: ["Fée"],
created: new Date()
},
{
id: 12,
name: "Groupix",
hp: 17,
cp: 8,
picture: "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/037.png",
types: ["Feu"],
created: new Date()
}
];
module.exports = pokemons
helper.js
const success = (message, data) => {
return {
message: message,
data: data
}
}
exports.success
Les réponses JSON (Correction) : Retourner une liste de données au format JSON
const express = require('express')
const { success } = require('./helper.js')
let pokemons = require('./mock-pokemon')
const app = express()
const port = 3000
app.get('/', (req,res) => res.send('Hello again, Express !'))
// On retourne la liste des pokémons au format JSON, avec un message :
app.get('/api/pokemons', (req, res) => {
const message = 'La liste des pokémons a bien été récupérée.'
res.json(success(message, pokemons))
})
app.get('/api/pokemons/:id', (req, res) => {
const id = parseInt(req.params.id)
const pokemon = pokemons.find(pokemon => pokemon.id === id)
const message = 'Un pokémon a bien été trouvé.'
res.json(success(message, pokemon))
})
app.listen(port, () => console.log(`Notre application Node est démarrée sur : http://localhost:${port}`))
Une API Rest Complète : Ajouter un nouveau Pokémon
app.js
//...
app.post('/api/pokemons', (req, res) => {
const id = 123
const pokemonCreated = { ...req.body, ...{id: id, created: new Date()}}
pokemons.push(pokemonCreated)
const message = `Le pokémon ${pokemonCreated.name} a bien été crée.`
res.json(success(message, pokemonCreated))
})
// ...
helper.js
exports.success = (message, data) => {
return { message, data }
}
exports.getUniqueId = (pokemons) => {
const pokemonsIds = pokemons.map(pokemon => pokemon.id)
const maxId = pokemonsIds.reduce((a,b) => Math.max(a, b))
const uniqueId = maxId + 1
return uniqueId
}
Une API Rest Complète : Effectuer une requête POST avec Insomnia
Le corps de la requête POST à copier-coller dans Insomnia pour ajouter le pokémon « Chenipan » sur votre API Rest.
{
"name": "Chenipan",
"hp": 29,
"cp":4,
"picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/detail/010.png",
"types": ["Insecte", "Poison"]
}
Une API Rest Complète : Modifier un Pokémon
app.js
// ...
app.put('/api/pokemons/:id', (req, res) => {
const id = parseInt(req.params.id);
const pokemonUpdated = { ...req.body, id: id }
pokemons = pokemons.map(pokemon => {
return pokemon.id === id ? pokemonUpdated : pokemon
})
const message = `Le pokémon ${pokemonUpdated.name} a bien été modifié.`
res.json(success(message, pokemonUpdated))
});
// ...
Une API Rest Complète : Supprimer un Pokémon
app.js
// ...
app.delete('/api/pokemons/:id', (req, res) => {
const id = parseInt(req.params.id)
const pokemonDeleted = pokemons.find(pokemon => pokemon.id === id)
pokemons = pokemons.filter(pokemon => pokemon.id !== id)
const message = `Le pokémon ${pokemonDeleted.name} a bien été supprimé.`
res.json(success(message, pokemonDeleted))
});
// ...
L’API Rest et la Base de données : Créer un modèle Sequelize
src/models/pokemon.js
module.exports = (sequelize, DataTypes) => {
return sequelize.define('Pokemon', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
hp: {
type: DataTypes.INTEGER,
allowNull: false
},
cp: {
type: DataTypes.INTEGER,
allowNull: false
},
picture: {
type: DataTypes.STRING,
allowNull: false
},
types: {
type: DataTypes.STRING,
allowNull: false
}
}, {
timestamps: true,
createdAt: 'created',
updatedAt: false
})
}
L’API Rest et la Base de données : Créer un modèle Sequelize
sequelize.js
const { Sequelize, DataTypes } = require('sequelize')
const PokemonModel = require('../models/pokemon')
const pokemons = require('./mock-pokemon')
const sequelize = new Sequelize('pokedex', 'root', '', {
host: 'localhost',
dialect: 'mariadb',
dialectOptions: {
timezone: 'Etc/GMT-2',
},
logging: false
})
const Pokemon = PokemonModel(sequelize, DataTypes)
const initDb = () => {
return sequelize.sync({force: true}).then(_ => {
pokemons.map(pokemon => {
Pokemon.create({
name: pokemon.name,
hp: pokemon.hp,
cp: pokemon.cp,
picture: pokemon.picture,
types: pokemon.types.join()
}).then(pokemon => console.log(pokemon.toJSON()))
})
console.log('La base de donnée a bien été initialisée !')
})
}
module.exports = {
initDb, Pokemon
}
findAllPokemons.js
const { Pokemon } = require('../db/sequelize')
module.exports = (app) => {
app.get('/api/pokemons', (req, res) => {
Pokemon.findAll()
.then(pokemons => {
const message = 'La liste des pokémons a bien été récupérée.'
res.json({ message, data: pokemons })
})
})
}
findPokemonByPk.js
const { Pokemon } = require('../db/sequelize')
module.exports = (app) => {
app.get('/api/pokemons/:id', (req, res) => {
Pokemon.findByPk(req.params.id)
.then(pokemon => {
const message = 'Un pokémon a bien été trouvé.'
res.json({ message, data: pokemon })
})
})
}
createPokemon.js
const { Pokemon } = require('../db/sequelize')
module.exports = (app) => {
app.post('/api/pokemons', (req, res) => {
Pokemon.create(req.body)
.then(pokemon => {
const message = `Le pokémon ${req.body.name} a bien été crée.`
res.json({ message, data: pokemon })
})
})
}
updatePokemon.js
const { Pokemon } = require('../db/sequelize')
module.exports = (app) => {
app.put('/api/pokemons/:id', (req, res) => {
const id = req.params.id
Pokemon.update(req.body, {
where: { id: id }
})
.then(_ => {
Pokemon.findByPk(id).then(pokemon => {
const message = `Le pokémon ${pokemon.name} a bien été modifié.`
res.json({message, data: pokemon })
})
})
})
}
deletePokemon.js
const { Pokemon } = require('../db/sequelize')
module.exports = (app) => {
app.delete('/api/pokemons/:id', (req, res) => {
Pokemon.findByPk(req.params.id).then(pokemon => {
const pokemonDeleted = pokemon;
Pokemon.destroy({
where: { id: pokemon.id }
})
.then(_ => {
const message = `Le pokémon avec l'identifiant n°${pokemonDeleted.id} a bien été supprimé.`
res.json({message, data: pokemonDeleted })
})
})
})
}
Authentification : Créer un modèle User avec Sequelize
models/user.js
module.exports = (sequelize, DataTypes) => {
return sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING
},
password: {
type: DataTypes.STRING
}
})
}
routes/login.js
const { User } = require('../db/sequelize')
const bcrypt = require('bcrypt')
module.exports = (app) => {
app.post('/api/login', (req, res) => {
User.findOne({ where: { username: req.body.username } }).then(user => {
bcrypt.compare(req.body.password, user.password).then(isPasswordValid => {
if(isPasswordValid) {
const message = `L'utilisateur a été connecté avec succès`;
return res.json({ message, data: user })
}
})
})
})
}
auth/auth.js
const jwt = require('jsonwebtoken')
const privateKey = require('../auth/private_key')
module.exports = (req, res, next) => {
const authorizationHeader = req.headers.authorization
if(!authorizationHeader) {
const message = `Vous n'avez pas fourni de jeton d'authentification. Ajoutez-en un dans l'en-tête de la requête.`
return res.status(401).json({ message })
}
const token = authorizationHeader.split(' ')[1]
const decodedToken = jwt.verify(token, privateKey, (error, decodedToken) => {
if(error) {
const message = `L'utilisateur n'est pas autorisé à accèder à cette ressource.`
return res.status(401).json({ message, data: error })
}
const userId = decodedToken.userId
if (req.body.userId && req.body.userId !== userId) {
const message = `L'identifiant de l'utilisateur est invalide.`
res.status(401).json({ message })
} else {
next()
}
})
}