├── .gitignore ├── .sequelizerc ├── lib └── models │ ├── user.js │ ├── todo.js │ └── index.js ├── db ├── config.json └── migrations │ ├── 20161116030820-create-user.js │ └── 20161116031206-create-todo.js ├── package.json ├── server.js └── readme.org /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.html 3 | db/*.sqlite -------------------------------------------------------------------------------- /.sequelizerc: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | 'config': path.resolve('./db', 'config.json'), 5 | 'migrations-path': path.resolve('./db', 'migrations'), 6 | 'models-path': path.resolve('./lib', 'models'), 7 | 'seeders-path': path.resolve('./db', 'seeders') 8 | } 9 | -------------------------------------------------------------------------------- /lib/models/user.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = function(sequelize, DataTypes) { 3 | var User = sequelize.define('User', { 4 | email: DataTypes.STRING 5 | }, { 6 | classMethods: { 7 | associate: function(models) { 8 | User.hasMany(models.Todo) 9 | } 10 | } 11 | }) 12 | return User 13 | } 14 | -------------------------------------------------------------------------------- /db/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "storage": "./db/development.sqlite", 4 | "dialect": "sqlite" 5 | }, 6 | "test": { 7 | "dialect": "sqlite", 8 | "storage": ":memory" 9 | }, 10 | "production": { 11 | "username": "root", 12 | "password": null, 13 | "database": "database_production", 14 | "host": "127.0.0.1", 15 | "dialect": "mysql" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/models/todo.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = function(sequelize, DataTypes) { 3 | var Todo = sequelize.define('Todo', { 4 | title: DataTypes.STRING, 5 | complete: DataTypes.BOOLEAN, 6 | UserId: DataTypes.INTEGER 7 | }, { 8 | classMethods: { 9 | associate: function(models) { 10 | Todo.belongsTo(models.User) 11 | } 12 | } 13 | }) 14 | return Todo 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sequelize_tutorial", 3 | "version": "1.0.0", 4 | "description": "* Sequelize: ORM", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.15.2", 14 | "express": "^4.14.0", 15 | "sequelize": "^3.25.0", 16 | "sqlite3": "^3.1.8" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /db/migrations/20161116030820-create-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: function(queryInterface, Sequelize) { 4 | return queryInterface.createTable('Users', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | email: { 12 | type: Sequelize.STRING 13 | }, 14 | createdAt: { 15 | allowNull: false, 16 | type: Sequelize.DATE 17 | }, 18 | updatedAt: { 19 | allowNull: false, 20 | type: Sequelize.DATE 21 | } 22 | }); 23 | }, 24 | down: function(queryInterface, Sequelize) { 25 | return queryInterface.dropTable('Users'); 26 | } 27 | }; -------------------------------------------------------------------------------- /db/migrations/20161116031206-create-todo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: function(queryInterface, Sequelize) { 4 | return queryInterface.createTable('Todos', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | title: { 12 | type: Sequelize.STRING 13 | }, 14 | complete: { 15 | type: Sequelize.BOOLEAN 16 | }, 17 | UserId: { 18 | type: Sequelize.INTEGER 19 | }, 20 | createdAt: { 21 | allowNull: false, 22 | type: Sequelize.DATE 23 | }, 24 | updatedAt: { 25 | allowNull: false, 26 | type: Sequelize.DATE 27 | } 28 | }); 29 | }, 30 | down: function(queryInterface, Sequelize) { 31 | return queryInterface.dropTable('Todos'); 32 | } 33 | }; -------------------------------------------------------------------------------- /lib/models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var Sequelize = require('sequelize'); 6 | var basename = path.basename(module.filename); 7 | var env = process.env.NODE_ENV || 'development'; 8 | var config = require(__dirname + '/../../db/config.json')[env]; 9 | var db = {}; 10 | 11 | if (config.use_env_variable) { 12 | var sequelize = new Sequelize(process.env[config.use_env_variable]); 13 | } else { 14 | var sequelize = new Sequelize(config.database, config.username, config.password, config); 15 | } 16 | 17 | fs 18 | .readdirSync(__dirname) 19 | .filter(function(file) { 20 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 21 | }) 22 | .forEach(function(file) { 23 | var model = sequelize['import'](path.join(__dirname, file)); 24 | db[model.name] = model; 25 | }); 26 | 27 | Object.keys(db).forEach(function(modelName) { 28 | if (db[modelName].associate) { 29 | db[modelName].associate(db); 30 | } 31 | }); 32 | 33 | db.sequelize = sequelize; 34 | db.Sequelize = Sequelize; 35 | 36 | module.exports = db; 37 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const app = express() 3 | const db = require(`${__dirname}/lib/models/index.js`) 4 | const bodyParser = require('body-parser') 5 | 6 | app.use(bodyParser.json()) 7 | app.use(bodyParser.urlencoded({ extended: false})) 8 | 9 | app.get('/', (req, res) => res.send('ok')) 10 | 11 | // create user 12 | app.post('/users', (req, res) => { 13 | db.User.create({ 14 | email: req.body.email 15 | }).then(function(user) { 16 | res.json(user) 17 | }) 18 | }) 19 | 20 | // create todo 21 | app.post('/todos', (req, res) =>{ 22 | db.Todo.create({ 23 | title: req.body.title, 24 | UserId: req.body.user_id 25 | }).then((todo) =>res.json(todo)) 26 | }) 27 | 28 | //list all todos 29 | app.get('/todos', (req, res) =>{ 30 | db.Todo.findAll({}).then((todos) => res.json(todos)) 31 | }) 32 | 33 | // one todo 34 | app.get('/todos/:id', (req, res) => { 35 | db.Todo.find({ 36 | where: { 37 | id: req.params.id 38 | } 39 | }).then((todo) => todo ? res.json(todo) : res.status(404).json({error: "unknown todo"})) 40 | }) 41 | 42 | // update todo 43 | app.put('/todo/:id', (req, res) =>{ 44 | db.Todo.find({ 45 | where: { 46 | id: req.params.id 47 | } 48 | }).then((todo) => { 49 | if(todo){ 50 | todo.updateAttributes({ 51 | title: req.body.title, 52 | complete: req.body.complete 53 | }).then(function(todo) { 54 | res.send(todo) 55 | }) 56 | } else 57 | res.status(404).json({error: "unknown todo"}) 58 | }) 59 | }) 60 | 61 | // delete todo 62 | app.delete('/todo/:id', (req, res) => { 63 | db.Todo.destroy({ 64 | where: { 65 | id: req.params.id 66 | } 67 | }).then((todo) => todo ? res.json(todo) : res.status(404).json({error: "unknown todo"})) 68 | }) 69 | 70 | const server = app.listen(3000, function () { 71 | var host = server.address().address 72 | var port = server.address().port 73 | console.log('Example app listening at http://%s:%s', host, port) 74 | 75 | }) 76 | -------------------------------------------------------------------------------- /readme.org: -------------------------------------------------------------------------------- 1 | * Sequelize: ORM 2 | 3 | Pour gérer les connexions à une base de données relationnelles, nous allons 4 | utiliser une librairie réalisant un [[https://fr.wikipedia.org/wiki/Mapping_objet-relationnel][mapping objet-relationnel]], ou ORM pour 5 | Object Relationnal Mapping en anglais. 6 | 7 | Le principe est d'associer une classe d'objets à une table. 8 | Un objet instance de la classe correspond alors à une ligne de la table. 9 | 10 | La classe fournit des accesseurs sur les objets, correspondant aux colonnes de 11 | la table. 12 | 13 | Par exemple, la classe `User` est associée à la table `users`. 14 | Si la table contient les colonnes `id`, de type entier, et `mail`, de type 15 | chaîne de caractère, alors la classe fournit les accesseurs `id` et `mail` sur 16 | les objets instances de la classe. 17 | 18 | Un ORM fournit généralement un outillage suffisant dans les cas simples pour 19 | générer les requêtes SQL classiques sur une table: 20 | 21 | - `SELECT` 22 | - `INSERT` 23 | - `UPDATE` 24 | 25 | * Présentation de sequelize 26 | 27 | [[http://docs.sequelizejs.com/en/v3/docs/models-usage/][Sequelize]] est un ORM pour node.js, qui supporte mysql, postgresql, sqlite3 et 28 | mssql. 29 | 30 | ** Installation 31 | 32 | On utilise npm, pour installer sequelize et également l'une des librairies 33 | en fonction du SGBD utilisé: 34 | 35 | #+BEGIN_SRC shell 36 | $ npm install --save sequelize 37 | 38 | # et un au choix parmi: 39 | $ npm install --save pg pg-hstore 40 | $ npm install --save mysql // For both mysql and mariadb dialects 41 | $ npm install --save sqlite3 42 | $ npm install --save tedious // MSSQL 43 | #+END_SRC 44 | 45 | On va également utiliser un outil en ligne de commande pour gérer la création 46 | des schéma des tables: 47 | 48 | #+BEGIN_SRC shell 49 | # installe la commande sequelize 50 | $ npm install -g sequelize-cli 51 | #+END_SRC 52 | 53 | * Connexion à la base 54 | Pour ce tutoriel, nous allons utiliser sqlite3, une base de données sans 55 | serveur. 56 | 57 | Pour l'installer sous debian/ubuntu: 58 | 59 | #+BEGIN_SRC shell 60 | sudo apt-get install sqlite3 61 | #+END_SRC 62 | 63 | La configuration de la connexion va ici consister uniquement à spécifier le 64 | chemin du fichier qui va contenir la base. 65 | 66 | Créer le fichier ~.sequelizerc~ avec le contenu suivant: 67 | 68 | #+BEGIN_SRC javascript 69 | var path = require('path'); 70 | 71 | module.exports = { 72 | 'config': path.resolve('./db', 'config.json'), 73 | 'migrations-path': path.resolve('./db', 'migrations'), 74 | 'models-path': path.resolve('./lib', 'models'), 75 | 'seeders-path': path.resolve('./db', 'seeders') 76 | } 77 | #+END_SRC 78 | 79 | Puis générez la configuration avec: 80 | 81 | #+BEGIN_SRC shell 82 | sequelize init 83 | #+END_SRC 84 | 85 | Reste à adapter le fichier ~db/config.json~ en fonction du SGBD utilisé, ici 86 | sqlite3: 87 | 88 | #+BEGIN_SRC json 89 | { 90 | "development": { 91 | "storage": "./db/development.sqlite", 92 | "dialect": "sqlite" 93 | }, 94 | "test": { 95 | "dialect": "sqlite", 96 | "storage": ":memory" 97 | }, 98 | "production": { 99 | "username": "root", 100 | "password": null, 101 | "database": "database_production", 102 | "host": "127.0.0.1", 103 | "dialect": "mysql" 104 | } 105 | } 106 | #+END_SRC 107 | 108 | 3 bases différentes sont définies, une par environnement: development, test, 109 | production. Nous allons juste nous servir de la base «development». 110 | 111 | Le code gérant la connexion est dans le module ~lib/models/index.js~: 112 | 113 | #+BEGIN_SRC javascript 114 | 'use strict'; 115 | 116 | var fs = require('fs'); 117 | var path = require('path'); 118 | var Sequelize = require('sequelize'); 119 | var basename = path.basename(module.filename); 120 | var env = process.env.NODE_ENV || 'development'; 121 | var config = require(__dirname + '/../../db/config.json')[env]; 122 | var db = {}; 123 | 124 | if (config.use_env_variable) { 125 | var sequelize = new Sequelize(process.env[config.use_env_variable]); 126 | } else { 127 | var sequelize = new Sequelize(config.database, config.username, config.password, config); 128 | } 129 | 130 | fs 131 | .readdirSync(__dirname) 132 | .filter(function(file) { 133 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 134 | }) 135 | .forEach(function(file) { 136 | var model = sequelize['import'](path.join(__dirname, file)); 137 | db[model.name] = model; 138 | }); 139 | 140 | Object.keys(db).forEach(function(modelName) { 141 | if (db[modelName].associate) { 142 | db[modelName].associate(db); 143 | } 144 | }); 145 | 146 | db.sequelize = sequelize; 147 | db.Sequelize = Sequelize; 148 | 149 | module.exports = db; 150 | #+END_SRC 151 | 152 | 1. lecture du fichier ~db/config.json~ 153 | 2. création de la connexion par appel de ~Sequelize~ 154 | 3. on retourne le résultat dans le symbole exporté par le module. 155 | 156 | Pour se connecter et manipuler la base de données, il suffira d'inclure ce 157 | module dans votre code. 158 | 159 | * Schéma: définition et création des tables 160 | 161 | Créons une table représentant des personnes, et une autre représentant des 162 | tâches. Chaque personne va avoir plusieurs tâches. 163 | 164 | #+BEGIN_SRC shell 165 | sequelize model:create --name User --attributes "email:string" 166 | #+END_SRC 167 | 168 | Les commandes ~model:create~ créent un fichier migration, représentant la mise à jour 169 | à faire sur le schéma de la base de données. 170 | 171 | Exemple pour la table users: 172 | 173 | #+BEGIN_SRC javascript 174 | 'use strict'; 175 | module.exports = { 176 | up: function(queryInterface, Sequelize) { 177 | return queryInterface.createTable('Users', { 178 | id: { 179 | allowNull: false, 180 | autoIncrement: true, 181 | primaryKey: true, 182 | type: Sequelize.INTEGER 183 | }, 184 | email: { 185 | type: Sequelize.STRING 186 | }, 187 | createdAt: { 188 | allowNull: false, 189 | type: Sequelize.DATE 190 | }, 191 | updatedAt: { 192 | allowNull: false, 193 | type: Sequelize.DATE 194 | } 195 | }); 196 | }, 197 | down: function(queryInterface, Sequelize) { 198 | return queryInterface.dropTable('Users'); 199 | } 200 | }; 201 | #+END_SRC 202 | On voit que la table créé sera ~Users~, avec les champs ~id~, ~email~, 203 | ~createdAt~ et ~updatedAt~. 204 | 205 | ~model.create~ crée également un fichier dans ~lib/models~. Toujours pour 206 | ~users~, ce sera ~lib/models/user.js~: 207 | 208 | #+BEGIN_SRC javascript 209 | 'use strict'; 210 | module.exports = function(sequelize, DataTypes) { 211 | var User = sequelize.define('User', { 212 | email: DataTypes.STRING 213 | }, { 214 | classMethods: { 215 | associate: function(models) { 216 | // associations can be defined here 217 | } 218 | } 219 | }); 220 | return User; 221 | }; 222 | #+END_SRC 223 | 224 | Créons maintenant la table Todos, dont une des colonnes va référencer l'id de la 225 | table users: 226 | 227 | #+BEGIN_SRC shell 228 | sequelize model:create --name Todo --attributes "title:string, complete:boolean,UserId:integer" 229 | #+END_SRC 230 | 231 | Pour coder l'assocation entre utilisateur et tâches, cela se passe dans les 232 | modèles javascript. 233 | 234 | Adapter les fichiers ~lib/models/user.js~ et ~lib/models/todo.js~: 235 | 236 | #+BEGIN_SRC javascript 237 | // lib/models/user.js 238 | 'use strict' 239 | module.exports = function(sequelize, DataTypes) { 240 | var User = sequelize.define('User', { 241 | email: DataTypes.STRING 242 | }, { 243 | classMethods: { 244 | associate: function(models) { 245 | User.hasMany(models.Todo) 246 | } 247 | } 248 | }) 249 | return User 250 | } 251 | #+END_SRC 252 | 253 | Pour ~todo.js~, on spécifie également une valeur par défaut pour le champ 254 | ~complete~: 255 | 256 | #+BEGIN_SRC javascript 257 | // lib/models/todo.js 258 | 'use strict' 259 | module.exports = function(sequelize, DataTypes) { 260 | var Todo = sequelize.define('Todo', { 261 | title: DataTypes.STRING, 262 | complete: { 263 | type: DataTypes.BOOLEAN, 264 | defaultValue: false 265 | } 266 | UserId: DataTypes.INTEGER 267 | }, { 268 | classMethods: { 269 | associate: function(models) { 270 | Todo.belongsTo(models.User) 271 | } 272 | } 273 | }) 274 | return Todo 275 | } 276 | #+END_SRC 277 | 278 | 279 | Pour créer les tables, il faut appliquer les migrations, et donc [[http://docs.sequelizejs.com/en/latest/docs/schema/][synchroniser]] la 280 | base de donnée avec la description javascript des fichiers ~lib/models/*.js~. 281 | 282 | 283 | #+BEGIN_SRC shell 284 | $ sequelize db:migrate 285 | 286 | Sequelize [Node: 6.9.0, CLI: 2.4.0, ORM: 3.25.0] 287 | 288 | Loaded configuration file "db/config.json". 289 | Using environment "development". 290 | == 20161116030820-create-user: migrating ======= 291 | == 20161116030820-create-user: migrated (0.069s) 292 | == 20161116031206-create-todo: migrating ======= 293 | == 20161116031206-create-todo: migrated (0.052s) 294 | #+END_SRC 295 | 296 | Le fichier ~db/development.sqlite~ contenant la base est créé. On peut regarder 297 | son contenu avec l'utilitaire ~sqlite3~: 298 | 299 | #+BEGIN_SRC shell 300 | sqlite3 db/development.sqlite 301 | sqlite> .schema 302 | CREATE TABLE `SequelizeMeta` (`name` VARCHAR(255) NOT NULL UNIQUE PRIMARY KEY, UNIQUE (name)); 303 | CREATE TABLE `Users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL); 304 | CREATE TABLE `Todos` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` VARCHAR(255), `complete` TINYINT(1), `UserId` INTEGER, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL); 305 | #+END_SRC 306 | 307 | La table ~SequelizeMeta~ contient la liste des migrations effectuées. 308 | 309 | * Utilisation dans une application express 310 | 311 | On va créer une application ~express.js~ pour démonter l'utilisation de 312 | sequelize, et les opérations de base sur les modèles liés à la base de données. 313 | 314 | #+BEGIN_SRC shell 315 | npm install --save express body-parser 316 | #+END_SRC 317 | 318 | ~body-parser~ est un middleware qui va décoder automatiquement les corps des 319 | requêtes http contenant du json ou provenant d'un formulaire html. 320 | 321 | Le squelette de l'applicacion, dans le fichier ~server.js~: 322 | 323 | #+BEGIN_SRC javascript 324 | // server.js 325 | const express = require('express') 326 | const app = express() 327 | const db = require(`${__dirname}/lib/models/index.js`) 328 | const bodyParser = require('body-parser') 329 | 330 | app.use(bodyParser.json()) 331 | app.use(bodyParser.urlencoded({ extended: false})) 332 | 333 | app.get('/', (req, res) => res.send('ok')) 334 | 335 | // rajouter les routes ici 336 | 337 | const server = app.listen(3000, function () { 338 | var host = server.address().address 339 | var port = server.address().port 340 | console.log('Example app listening at http://%s:%s', host, port) 341 | 342 | }) 343 | #+END_SRC 344 | 345 | 346 | 347 | ** Création 348 | 349 | Toutes les opérations de sequelize sont asynchrones et renvoient des promesses. 350 | 351 | Par exemple, pour créer un nouvelle ligne dans une table: 352 | 353 | #+BEGIN_SRC javascript 354 | db.TableName.create({ // js object, properties = table fields}) 355 | .then((result) => { // sucess}) 356 | .cach((error) => { // nope }) 357 | #+END_SRC 358 | 359 | Commençons par le code suivant, qui va nous permettre de créer un utilisateur: 360 | 361 | #+BEGIN_SRC javascript 362 | app.post('/users', (req, res) => { 363 | db.User.create({ 364 | email: req.body.email 365 | }).then(function(user) { 366 | res.json(user) 367 | }) 368 | }) 369 | #+END_SRC 370 | 371 | Pour tester, démarrez le serveur: 372 | 373 | #+BEGIN_SRC shell 374 | node server.js 375 | #+END_SRC 376 | 377 | Et dans un autre terminal: 378 | 379 | #+BEGIN_SRC shell 380 | curl --data "email=pierre.gambarotto@enseeiht.fr" http://127.0.0.1:3000/users 381 | {"id":1,"email":"pierre.gambarotto@enseeiht.fr","updatedAt":"2016-11-16T03:51:41.542Z","createdAt":"2016-11-16T03:51:41.542Z"} 382 | #+END_SRC 383 | 384 | On peut vérifier qu'une ligne a bien été créée dans la table ~users~: 385 | 386 | #+BEGIN_SRC shell 387 | sqlite3 db/development.sqlite 388 | SQLite version 3.14.1 2016-08-11 18:53:32 389 | Enter ".help" for usage hints. 390 | sqlite> select * from users; 391 | 1|pierre.gambarotto@enseeiht.fr|2016-11-16 03:51:41.542 +00:00|2016-11-16 03:51:41.542 +00:00 392 | #+END_SRC 393 | 394 | ** findAll & find 395 | Rajoutons maintenant les routes pour créer un todo et afficher tous les todos: 396 | 397 | #+BEGIN_SRC javascript 398 | // create todo 399 | app.post('/todos', (req, res) =>{ 400 | db.Todo.create({ 401 | title: req.body.title, 402 | UserId: req.body.user_id 403 | }).then((todo) =>res.json(todo)) 404 | }) 405 | 406 | //list all todos 407 | app.get('/todos', (req, res) =>{ 408 | db.Todo.findAll({}).then((todos) => res.json(todos)) 409 | }) 410 | #+END_SRC 411 | 412 | Pensez à redémarrer le serveur pour tester: 413 | 414 | #+BEGIN_SRC shell 415 | > curl --data "title=test&user_id=1" http://127.0.0.1:3000/todos 416 | {"id":1,"title":"test","UserId":"1","updatedAt":"2016-11-16T03:59:45.832Z","createdAt":"2016-11-16T03:59:45.832Z"} 417 | > curl --data "title=test&user_id=1" http://127.0.0.1:3000/todos 418 | {"id":2,"title":"test","UserId":"1","updatedAt":"2016-11-16T04:00:05.189Z","createdAt":"2016-11-16T04:00:05.189Z"} 419 | > sqlite3 db/development.sqlite 'select * from todos' 420 | 1|test||1|2016-11-16 03:59:45.832 +00:00|2016-11-16 03:59:45.832 +00:00 421 | 2|test||1|2016-11-16 04:00:05.189 +00:00|2016-11-16 04:00:05.189 +00:00 422 | #+END_SRC 423 | 424 | La liste des todos à partir du navigateur ~http://127.0.0.1:3000/todos~: 425 | 426 | #+BEGIN_SRC json 427 | [ 428 | 429 | { 430 | "id": 1, 431 | "title": "test", 432 | "complete": null, 433 | "UserId": 1, 434 | "createdAt": "2016-11-16T03:59:45.832Z", 435 | "updatedAt": "2016-11-16T03:59:45.832Z" 436 | }, 437 | { 438 | "id": 2, 439 | "title": "test", 440 | "complete": null, 441 | "UserId": 1, 442 | "createdAt": "2016-11-16T04:00:05.189Z", 443 | "updatedAt": "2016-11-16T04:00:05.189Z" 444 | } 445 | 446 | ] 447 | #+END_SRC 448 | 449 | Pour n'extraire qu'une ligne d'une table, et donc ne récupérer qu'un objet 450 | javascript, on va utiliser ~find~, en spécifiant l'équivalent d'une clause SQL 451 | ~WHERE~: 452 | 453 | #+BEGIN_SRC javascript 454 | // one todo 455 | app.get('/todos/:id', (req, res) => { 456 | db.Todo.find({ 457 | where: { 458 | id: req.params.id 459 | } 460 | }).then((todo) => todo ? res.json(todo) : res.status(404).json({error: "unknown todo"})) 461 | }) 462 | #+END_SRC 463 | 464 | ** Modifier/effacer 465 | 466 | Pour mettre à jour un todo 467 | 468 | #+BEGIN_SRC javascript 469 | // update 470 | app.put('/todo/:id', (req, res) =>{ 471 | db.Todo.find({ 472 | where: { 473 | id: req.params.id 474 | } 475 | }).then((todo) => { 476 | if(todo){ 477 | todo.updateAttributes({ 478 | title: req.body.title, 479 | complete: req.body.complete 480 | }).then(function(todo) { 481 | res.send(todo) 482 | }) 483 | } else 484 | res.status(404).json({error: "unknown todo"}) 485 | }) 486 | }) 487 | 488 | #+END_SRC 489 | 490 | Pour tester: 491 | 492 | #+BEGIN_SRC shell 493 | curl -X PUT --data "complete=true" http://127.0.0.1:3000/todo/2 494 | #+END_SRC 495 | 496 | Et enfin pour effacer: 497 | 498 | #+BEGIN_SRC javascript 499 | // delete todo 500 | app.delete('/todo/:id', (req, res) => { 501 | db.Todo.destroy({ 502 | where: { 503 | id: req.params.id 504 | } 505 | }).then((todo) => todo ? res.json(todo) : res.status(404).json({error: "unknown todo"})) 506 | }) 507 | #+END_SRC 508 | 509 | #+BEGIN_SRC shell 510 | curl -X DELETE http://127.0.0.1:3000/todo/2 511 | #+END_SRC 512 | 513 | ** Bilan des opérations 514 | 515 | Les opérations correspondant à un ~SELECT~ (~find~ et ~findAll) ou à un ~INSERT~ 516 | (~create~) se font sur les modèles, les opérations engendrant un ~UPDATE~ 517 | (~updateAttributes~) ou un ~DROP~ (~destroy~) se font sur une instance. 518 | 519 | 520 | * COMMENT notes 521 | 522 | - ~sequelize.sync()~ :: créé les tables manquantes, ~force:true~ : drop les tables 523 | existantes avant 524 | - [[https://github.com/sequelize/umzug][umzug]] :: gestion des migrations 525 | - ~sequelize.define()~ :: mapping table <-> modèle, voir la [[http://docs.sequelizejs.com/en/v3/docs/models-definition/][documentation]] 526 | 527 | plan: 528 | 529 | principe global ORM : table <-> modèle 530 | une instance du modèle <-> une ligne de la table 531 | 532 | création des tables : migrations 533 | 534 | les [[http://docs.sequelizejs.com/en/v3/docs/models-usage/][opérations de base]] : 535 | convention de nommage 536 | 537 | 538 | 539 | find/findAll 540 | create 541 | 542 | les relations entre modèle 543 | 544 | 1-1 545 | 1-n 546 | n-n 547 | 548 | tutoriel : http://mherman.org/blog/2015/10/22/node-postgres-sequelize/ 549 | 550 | nodemon à rajouter 551 | --------------------------------------------------------------------------------