Catégories
Astuces et Design

Comment créer votre première API REST avec Fastify

Fastify est un framework conçu pour le développement Web back-end. Il offre une alternative plus légère aux frameworks d'API Node.js plus lourds, tels que Hapi et Express. Depuis juillet 2020, Fastify a publié sa troisième version du framework.

Cette troisième version est dotée de capacités de validation améliorées pour vérifier les demandes entrantes et sortantes, en tant que paramètres de demande. De plus, la troisième version du framework consolide ses revendications de débit comme étant le framework Node.js le plus rapide par rapport à Koa, Resitfy, Hapi et Express. Plus d'informations peuvent être trouvées sur la page des benchmarks.

Fastify a acquis une grande popularité en raison de sa conception légère. Cependant, une grande attention est portée à son écosystème de plugins. Fastify a adopté l'idée que tout est un plugin, alors qu'avec JavaScript, tout est un objet. Cela vous permet d'encapsuler rapidement les fonctionnalités de votre projet en tant que plugin et de les distribuer afin que d'autres projets puissent utiliser votre code.

Commençons par ce didacticiel. Vous apprendrez les aspects suivants de Fastify:

  • Comment configurer votre première API Fastify
  • Comment définir les routes d'API Fastify
  • Comment ajouter une validation de schéma aux demandes
  • Comment charger et utiliser les plugins Fastify
  • Comment définir les hooks Fastify

Exigences et installation

Pour suivre ce didacticiel, vous aurez besoin des éléments suivants:

  1. la dernière version de Node.js
  2. un outil d'envoi de requêtes, tel que cURL ou Postman

Ensuite, assurez-vous de créer un projet Node.js vide. Si vous n'en avez pas encore, vous pouvez utiliser la commande suivante pour configurer votre projet:

npm init -y

Enfin, nous souhaitons ajouter cette dépendance Fastify à notre projet:

npm i fastify --save

Tout bon? Créons notre configuration API de base à l'étape suivante.

Étape 1: Configuration de l'API de base

Commençons par créer notre configuration API de base. Pour commencer, nous devons créer un nouveau fichier appelé index.js dans la racine de notre projet:

touch index.js

Ensuite, ajoutons la configuration de base du serveur. Copiez le code ci-dessous:


const app = require('fastify')({
    logger: true
})


app.get('/', function (req, reply) {
    reply.send({ hello: 'world' })
})


app.listen(3000, (err, address) => {
    if (err) {
        app.log.error(err)
        process.exit(1)
    }
    app.log.info(`server listening on ${address}`)
})

Il se passe plusieurs choses ici. Nous chargeons d'abord l'objet d'application Fastify et activons la journalisation. Ensuite, nous déclarons une route racine qui répond avec une réponse JSON. La dernière partie de l'extrait de code montre que nous écoutons sur le port 3000 pour que l'application reçoive des demandes.

Vérifions si la configuration de base de votre serveur fonctionne. Tout d'abord, nous devons démarrer le serveur en exécutant le index.js fichier:

node index.js

Ensuite, accédez à http://localhost:3000 dans votre navigateur. Vous devriez voir la réponse suivante:

{
    "hello": "world"
}

Succès? Passons à l'étape 2 pour définir différents itinéraires CRUD.

Étape 2: définir les routes CRUD

Une API est inutile avec uniquement des routes GET. Définissons d'autres itinéraires pour gérer les blogs. Par conséquent, créons les itinéraires suivants:

  • OBTENEZ tous les blogs sur / api / blogs
  • OBTENEZ un blog sur / api / blogs /: id
  • POST ajouter un blog sur / api / blogs
  • Blog de mise à jour PUT à / api / blogs /: id
  • SUPPRIMER supprimer le blog dans / api / blogs /: id

La première chose à faire est de créer un contrôleur de blog.

Étape 2.1: Créer un contrôleur de blogs

Pour garder notre code propre, définissons un controller dossier à la racine du projet. Ici, nous créons un fichier appelé blogs.js.

Ce fichier contient des données de démonstration pour éviter de compliquer ce tutoriel avec une intégration de base de données. Par conséquent, nous utilisons un tableau contenant des objets de blog qui contiennent chacun un champ ID et titre.

