何かを簡単に作って、ちょっとした勉強になる。そんなシリーズになる予定です。
今回は、Node.jsで簡単なTodoアプリに使うAPIサーバーを作っていきます。データベースはMySQLで、データベース操作にはSequelizeを利用します。
完成品はこちら -> sequelize-todo-api-server
材料
- mysql2
- sequelize
- sequelize-cli
- express
作り方
1. ライブラリのインストール
適当にディテクトリ作って、npm init
で初期化してから、今回使うライブラリをインストールします。
npm i sequelize mysql2
npm i -D sequelize-cli
予めMySQLサーバーをインストールして、設定をしておきます。以下は今回使うユーザー名とパスワードです。
usemysql;createusersequelize@localhostidentifiedby'password';mysql>grantallon*.*tosequelize@localhost;exit;
2. データベースを作る
データベースがMySQLかPostgreSQLの場合は、sequelize-cliでコマンドを打っていくことで簡単にデータベースのマイグレーションや初期値のセットなどができます。
sequelize-cli init
を利用して、Sequelizeの初期化をします。
npx sequelize-cli init
Sequelize CLI [Node: 14.2.0, CLI: 5.5.1, ORM: 5.21.10]
Created "config/config.json"
Successfully created models folder at "/home/kei/Programming/Project/temp/sequelize-test/models".
Successfully created migrations folder at "/home/kei/Programming/Project/temp/sequelize-test/migrations".
Successfully created seeders folder at "/home/kei/Programming/Project/temp/sequelize-test/seeders".
初期化をすると、3つのディレクトリが作成されます。config/config.jsonの設定をしていきます。自動生成されたconfig.jsonのusernameとpasswordの部分を実際のデータベースで設定したユーザー名とパスワードにします。operatorsAliasesの項目があるとエラーになるので、ある場合は削除します。
{"development":{"username":"sequelize","password":"password","database":"database_development","host":"127.0.0.1","dialect":"mysql"},"test":{"username":"sequelize","password":"password","database":"database_test","host":"127.0.0.1","dialect":"mysql"},"production":{"username":"sequelize","password":"password","database":"database_production","host":"127.0.0.1","dialect":"mysql"}}
下のコマンドでデータベースを作ります。
$ npx sequelize-cli db:create
Sequelize CLI [Node: 14.2.0, CLI: 5.5.1, ORM: 5.21.10]
Loaded configuration file "config/config.json".
Using environment "development".
Database database_development created.
config.jsonのdevelopmantの設定に従って、データベースが作成されました。
3. モデルとテーブルの作成
npx sequelize-cli model:generate
でモデルを作成し、npx sequelize-cli db:migrate
でデータベースのテーブルを作成します。
$ npx sequelize-cli model:generate --name Todo --attributes title:string,completed:boolean
Sequelize CLI [Node: 14.2.0, CLI: 5.5.1, ORM: 5.21.10]
New model was created at /home/kei/Programming/Project/temp/sequelize-test/models/todo.js .
New migration was created at /home/kei/Programming/Project/temp/sequelize-test/migrations/20200521050853-Todo.js .$ npx sequelize-cli db:migrate
Sequelize CLI [Node: 14.2.0, CLI: 5.5.1, ORM: 5.21.10]
Loaded configuration file "config/config.json".
Using environment "development".== 20200521050853-create-todo: migrating ========= 20200521050853-create-todo: migrated (0.087s)
mysql>usedatabase_development;Databasechangedmysql>showtables;+--------------------------------+|Tables_in_database_development|+--------------------------------+|SequelizeMeta||Todos|+--------------------------------+2rowsinset(0.00sec)mysql>select*fromTodos;Emptyset(0.00sec)
4. テスト用の初期値の準備
テスト用の初期値を作成します。Seederを利用すると、コマンドで初期値をセットしたり、破棄したりできるようになります。
npx sequelize-cli seed:generate
でSeederを用意します。
$ npx sequelize-cli seed:generate --name test-data
Sequelize CLI [Node: 14.2.0, CLI: 5.5.1, ORM: 5.21.10]
seeders folder at "/home/kei/Programming/Project/temp/sequelize-test/seeders" already exists.
New seed was created at /home/kei/Programming/Project/temp/sequelize-test/seeders/20200521060543-test-data.js .
このコマンドで作成されたファイルに、初期値を書いていきます。
"use strict";module.exports={up:(queryInterface,Sequelize)=>{consttodos=[{title:"草むしり",completed:false,updatedAt:newDate(2020,4,22,9,47,19),createdAt:newDate(2020,4,22,9,47,19),},{title:"買い物",completed:true,updatedAt:newDate(2020,4,20,18,2,33),createdAt:newDate(2020,4,20,15,41,58),},{title:"トイレ掃除",completed:false,updatedAt:newDate(2020,4,20,18,4,21),createdAt:newDate(2020,4,20,18,4,21),},];returnqueryInterface.bulkInsert("Todos",todos);},down:(queryInterface,Sequelize)=>{returnqueryInterface.bulkDelete("Todos",null,{});},};
npx sequelize db:seed:all
でテーブルに初期値を書き込みます。
$ npx sequelize db:seed:all
Sequelize CLI [Node: 14.2.0, CLI: 5.5.1, ORM: 5.21.10]
Loaded configuration file "config/config.json".
Using environment "test".== 20200521060543-test-data: migrating ========= 20200521060543-test-data: migrated (0.049s)
mysql>usedatabase_development;Databasechangedmysql>select*fromTodos;+----+-----------------+-----------+---------------------+---------------------+|id|title|completed|createdAt|updatedAt|+----+-----------------+-----------+---------------------+---------------------+|1|草むしり|0|2020-05-2200:47:19|2020-05-2200:47:19||2|買い物|1|2020-05-2006:41:58|2020-05-2009:02:33||3|トイレ掃除|0|2020-05-2009:04:21|2020-05-2009:04:21|+----+-----------------+-----------+---------------------+---------------------+3rowsinset(0.00sec)
5. データ処理する関数を書く
とりあえず、CRUD処理をするため、
- create: 1つのアイテムを書き込む
- read: すべてのアイテムを取得
- update: 1つのアイテムを更新する
- deleteItem: 1つのアイテムを削除する
という4つの関数を書きます。updateとdeleteItemはidで対象アイテムを指定します。
const{Todo}=require("../models");constcreate=async(title,completed)=>{consttodo=awaitTodo.create({title,completed});returntodo.dataValues;};constread=async()=>{consttodos=awaitTodo.findAll();returntodos.map((todo)=>todo.dataValues);};constupdated=async(id,title,completed)=>{constisUpdate=awaitTodo.update({title,completed},{where:{id,},});returnisUpdated[0]?{message:"updated"}:{error:"item not found"};};constdeleteItem=async(id)=>{constisDeleted=awaitTodo.destroy({where:{id,},});returnisDeleted?{message:"deleted"}:{error:"item not found"};};module.exports={create,read,update,deleteItem};
update関数のisUpdatedはアップデートした件数が配列の1番目に入っています。なので、それでアップデートしたかを判定しています。deleteItem関数のisDeletedも同様です。
6. サーバーを作る
Expressでサーバーを作ります。先ほど作った関数とURLを結びつけるだけです。
constexpress=require("express");constdb=require("./lib/db-simple");constapp=express();app.use(express.json());app.use(express.urlencoded({extended:true}));app.get("/",async(req,res)=>{consttodos=awaitdb.read();res.json(todos);});app.post("/create",async(req,res)=>{constresult=awaitdb.create(req.body.title,req.body.completed);res.json(result);});app.put("/update",async(req,res)=>{constresult=awaitdb.update(req.body.id,req.body.title,req.body.completed);res.json(result);});app.delete("/delete",async(req,res)=>{constresult=awaitdb.deleteItem(req.body.id);res.json(result);});app.listen(3000,()=>console.log("listening on port 3000"));
下はPostmanで"/"にget、"/create"にpostしたときの結果です。
Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.
おわりに
Sequelizeは、慣れればDjangoのORMとほぼ同じように使えるような気がします。とても便利です。
これ書いた人は色々調べながら+実は少しテストも書きながら+記事書きながら作っていたので、作成日数が1日を超えました\(^o^)/
コード -> sequelize-todo-api-server