-
Guillaume WANG authoredGuillaume WANG authored
Mémo d'introduction à Knex.js pour les nuls
Note : ce mémo a été rédigé à l'origine pour le CONTRIBUTING.md de shitpost-backend, donc fait parfois référence à des fichiers de ce projet. C'est indiqué à chaque fois lorsque c'est le cas.
Knex.js is a "batteries included" SQL query builder for Postgres, MSSQL, MySQL, MariaDB, SQLite3, Oracle, and Amazon Redshift designed to be flexible, portable, and fun to use.
J'utilise Knex.js avec PostgreSQL (comme dans sigma).
knexfile.js et knex_init.js
knexfile.js
:
- Fichier utilisé par les command-line-tools de knex (
knex migrate:*
etknex seed:*
) - Le rôle principal du fichier est de préciser (quelle db on utilise, et) où stocker les fichiers de migrations Knex ainsi que les seeds.
- Malheureusement, la documentation de knex est très pauvre sur quoi mettre dans knexfile.js... [2018-10-13]
- Destiné aux migrations et aux seeds, pas aux fichiers js.
- Correspond à cette partie de la doc : https://knexjs.org/#knexfile (Migrations/CLI/knexfile)
- Pour utiliser les commandes
knex
en CLI, il faut se placer dans le directory qui contient ce fichier.
knex_init.js
:
- Configure et exporte un objet knex permettant les requêtes SQL
- Spécifie la bdd à laquelle se connecter, son adresse, en tant que quel user (en l'occurrence quel Role) se connecter au serveur, et son mdp.
- Contrairement à
knexfile.js
, il est destiné au serveur Nodejs (i.e. aux fichiers js).- Correspond à cette partie : https://knexjs.org/#Installation-client ("Initializing the library")
Il est évident que ces deux fichiers ont en gros le même contenu ! C'est un "problème" connu, et actuellement (2018-10, knexjs v0.15.2) c'est bien comme ça qu'on est censé faire. (Mais en fait ce n'est pas un gros problème, juste un peu confusing, et engendre un peu de redondance.)
Interface en ligne de commande
L'interface en ligne de commande de Knex.js contient les commandes suivantes :
-
knex migrate:make <nom_migration>
: crée un fichier de migration viergedb/migrations/[timestamp]_nom_migration.js
-
knex migrate:latest
: met à jour le schéma de la BDD en exécutant les nouvelles migrations dans l'ordre de leur timestamp (utilise lesexports.up
) -
knex migrate:rollback
: annule toutes les migrations (utilise lesexports.down
) -
knex seed:make <nom_seed>
: crée un fichier seed viergedb/seeds/[nn]_<nom_seed>.js
, oùnn
est le numéro du seed -
knex seed:run
: insère les seeds dans la BDD, en exécutant tous les seeds dans l'ordre de leur numéro
"Migrations" et "Seeds"
https://gist.github.com/NigelEarle/70db130cc040cc2868555b29a0278261
Migrations
Migrations are a way to make database changes or updates, like creating or dropping tables, as well as updating a table with new columns with constraints, via generated scripts. We can build these scripts via the command line using
knex
command line tool.
En gros, les migrations permettent de définir, de façon "statique" et donc plus lisible, le schéma de la BDD. Elles permettent également de définir "statiquement" les modifications à apporter au schéma d'une BDD existante. C'est moins utile pour nous, mais sert à apporter des modifications à la BDD d'une application déjà en production, en s'assurant que si ça casse quelque chose on aura la possibilité de revenir en arrière.
Les fichiers définissant les migrations consistent en une liste de commandes knex qui disent exactement quoi faire (le exports.up
). 1
Ils comportent aussi une liste de commandes knex qui disent comment défaire la migration (le exports.down
).
Ecrire des migrations
Pour créer un fichier de migration vierge, on lance la commande knex migrate:make <nom_migration>
, qui crée un fichier db/migrations/[hash]_nom_migration.js
où le hash est la date et l'heure.
Maintenant, on édite ce fichier. Il exporte deux fonctions, exports.up
et exports.down
, qui définissent respectivement comment mettre à jour le schéma de la BDD et comment annuler la modification si knex migrate:rollback
est invoqué.
Il faut se référer à la documentation de Knex.js pour comprendre comment écrire les requêtes. Voici un cheatsheet.
Seeds
Similar to migrations, the
knex
module allows us to create scripts, called seed files, to insert initial data into our tables!
Les seeds permettent d'insérer des données dans la BDD. Bien sûr, il est beaucoup plus logique de définir (knex migrate:make <nom_migrations>
) et d'exécuter (knex migrate:latest
) ses migrations, avant de gérer les seeds.
ON DELETE
Il peut y avoir un petit problème avec les seeds si on ne fait pas attention.
Puisqu'à chaque knex seed:run
on DELETE toutes les entrées de toutes les tables avant de les reconstruire (cf. ligne 3 de tous les seedfiles : "// Deletes ALL existing entries. return knex('table_name').del().then(...)
"), on tombe sur ce genre de problème :
error: update or delete on table "channels" violates foreign key constraint "messages_channel_foreign" on table "messages"
Solution : spécifier dans le schéma (fichiers migrations) ce qu'il faut faire quand l'objet référencé par une colonne FOREIGN KEY est détruit, avec l'option ON DELETE (reflété par .onDelete(...)
de knex). https://www.postgresql.org/docs/current/static/ddl-constraints.html#DDL-CONSTRAINTS-FK
Choix du schéma et rédaction des migrations
[spécifique à shitpost :]
On a défini les migrations (./db/migrations/*
) en se basant sur le schéma GraphQL (typeDefs.js
) (et pas l'inverse), ce qui se voit bien puisqu'on suit presque exactement ce dernier.
[spécifique à shitpost :]
Un point particulier cependant : comme expliqué en commentaires du migration-file ./db/migrations/[timestamp]_create_comments.js
, on n'a pas déclaré le champ "forMessage" de la table "comments" comme ayant "messages.id" comme foreign key, car ce n'est pas possible avec PostgreSQL v10.
-
C'est le même principe que quand on exporte une BDD SQL : le fichier obtenu est en fait une liste de commandes SQL qui, lorsqu'executée, recrée la BDD exportée. ↩