De plus, nous définissons les différents gestionnaires pour toutes les routes ci-dessus dans ce fichier. Un gestionnaire accepte toujours un req (demande) et reply paramètre. Le paramètre de demande est utile pour accéder aux paramètres de demande ou demander des données de corps.

Ajoutez le code suivant à votre /controller/blogs.js fichier:


let blogs = (
    {
        id: 1,
        title: 'This is an experiment'
    },
    {
        id: 2,
        title: 'Fastify is pretty cool'
    },
    {
        id: 3,
        title: 'Just another blog, yea!'
    }
)


const getAllBlogs = async (req, reply) => {
    return blogs
}

const getBlog = async (req, reply) => {
    const id = Number(req.params.id) 
    const blog = blogs.find(blog => blog.id === id)
    return blog
}

const addBlog = async (req, reply) => {
    const id = blogs.length + 1 
    const newBlog = {
        id,
        title: req.body.title
    }

    blogs.push(newBlog)
    return newBlog
}

const updateBlog = async (req, reply) => {
    const id = Number(req.params.id)
    blogs = blogs.map(blog => {
        if (blog.id === id) {
            return {
                id,
                title: req.body.title
            }
        }
    })

    return {
        id,
        title: req.body.title
    }
}

const deleteBlog = async (req, reply) => {
    const id = Number(req.params.id)

    blogs = blogs.filter(blog => blog.id !== id)
    return { msg: `Blog with ID ${id} is deleted` }
}

module.exports = {
    getAllBlogs,
    getBlog,
    addBlog,
    updateBlog,
    deleteBlog
}

Notez comment nous pouvons accéder au paramètre de requête pour les routes telles que /api/blogs/:id via req.params.id. Pour les routes POST et PUT, nous pouvons accéder au corps de la requête via req.body.

À l'étape 2.2, nous allons connecter les gestionnaires d'itinéraire aux objets d'itinéraire.

Étape 2.2: définir les itinéraires de blog et le contrôleur de couple de blogs

Encore une fois, pour garder notre code propre, définissons un routes dossier à la racine du projet. Ici, nous créons un fichier appelé blogs.js. Ce fichier contient l'objet routes pour nos routes de blog:

mkdir routes
cd routes
touch blogs.js

Heureusement, Fastify nous permet de définir un tableau contenant des objets d'itinéraire. Ici, nous pouvons coupler les gestionnaires que nous avons définis précédemment aux différentes routes. N'oubliez pas d'exiger le contrôleur de blogs. Nous allons jeter un coup d'oeil:

const blogController = require('../controller/blogs');

const routes = ({
        method: 'GET',
        url: '/api/blogs',
        handler: blogController.getAllBlogs
    },
    {
        method: 'GET',
        url: '/api/blogs/:id',
        handler: blogController.getBlog
    },
    {
        method: 'POST',
        url: '/api/blogs',
        handler: blogController.addBlog
    },
    {
        method: 'PUT',
        url: '/api/blogs/:id',
        handler: blogController.updateBlog
    },
    {
        method: 'DELETE',
        url: '/api/blogs/:id',
        handler: blogController.deleteBlog
    }
)
module.exports = routes

Nous avons maintenant défini tous les itinéraires. Cependant, Fastify ne connaît pas ces itinéraires. L'étape suivante montre comment vous pouvez enregistrer des itinéraires avec votre objet d'application Fastify.

Étape 2.3: enregistrer les routes Fastify

Dans cette étape, nous enregistrerons les routes Fastify vers l'objet d'application. Tout d'abord, nous chargeons toutes les routes du blog. Ensuite, nous bouclons sur toutes les routes pour les enregistrer une par une:


const app = require('fastify')({
    logger: true
})


app.get('/', function (req, reply) {
    reply.send({ hello: 'world' })
})


const blogRoutes = require('./routes/blogs')
blogRoutes.forEach((route, index) => {
    app.route(route)
})


app.listen(3000, (err, address) => {
    if (err) {
        app.log.error(err)
        process.exit(1)
    }
    app.log.info(`server listening on ${address}`)
})

Terminé? Il est temps de valider si les itinéraires du blog fonctionnent. Faites tourner le serveur en utilisant node index.js et visiter http://localhost:3000/blogs/1 pour obtenir le premier blog à partir des données de démonstration. Vous devriez voir le résultat suivant:

