##はじめに
今まではPHPでAPIを構築することが多かったが、メンバーのスキルセットがjs寄りだったため、
express(node.js)でAPIを構築することになったので、その際のメモ。
##環境
- Windows10
- Docker for Windows:3.0.0
- node.js:v14.15.1
- npm:6.14.8
- TypeScript:4.1.3
- express:4.16.1
- MySQL:8.0.21
- eslint:7.15.0
- prettier:2.2.1
##Dockerで動作環境を構築
docker-compose.ymlはこんな感じ
docker-compose.yml
version: "3"
services:
mysql:
image: mysql:8
command: --default-authentication-plugin=mysql_native_password
env_file: ./mysql/.env
ports:
- 3306:3306
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/conf/my.cnf:/etc/my.cnf
phpmyadmin:
image: phpmyadmin/phpmyadmin
env_file: ./phpmyadmin/.env
ports:
- 8080:80
app:
image: node:12
env_file: ../api-dir/.env # Docker用のディレクトリと同列にAPIのディレクトリを配置
tty: true
ports:
- 3000:3000
volumes:
- ../api-dir:/app
working_dir: /app
command: npm start
volumes:
mysqldata:
expressの用意
まずDocker用のディレクトリと同列にAPIのディレクトリを作成する
$ cd ../
$ mkdir api-dir
$ cd api-dir
必要なモジュールをnpmでインストール(主なもの)
- express:node.jsのフレームワーク
- express-validator:バリデーション
- knex:クエリビルダー、マイグレーション、シーディング等
- winston:ログ
- jest:UnitTest
- supertest:jestでのAPIリクエストテスト用
- json2csv:csv変換
- nodemon:コード変換時の自動再起動
- ts-node:TypeScriptのトランスパイル
- eslint:静的解析
- prettier:コード整形
node.jsのクエリビルダーで良いものがないか探し回った末、knexにたどり着いた。
Laravelに慣れていたが、違和感なく使えた。
公式ドキュメントも充実しているのでオススメ。
各種設定ファイルの作成
package.json(一部抜粋)
"scripts": {
"start": "NODE_ENV=development npx nodemon --exects-node", // 起動コマンドを変更
"lint:fix": "eslint */**/*.ts *.ts *.json --fix",
"db:down": "knex --knexfile knexfile.ts migrate:rollback --all --env test",
"db:migrate": "knex --knexfile knexfile.ts migrate:latest --env test",
"db:seed": "knex --knexfile knexfile.ts seed:run --env test",
"db": "npm run db:down && npm run db:migrate && npm run db:seed",
"test": "jest --config jestconfig.json --coverage --verbose --runInBand --detectOpenHandles",
}
nodemon.json
{
"watch": ["src", "seeds", "migrations", "tests"],
"ext": "ts",
"exec": "ts-node ./bin/www"
}
jestconfig.json
{
"testMatch": ["**/?(*.)+(spec|test).+(ts|tsx|js)"],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"setupFilesAfterEnv": ["./jest.setup.js"] // タイムアウトの秒数を調整
}
jest.setup.js
jest.setTimeout(5000)
knexfile.ts
if (!require.cache['dotenv']) {
require('dotenv').config()
}
module.exports = {
development: { // 開発用
client: 'mysql',
connection: {
host: process.env.MYSQL_SERVER,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
port: process.env.MYSQL_PORT,
},
migrations: {
extension: 'ts',
tableName: 'knex_migrations',
},
},
test: { // Jest用
client: 'mysql',
connection: {
host: '127.0.0.1',
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
port: process.env.MYSQL_PORT,
},
migrations: {
extension: 'ts',
tableName: 'knex_migrations',
},
},
}
winston.ts(おまけ)
import fs from 'fs'
import path from 'path'
import moment from 'moment'
const winston = require('winston')
require('winston-daily-rotate-file')
const logDirectory = path.join(__dirname, './logs')
//指定したディレクトリが存在するか?
fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory)
const transport = new winston.transports.DailyRotateFile({
filename: path.join(logDirectory, './error-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
})
transport.on('rotate', function (oldFilename, newFilename) {
// do something fun
})
// creates a new Winston Logger
const logger = new winston.createLogger({
level: 'info',
transports: [transport],
exitOnError: false,
})
logger.errorLog = (req, res, err) => {
logger.error(
`[${moment().format('YYYY-MM-DD HH:mm:ss')}] ${req.method} - ${
res.statusCode
} - ${req.originalUrl} - ${req.ip} - ${err.message}`,
)
}
module.exports = logger
.eslintrc.json
{
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"rules": {
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-unused-vars": "off",
"prettier/prettier": [
"error",
{
"trailingComma": "all",
"endOfLine": "lf",
"semi": false,
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2
}
]
}
}
##起動
expressの起動
$ cd docker-dir
$ docker-compose up -d // コンテナ起動時にnpm startが実行され、nodemonで変更監視される
テストの際
$ cd docker-dir
$ docker-compose up mysql phpmyadmin -d
$ cd ../api-dir
$ npm run db // DBのセットアップ
$ npm run test
終わりに
今回、フロントエンドはVue.js(TypeScript)、APIはexpress(TypeScript)を採用した結果、
フロントエンジニアにもAPI製造を手伝ってもらうことができた。
言語の壁はあまり感じることなく、スムーズに作業を進められている様子だった。