DEV Community

Cover image for Créer un RESTFul API avec AdonisJS 5.0 (incluant l'authentification par token)
 Eric Le Codeur
Eric Le Codeur

Posted on • Updated on • Originally published at ericlecodeur.com

Créer un RESTFul API avec AdonisJS 5.0 (incluant l'authentification par token)

Si vous désirez plus de contenu francophone comme celui-ci, cliquer Follow ou suivez-moi sur Twitter


AdonisJS est à Javascript ce que Laravel est à PHP. Adonis est donc un framework backend construit avec l'idée d'apporter une expérience développeur (DX) extraordinaire.

Adonis rend disponible tous les outils que vous avez besoin pour bâtir une application fullstack de A à Z

Aujourd'hui vous allez découvrir comment bâtir un RESTful API avec Adonis. Vous allez même découvrir comment intégré l'authentification par token à votre API.

L'API que vous allez construire est une gestion de tâches. Vous aller créer un API pour créer des tâches, lire, effacer et mettre à jour des tâches

Vous allez également créer un API qui pourra créer un user et faire l'authentification. Ce dernier point peut vous sembler compliqué mais en faite Adonis à un package Auth qui s'occupe de presque tout.

Pré-requis

Avoir des connaissances de base sur les Restful API et sur les frameworks backend MVC

Partie 1 : Création du projet et de l'authentification

Créer un nouveau projet Adonis

$ npm init adonis-ts-app@latest my-project-name

  choisir project structure API
Enter fullscreen mode Exit fullscreen mode

Une fois le project créé, vous pouvez lancer le serveur local :

cd my-project-name
node ace serve -w
Enter fullscreen mode Exit fullscreen mode

Installer et configurer le module de base de données

npm i @adonisjs/lucid
node ace configure @adonisjs/lucid

  choisir SQLite
Enter fullscreen mode Exit fullscreen mode

Ici Adonis va créer une base de donnée SQLite qui sera pré-configuré et accessible de votre application

Installer and configurer le module auth

npm i @adonisjs/auth
node ace configure @adonisjs/auth

    - choisir Lucid
    - Saisir modèle User
    - choisir API Tokens
    - choisir créer la migration
    - choisir utiliser une base de donnée et créer la table pour stocker les Tokens 
Enter fullscreen mode Exit fullscreen mode

Le module Auth va vous permettre faire un login avec token

Ajouter le champ username au modèle User
(app/models/user.ts)

@column()
public username: string
Enter fullscreen mode Exit fullscreen mode

Par défaut le champ username n'est pas créer, vous allez donc en créer un.

Ajouter le champ username au fichier de migration User
(database/migrations/xxxxxxxxx_users.ts)

table.string('username', 255).notNullable()
table.string('email', 255)->notNullable().unique()
Enter fullscreen mode Exit fullscreen mode

Lancer la migration (afin de créer la table users)

node ace migration:run
Enter fullscreen mode Exit fullscreen mode

La migration s'occupe de créer et mettre à jour les tables et champs de votre base de données.

Installer le module pour hasher le mot de passe

npm i phc-argon2
Enter fullscreen mode Exit fullscreen mode

Ce module vous servira à crypter le mot de passe de l'utilisateur

Création de la route post pour permettre d'ajouter un User
(start/routes.ts)

Route.post('users', 'AuthController.register')
Enter fullscreen mode Exit fullscreen mode

Création du validator:
(validators/Auth/StoreUserValidator.ts)

node ace make:validator Auth/StoreUser
Enter fullscreen mode Exit fullscreen mode

Les Validator permettent d'annuler un requête si cette requête ne passe pas la validation.

Les Validator retournent également un message d'erreur de validation si la validation de passe pas

import { schema, rules } from @ioc:Adonis/Core/Validator

public schema = schema.create({
    email: schema.string({ trim: true }, [
      rules.email(),
      rules.unique({ table: 'users', column: 'email ' }),
    ]),
    username: schema.string({ trim: true }),
    password: schema.string(),
})
Enter fullscreen mode Exit fullscreen mode

Création du controller

node ace make:controller Auth
Enter fullscreen mode Exit fullscreen mode

Les controller contient toutes les fonctions que les routes vont exécuter

Ajouter une fonction 'register' au controller
(app/controllers/Http/AuthController.ts)

public async register({ request, response } : HttpContextContract) {

  const payload = await request.validate(StoreUserValidator)

  const user = await User.create(payload.user)

  return response.created(user) // 201 CREATED
}
Enter fullscreen mode Exit fullscreen mode

Cette fonction permet de créer un 'user' selon les informations soumis par l'API (email, username et password)

Login

Route.post('users/login', 'AuthController.login')
Enter fullscreen mode Exit fullscreen mode

Création du validator: Validators/Auth/LoginValidator.ts

node ace make:validator Auth/Login
Enter fullscreen mode Exit fullscreen mode
import { schema, rules } from @ioc:Adonis/Core/Validator

public schema = schema.create({
    email: schema.string({}, [rules.email()]),
    password: schema.string()
})
Enter fullscreen mode Exit fullscreen mode

Création de la fonction login dans le controller Auth

public async login({ auth, request, response }: HttpContextContract) {

    const { email, password } = await request.validate(LoginValidator)

    const token = await auth.attempt(email, password)
    const user = auth.user!

    return response.ok({
      "token": token,
      ...user.serialize(),
    })
}
Enter fullscreen mode Exit fullscreen mode

Cette fonction s'occupe de faire l'authentification et retourne un token que le client pourra utiliser pour accéder aux routes protégé.

Get user (start/route.ts)

Route.get('user', 'AuthController.me').middleware(['auth'])
Enter fullscreen mode Exit fullscreen mode
public async me({auth, response} : HttpContextContract) {

  return response.ok({ auth.user })
}
Enter fullscreen mode Exit fullscreen mode

Auth middleware (start/kernel.ts)

Server.middleware.registerNamed({ auth: () => import('App/Middleware/Auth') })
Enter fullscreen mode Exit fullscreen mode

Création du middleware qui permettre de vérifier le token

Création de la route put pour update User

Route.put('users', 'AuthController.update').middleware(['auth'])
Enter fullscreen mode Exit fullscreen mode

Création du validator: Validators/Auth/UpdateUserValidator.ts

node ace make:validator Auth/UpdateUser
Enter fullscreen mode Exit fullscreen mode
import { schema, rules } from @ioc:Adonis/Core/Validator

public schema =  schema.create({
  email: schema.string.optional({ trim: true }, [ 
    rules.email(),
    rules.unique({ table: 'users', column: 'email' }),
  ]),
  username: schema.string.optional({ trim: true }),
  password: schema.string.optional(),
})
Enter fullscreen mode Exit fullscreen mode

Création de la fonction update du controller Auth
(app/Controllers/Http/AuthController.ts)

public async update({ auth, request, response } : HttpContextContract) {

  const payload = await request.validate(UpdateUserValidator)

  const user = await auth.user!.merge(payload).save()

  return response.ok(user) // 200 OK
}
Enter fullscreen mode Exit fullscreen mode

Partie 2 - Création du modèle Task

Création du modèle, de la migration et du controller

node ace make:model Task -cm
Enter fullscreen mode Exit fullscreen mode

L'option -cm va créer le fichier de la migration et le fichier du controller

Ouvrir la migration et ajouter les champs voulus:

public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')
      table.integer('user_id').unsigned().references('users.id').onDelete('CASCADE')
      table.string('name').notNullable()
      table.boolean('is_done').defaultTo(false)
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }
Enter fullscreen mode Exit fullscreen mode

Ouvrir le modèle et ajouter les columns et la relation belongTo

import { DateTime } from 'luxon'
import { BaseModel, BelongsTo, belongsTo, column, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
import User from './User'

export default class Task extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @column()
  public name: string

  @column()
  public is_done: boolean

  @belongsTo(() => User)
  public user: BelongsTo<typeof User>
}
Enter fullscreen mode Exit fullscreen mode

Ouvrir le fichier modèle User et ajouter le HasMany

@hasMany(() => Task)
  public tasks: HasMany<typeof Task>
Enter fullscreen mode Exit fullscreen mode

Créer les routes pour le CRUD de Tasks

Route.resource('tasks', 'TaskController').apiOnly()

// Cette ligne de code va créer 5 chemin urls pour le CRUD

// Liste des tâches: GET /tasks (tasks.index)
// Sauvegarder une tâches: POST /tasks (tasks.store)
// Lire une tâche: GET tasks/:id (tasks.show)
// Mise à jour d'une tâche: PUT tasks/:id (tasks.update)
// Effacer une tâche: DELETE tasks/:id (tasks.destroy)

Enter fullscreen mode Exit fullscreen mode

Dans le fichier TasksController créer les 5 actions CRUD

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Task from 'App/Models/Task'
import StoreTaskValidator from 'App/Validators/Tasks/StoreTaskValidator'
import UpdateTaskValidator from 'App/Validators/Tasks/UpdateTaskValidator'

export default class TasksController {
  public async index ({response}: HttpContextContract) {
    const tasks = await Task.all()
    return response.ok(tasks)
  }

  public async store ({ request, response }: HttpContextContract) {
    const payload = await request.validate(StoreTaskValidator)
    const task = await Task.create(payload)
    return response.created(task)
  }

  public async show ({ response, params }: HttpContextContract) {
    console.log(params)
    const task = await Task.findOrFail(params.id)
    return response.ok(task)
  }

  public async update ({ request, response, params }: HttpContextContract) {
    const task = await Task.findOrFail(params.id)
    const payload = await request.validate(UpdateTaskValidator)
    task.merge(payload).save()
    return response.ok(task)
  }

  public async destroy ({ response, params }: HttpContextContract) {
    const task = await Task.findOrFail(params.id)
    task.delete()
    return response.ok(task)
  }
}
Enter fullscreen mode Exit fullscreen mode

Créer le StoreTaskValidator

node ace make:validator Tasks/StoreTask

public schema = schema.create({
    name: schema.string(),
    is_done: schema.boolean(),
  })
Enter fullscreen mode Exit fullscreen mode

Créer le UpdateTaskValidator

node ace make:validator Tasks/UpdateTask

public schema = schema.create({
    name: schema.string.optinal(),
    is_done: schema.boolean.optional(),
  })
Enter fullscreen mode Exit fullscreen mode

Conclusion

Comme vous avez sans douter constater, la syntaxe de Adonis pour créer un Restful API est très propre. Adonis est un des premiers, sinon le premier framework javascript qui rappel le plaisir et l'efficacité pour les développeurs d'utiliser un framework comme Laravel.

Top comments (2)

Collapse
 
chapristi profile image
chapristi

thanks a lot

Collapse
 
adzetko profile image
Maxim "Adzy" Hohengarten

Comme je suis en train de mettre le nez dans les bases de données, je me demandais si ça ferait beaucoup de changements de suivre ce tuto mais pour du MongoDB ? C'est peut-être une question bête mais bon moi je touche surtout du front-end du coup je sais pas trop