{
    "id": 1,
    "title": "This is an experiment"
}

Tout bon? Apprenons à l'étape 3 comment ajouter une validation de schéma aux demandes et réponses.

Étape 3: Ajout de la validation de schéma

Cette étape vous apprend à ajouter une validation de schéma à votre projet. Nous pouvons utiliser le schema clé dans notre routes définition pour transmettre un schéma de validation à une route particulière.

Commençons par définir un schéma pour l'itinéraire /api/blogs/:id pour valider le paramètre de demande et la réponse. Exigences?

  1. :id le paramètre doit être de type chaîne
  2. la réponse doit contenir un objet avec deux propriétés id (entier) et title (chaîne)

Ajoutez l'objet de validation suivant à votre routes/blogs.js fichier:

const getBlogValidation = {
        params: {
            id: { type: 'string' }
        },
        response: {
            200: {
                type: 'object',
                properties: {
                    id: { type: 'integer' },
                    title: { type: 'string' }
                }
            }
        }
}

Pour connecter l'objet de validation à notre route, nous devons définir la clé de schéma. Cherchez le /api/blogs/:id itinéraire dans le routes tableau et modifiez l'objet en conséquence:

...
{
    method: 'GET',
    url: '/api/blogs/:id',
    schema: getBlogValidation, 
    handler: blogController.getBlog
},
...

Faisons de même pour ajouter un blog POST /api/blogs. Ici, nous voulons vérifier si le req.body objet contient un title paramètre. Nous allons jeter un coup d'oeil:

const addBlogValidation = {
    body: {
        type: 'object',
        required: (
            'title'
        ),
        properties: {
            title: { type: 'string' }
        }
    },
    response: {
        200: {
            type: 'object',
            properties: {
                id: { type: 'integer' },
                title: { type: 'string' }
            }
        }
    }
}

Ensuite, nous devons reconnecter l'objet de validation à la bonne route:

...
{
    method: 'POST',
    url: '/api/blogs',
    schema: addBlogValidation, 
    handler: blogController.addBlog
},
...

Pour vérifier notre validation, récupérons le blog avec l'ID 3. Ouvrez votre navigateur à l'adresse http://localhost:3000/api/blogs/3. Vous devriez voir la réponse suivante:

{
    "id": 3,
    "title": "Just another blog, yea!"
}

Maintenant, faisons une erreur et changeons le params validation pour le id champ de sting à object ainsi:

const getBlogValidation = {
        params: {
            id: { type: 'object' } 
        },
        response: {
            200: {
                type: 'object',
                properties: {
                    id: { type: 'integer' },
                    title: { type: 'string' }
                }
            }
        }
}

Lorsque vous demandez la même ressource à votre API, vous recevez le message d'erreur suivant.

{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "params.id should be object"
}

Voyez-vous l'erreur? Bien! Rétablissons la modification en string pour éviter de futures erreurs et passer à l'étape suivante.

Étape 4: Chargez les plugins Fastify

Ici, utilisons le riche écosystème de plugins de Fastify. Vous pouvez trouver des plugins qui vous aident dans diverses tâches, telles que les intégrations de bases de données ou les configurations d'autorisation. Pourquoi passeriez-vous du temps à rédiger une autorisation à partir de zéro alors que vous pouvez utiliser les plugins Fastify? Souvent, vous souhaitez rechercher des packages en dehors de l'écosystème de Fastify qui vous aident à résoudre certains problèmes ou certaines tâches. Cependant, en fournissant un riche écosystème de plugins, Fastify devient une solution unique qui améliore définitivement l'expérience des développeurs!

Une note rapide sur les plugins: vous pouvez créer vos propres plugins pour encapsuler les fonctionnalités. De plus, vous pouvez charger ces plugins dans votre objet d'application Fastify. Par défaut, Fastify chargera d'abord les plugins à partir de l'écosystème Fastify. Ensuite, les plugins personnalisés sont chargés.

Ok, soyons pratiques! Je voudrais utiliser le plugin fastify-env, qui vous aide à charger les variables d'environnement et à définir les valeurs par défaut pour chaque variable. Par conséquent, ajoutons cette dépendance à notre projet:

npm install --save fastify-env

