目的
バックエンドをESモジュールで記載できるようにしたい(フロントエンドに合わせる)という目的です。
(babelにするかtypescriptにするかで悩んでいます)
おまけにsequelizeを追加して、動作することも確認する。
完成後ソース(tagのv1がsequelize追加前まで、v2(最新コミット)が最後まで)
https://github.com/murasuke/express-generator-babel
結果
- ESMとしてexportしたモジュールはimportする、module.exportの場合(node)はrequire()を利用するということに気を付ければ、混在していても問題なく動く。
実現手段
expessをES6(import)で利用できるようにするため、babelでトランスパイルする。
⇒package.json に 「type: module」を追加すればESモジュールを使えますが、jsファイルの拡張子(.cjs、.mjs)の切り分けが面倒なのでbabelにします。express-generatorでひな形を生成する。
開発時は「babel-node」で実行(ビルド不要)可能とする。トランスパイルしたjsファイルが必要な場合はbuildを行う。
sequelizeと組み合わせて使用する(sequelizeはnodeモジュール形式で生成される。ES6形式で記載したexpress側で問題なく利用できることを確認する)
前提
- nodeとyarnがインストール済みであること (npmを使う場合は適宜読み替えてください)
利用モジュール
{"dependencies":{"cookie-parser":"~1.4.4","debug":"~2.6.9","express":"~4.16.1","morgan":"~1.9.1"},"devDependencies":{"@babel/cli":"^7.12.10","@babel/core":"^7.12.10","@babel/node":"^7.12.10","@babel/preset-env":"^7.12.11"}}
手順
①expressのひな形をESモジュールに変更し、babelでコンパイルする
1. express-generatorでひな形を作成(viewは生成しない。.gitignoreは生成する)
npx express-generator express-generator-babel --no-view--git
2. (作成されたフォルダに移動し)必要なライブラリをインストール
yarn install
3. expressが起動することを確認
yarn start
ブラウザで「http://localhost:3000」を表示する
4. babelインストール
yarn add @babel/cli @babel/core @babel/preset-env -D
5. babelの設定ファイル(.babelrc)を作成する
targetにcurrentを入れることで、インストールされているnode.jsが理解できる形でトランスパイルされます。
(最近のnodeであればawaitやasyncなどがヘルパー関数ではなく、そのまま出力されます(pollyfill不要))
{"presets":[["@babel/preset-env",{"targets":{"node":"current"}}]]}
6. babelでビルドするため、フォルダ構成を変更する。
- jsファイルを「/src」フォルダ配下にすべて移動する
mkdir src
mv bin/ routes/ app.js src/
※「bin」「routes」フォルダと、app.jsファイルを「src」フォルダに移動している。
- フォルダ階層が変わったためソースを一部修正する
app.js (publicフォルダの位置を修正)
app.use(express.static(path.join(__dirname, 'public')));↓
app.use(express.static(path.join(__dirname, '../public')));
- トランスパイル先フォルダを作成する
mkdir dist
7. ビルド用コマンドをpackage.jsonに追加する
package.json の "scripts"に下記コマンドを追加する。
"build":"babel src --out-dir dist --copy-files && babel ./src/bin/www --out-file ./dist/bin/www"
※.src/bin/www は拡張子がないjsファイル。フォルダ指定では対象にならないため、個別にトランスパイルをしている。
8. babelでトランスパイルを行う
yarn build
9. トランスパイルしたソースで動作確認を行う
- package.json の "scripts"に下記コマンドを追加する。
"serve":"node ./dist/bin/www"
- サーバを起動する
yarn serve
ブラウザで「localhost:3000」を開き、expressのトップページが開くことを確認する。
10. ESモジュールに変更して動作確認を行う
- require()をimportに変更しても動作することを確認する。
- module.exportsからES6のexportに変更した場合、読み込み元も「import」に修正する必要がある。
- ソース内でrequire()をimportに変更しても、外部にエクスポートする「module.export」を変更しなければ、呼び出し元は変更不要。
/src/routes/index.js
// var express = require('express'); //コメントアウトimportexpressfrom'express';//追加varrouter=express.Router();/* GET home page. */router.get('/',function(req,res,next){res.render('index',{title:'Express'});});// module.exports = router; //コメントアウトexportdefaultrouter;//追加
/src/app.js
// var express = require('express'); //コメントアウト// var path = require('path');// var cookieParser = require('cookie-parser');// var logger = require('morgan');importexpressfrom'express';//追加importpathfrom'path';importcookieParserfrom'cookie-parser';importloggerfrom'morgan';// var indexRouter = require('./routes/index'); //コメントアウトimportindexRouterfrom'./routes/index';//追加varusersRouter=require('./routes/users');varapp=express();app.use(logger('dev'));app.use(express.json());app.use(express.urlencoded({extended:false}));app.use(cookieParser());app.use(express.static(path.join(__dirname,'../public')));app.use('/',indexRouter);app.use('/users',usersRouter);module.exports=app;//コメントアウトしない。読み込み元(bin/www)を変更せず、require()のままとするため。//export default app; // exportに書き換えると、読み込み元をimportに書き換える必要がある。
11. ESモジュール化後の動作確認
yarn build
yarn serve
- ブラウザで「localhost:3000」を開き、expressのトップページが開くことを確認する。
12. 開発用に「babel-node」を導入する(ビルド無しで動作させるため)
- babel-nodeをインストールする
yarn add @babel/node -D
- package.jsonの startを修正する
"start":"node ./bin/www", ↓"start":"babel-node ./src/bin/www",
- babel-nodeで起動することを確認する(build不要)
yarn start
ブラウザで「localhost:3000」を開き、expressのトップページが開くことを確認する。
②sequelize+sqliteを追加してデータを取得する
sequelize-cliを利用して生成したひな形(node module形式)を、express側から利用できることを確認する。
13. sequelize導入(DBはsqliteを利用する。別にサーバを作る必要がないため)
yarn add sequelize sqlite3
yarn add sequelize-cli -D
- 初期化時に自動生成されるフォルダを「/src」配下にするため、先に「.sequelizerc」ファイルを作成する(プロジェクトルート)
touch .sequelizerc
- sequelize関連ファイルを「/src/seqelize」で管理するように設定する(指定しない場合は、プロジェクトルートに作成される)
constpath=require('path');module.exports={'config':path.resolve('./src/sequelize/config','config.json'),'models-path':path.resolve('./src/sequelize/models'),'seeders-path':path.resolve('./src/sequelize/seeders'),'migrations-path':path.resolve('./src/sequelize/migrations'),};
- マイグレーションのための初期化を行う
yarn sequelize init
※コマンドは、sequelize, sequelize-cli どちらでもよい(エイリアス)
プロジェクトルートではなく、/src/sequelizeに作成されます。
- config.jsonをsqlite用に書き換える。
{"development":{"database":"database_development","dialect":"sqlite","storage":"db/proto_app_dev.sqlite"},"test":{"database":"database_test","dialect":"sqlite","storage":"db/proto_app_test.sqlite"},"production":{"database":"database_production","dialect":"sqlite","storage":"db/proto_app.sqlite"}}
14. sequelize-cli でテーブル定義のひな形を作成後、マイグレーションを行う
- sequelize model:generateでmodel定義と、マイグレーション用のひな形を生成する。
yarn sequelize model:generate --name User --attributes name:string,birth:date,email:string
- マイグレーションを行い、テーブルを作成する
yarn sequelize db:migrate
15. テスト用データを登録(db:seed)する
- ひな形を作成する
yarn sequelize seed:generate --name user
- ひな形を修正(登録データを用意する)
'use strict';module.exports={up:async(queryInterface,Sequelize)=>{constnow=newDate();constbirth=newDate(now);constseeds=[];constsubtractYear=(date,year)=>newDate(date.setYear(date.getFullYear()-year));seeds.push({name:"name1",birth:subtractYear(birth,1),email:"mail1@example.com",createdAt:now,updatedAt:now});seeds.push({name:"name2",birth:subtractYear(birth,1),email:"mail2@example.com",createdAt:now,updatedAt:now});seeds.push({name:"name3",birth:subtractYear(birth,1),email:"mail3@example.com",createdAt:now,updatedAt:now});returnawaitqueryInterface.bulkInsert("users",seeds,{});},down:async(queryInterface,Sequelize)=>{awaitqueryInterface.bulkDelete('users',null,{});}};
- seedを登録する
yarn sequelize db:seed:all
※やり直す場合は「yarn sequelize db:seed:undo:all」
16. 作成したmodelをexpress側から利用し、登録したデータが取得できることを確認する
- ./route/users.jsを修正し、sequelizeのmodel(users)を全レコードjson形式で返すように修正する
修正前
varexpress=require('express');varrouter=express.Router();/* GET users listing. */router.get('/',function(req,res,next){res.send('respond with a resource');});module.exports=router;
修正後
importdbfrom"../sequelize/models/index";importexpressfrom'express';constrouter=express.Router();/* GET users listing. */router.get('/',asyncfunction(req,res,next){//res.send('respond with a resource');constusers=awaitdb.User.findAll();res.json(users);});// module.exports = router;exportdefaultrouter;
呼び出し元(app.js)も変更する。
// var usersRouter = require('./routes/users');importusersRouterfrom'./routes/users';
17. ビルド後、データが取得できることを確認する
yarn build
yarn serve
- localhost:3000/users にアクセスして、DBからデータが取得できることを確認する
[{"id":1,"name":"name1","birth":"2020-01-10T09:51:38.737Z","email":"mail1@example.com","createdAt":"2021-01-10T09:51:38.737Z","updatedAt":"2021-01-10T09:51:38.737Z"},{"id":2,"name":"name2","birth":"2019-01-10T09:51:38.737Z","email":"mail2@example.com","createdAt":"2021-01-10T09:51:38.737Z","updatedAt":"2021-01-10T09:51:38.737Z"},{"id":3,"name":"name3","birth":"2018-01-10T09:51:38.737Z","email":"mail3@example.com","createdAt":"2021-01-10T09:51:38.737Z","updatedAt":"2021-01-10T09:51:38.737Z"}]