Ensuite, nous pouvons charger la dépendance après avoir chargé l'objet d'application Fastify dans le index.js fichier. Votre index.js le fichier ressemble à ceci:


const app = require('fastify')({
    logger: true
})


const fastifyEnv = require('fastify-env') 

const options = {
    confKey: 'config', 
    schema: {
        type: 'object',
        required: ('PORT'),
        properties: {
            PORT: {
                type: 'string',
                default: 1000
            }
        }
    }
}

app
    .register(fastifyEnv, options)
    .ready((err) => {
        if (err) console.error(err)

        console.log(app.config)
        
    })


app.get('/', function (req, reply) {
    reply.send({ hello: 'world' })
})


const blogRoutes = require('./routes/blogs')
blogRoutes.forEach((route, index) => {
    app.route(route)
})


app.listen(app.config.PORT, (err, address) => {
    if (err) {
        app.log.error(err)
        process.exit(1)
    }
    app.log.info(`server listening on ${address}`)
})

Notez que nous devons définir un options objet qui indique au plugin fastify-env les variables d'environnement à rechercher et les valeurs par défaut à définir. Ici, je veux charger un PORT variable avec une valeur par défaut de 1000.

Par défaut, le plugin fastify-env rendra toutes les variables d'environnement disponibles via l'objet d'application Fastify comme suit: app.config.PORT. Pourquoi? Le plugin fastify-env attache les configurations chargées au confKey, qui par défaut est défini sur config. Cependant, si vous le souhaitez, vous pouvez le remplacer par une autre clé.

Démarrez le projet avec node index.js et surveillez la sortie. Vous devriez voir le PORT variable en cours d'impression dans votre terminal.

D'autres plugins intéressants à utiliser?

  1. fastify-auth: exécuter plusieurs fonctions d'authentification dans Fastify
  2. fastify-bearer-auth: plugin d'authentification du porteur pour Fastify
  3. mise en cache fastify: prise en charge générale du cache côté serveur et de l'ETAG
  4. fastify-cors: permet l'utilisation de CORS dans une application Fastify

Étape 5: définir les crochets

Enfin, définissons quelques crochets. Dans la documentation des hooks Fastify, nous pouvons lire ce qui suit. «Les hooks sont enregistrés avec la méthode fastify.addHook et vous permettent d'écouter des événements spécifiques dans l'application ou le cycle de vie des requêtes / réponses. Vous devez enregistrer un hook avant que l'événement ne soit déclenché, sinon l'événement est perdu. »

Assurez-vous de définir des hooks avant de définir des itinéraires:


app.addHook('onRoute', (routeOptions) => {
    console.log(`Registered route: ${routeOptions.url}`)
})


app.get('/', function (req, reply) {
    reply.send({ hello: 'world' })
})

Comme vous pouvez le voir, le addHook La fonction accepte d'abord le hook que vous souhaitez écouter. Dans notre exemple, nous voulons écouter les nouvelles routes enregistrées avec l'application. Ensuite, la fonction de rappel accepte un routeOptions argument qui contient beaucoup d'informations, telles que l'URL de la route ou la méthode de la route.

Détails spécifiques pour le onRoute hook peut être trouvé dans la documentation.

Commençons l'API avec node index.js pour voir quels itinéraires ont été enregistrés. La sortie de votre terminal devrait ressembler à ceci:

Registered route: /
Registered route: /api/blogs
Registered route: /api/blogs/:id
Registered route: /api/blogs
Registered route: /api/blogs/:id
Registered route: /api/blogs/:id

Vous avez le même résultat? Succès! En même temps, c'était la fin du tutoriel Fastify. Terminons ce projet par une brève conclusion.

Emballer

Fastify est un excellent projet léger qui vous permet d'utiliser son riche écosystème de plugins. Au lieu de créer des fonctionnalités à partir de zéro, vous pouvez utiliser les plugins existants. En d'autres termes, Fastify agit comme un guichet unique pour les développeurs, améliorant définitivement l'expérience des développeurs.

Personnellement, j'aime la fonctionnalité Fastify hooks car vous pouvez écouter divers événements du cycle de vie au sein de votre application.

Pour en savoir plus sur Fastify, consultez les pages de documentation suivantes:

Vous pouvez également consulter le référentiel de cette introduction sur GitHub.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *