はじめに
この記事ではAPIの仮組みについて解説します。
仮組みするAPI
仮組みするAPIを下記に示します。
- プロジェクト
- 一覧 (GET /projects)
- 追加 (POST /projects)
- 詳細 (GET /projects/:id)
- 更新 (PUT /projects/:id)
- 削除 (DELETE /projects/:id)
- レポート
- 一覧 (GET /reports)
- 追加 (POST /reports)
- 詳細 (GET /reports/:id)
- 更新 (PUT /reports/:id)
- 削除 (DELETE /reports/:id)
scaffoldクラスの作成
Scaffoldクラスを記述するファイルと、そのファイルを格納するディレクトリを作成します。
mkdir app/utils
touch app/utils/scaffold.js
scaffold.jsの内容を下記に示します。
app/utils/scaffold.js
'use strict';
module.exports = factory
module.exports.Scaffold = Scaffold
function factory(model) {
return new Scaffold({ model: model })
}
function Scaffold(options) {
this.model = options.model
}
Scaffold.prototype.findById = function () {
var self = this
return function (req, res, next) {
self.model.findOne({ where: { id: req.params.id } })
.then(function (resource) {
if (resource === null) {
var err = new Error('Not Found')
err.status = 404
return next(err)
}
req.resource = resource
res.locals.resource = resource
next()
})
}
}
Scaffold.prototype.list = function () {
var self = this
return function (req, res, next) {
return self.model.findAll()
.then(function (resources) {
res.send(resources)
})
}
}
Scaffold.prototype.post = function () {
var self = this
return function (req, res, next) {
return self.model.create(req.body)
.then(function () {
res.status(201).end()
})
}
}
Scaffold.prototype.get = function () {
return function (req, res, next) {
res.send(req.resource)
}
}
Scaffold.prototype.put = function () {
return function (req, res, next) {
Object.keys(req.body).forEach(function (key) {
req.resource[key] = req.body[key]
})
req.resource.save()
.then(function () {
res.status(204).end()
})
}
}
Scaffold.prototype.delete = function () {
return function (req, res, next) {
req.resource.destroy()
.then(function () {
res.status(204).end()
})
}
}
ルータの編集
app/routes/projects.jsの編集
変更後の内容を下記に示します。
app/routes/projects.js
'use strict';
var express = require('express')
var models = require('../models')
var scaffold = require('../utils/scaffold')(models.Project)
var adminHtml = express.Router({ strict: true })
var adminApi = express.Router({ strict: true })
module.exports = {
admin: {
html: adminHtml,
api: adminApi,
},
}
adminHtml.get('/', adminHtmlList())
adminHtml.get('/add/', adminHtmlAdd())
adminHtml.get('/:id([0-9]+)/', adminFindById(), adminHtmlDetail())
adminHtml.get('/:id([0-9]+)/edit/', adminFindById(), adminHtmlEdit())
adminHtml.get('/:id([0-9]+)/delete/', adminFindById(), adminHtmlDelete())
adminApi.get('/', adminApiList())
adminApi.post('/', adminApiPost())
adminApi.get('/:id([0-9]+)', adminFindById(), adminApiGet())
adminApi.put('/:id([0-9]+)', adminFindById(), adminApiPut())
adminApi.delete('/:id([0-9]+)', adminFindById(), adminApiDelete())
function adminHtmlList() {
return function (req, res) {
res.render('projects/admin-list')
}
}
function adminHtmlAdd() {
return function (req, res) {
res.render('projects/admin-add')
}
}
function adminHtmlDetail() {
return function (req, res) {
res.render('projects/admin-detail')
}
}
function adminHtmlEdit() {
return function (req, res) {
res.render('projects/admin-edit')
}
}
function adminHtmlDelete() {
return function (req, res) {
res.render('projects/admin-delete')
}
}
function adminApiList() {
return scaffold.list()
}
function adminApiPost() {
return scaffold.post()
}
function adminApiGet() {
return scaffold.get()
}
function adminApiPut() {
return scaffold.put()
}
function adminApiDelete() {
return scaffold.delete()
}
function adminFindById() {
return scaffold.findById()
}
変更点は下記の通りです。
--- a/app/routes/projects.js
+++ b/app/routes/projects.js
@@ -1,6 +1,8 @@
'use strict';
var express = require('express')
+var models = require('../models')
+var scaffold = require('../utils/scaffold')(models.Project)
var adminHtml = express.Router({ strict: true })
var adminApi = express.Router({ strict: true })
@@ -14,15 +16,15 @@ module.exports = {
adminHtml.get('/', adminHtmlList())
adminHtml.get('/add/', adminHtmlAdd())
-adminHtml.get('/:id([0-9]+)/', adminHtmlDetail())
-adminHtml.get('/:id([0-9]+)/edit/', adminHtmlEdit())
-adminHtml.get('/:id([0-9]+)/delete/', adminHtmlDelete())
+adminHtml.get('/:id([0-9]+)/', adminFindById(), adminHtmlDetail())
+adminHtml.get('/:id([0-9]+)/edit/', adminFindById(), adminHtmlEdit())
+adminHtml.get('/:id([0-9]+)/delete/', adminFindById(), adminHtmlDelete())
adminApi.get('/', adminApiList())
adminApi.post('/', adminApiPost())
-adminApi.get('/:id([0-9]+)', adminApiGet())
-adminApi.put('/:id([0-9]+)', adminApiPut())
-adminApi.delete('/:id([0-9]+)', adminApiDelete())
+adminApi.get('/:id([0-9]+)', adminFindById(), adminApiGet())
+adminApi.put('/:id([0-9]+)', adminFindById(), adminApiPut())
+adminApi.delete('/:id([0-9]+)', adminFindById(), adminApiDelete())
function adminHtmlList() {
return function (req, res) {
@@ -55,41 +57,25 @@ function adminHtmlDelete() {
}
function adminApiList() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.list()
}
function adminApiPost() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.post()
}
function adminApiGet() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.get()
}
function adminApiPut() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.put()
}
function adminApiDelete() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.delete()
+}
+
+function adminFindById() {
+ return scaffold.findById()
}
app/routes/reports.jsの編集
変更後の内容を下記に示します。
app/routes/reports.js
'use strict';
var express = require('express')
var models = require('../models')
var scaffold = require('../utils/scaffold')(models.Report)
var adminHtml = express.Router({ strict: true })
var adminApi = express.Router({ strict: true })
module.exports = {
admin: {
html: adminHtml,
api: adminApi,
},
}
adminHtml.get('/', adminHtmlList())
adminHtml.get('/add/', adminHtmlAdd())
adminHtml.get('/:id([0-9]+)/', adminFindById(), adminHtmlDetail())
adminHtml.get('/:id([0-9]+)/edit/', adminFindById(), adminHtmlEdit())
adminHtml.get('/:id([0-9]+)/delete/', adminFindById(), adminHtmlDelete())
adminApi.get('/', adminApiList())
adminApi.post('/', adminApiPost())
adminApi.get('/:id([0-9]+)', adminFindById(), adminApiGet())
adminApi.put('/:id([0-9]+)', adminFindById(), adminApiPut())
adminApi.delete('/:id([0-9]+)', adminFindById(), adminApiDelete())
function adminHtmlList() {
return function (req, res) {
res.render('reports/admin-list')
}
}
function adminHtmlAdd() {
return function (req, res) {
res.render('reports/admin-add')
}
}
function adminHtmlDetail() {
return function (req, res) {
res.render('reports/admin-detail')
}
}
function adminHtmlEdit() {
return function (req, res) {
res.render('reports/admin-edit')
}
}
function adminHtmlDelete() {
return function (req, res) {
res.render('reports/admin-delete')
}
}
function adminApiList() {
return scaffold.list()
}
function adminApiPost() {
return scaffold.post()
}
function adminApiGet() {
return scaffold.get()
}
function adminApiPut() {
return scaffold.put()
}
function adminApiDelete() {
return scaffold.delete()
}
function adminFindById() {
return scaffold.findById()
}
変更点は下記の通りです。
@@ -1,6 +1,8 @@
'use strict';
var express = require('express')
+var models = require('../models')
+var scaffold = require('../utils/scaffold')(models.Report)
var adminHtml = express.Router({ strict: true })
var adminApi = express.Router({ strict: true })
@@ -14,15 +16,15 @@ module.exports = {
adminHtml.get('/', adminHtmlList())
adminHtml.get('/add/', adminHtmlAdd())
-adminHtml.get('/:id([0-9]+)/', adminHtmlDetail())
-adminHtml.get('/:id([0-9]+)/edit/', adminHtmlEdit())
-adminHtml.get('/:id([0-9]+)/delete/', adminHtmlDelete())
+adminHtml.get('/:id([0-9]+)/', adminFindById(), adminHtmlDetail())
+adminHtml.get('/:id([0-9]+)/edit/', adminFindById(), adminHtmlEdit())
+adminHtml.get('/:id([0-9]+)/delete/', adminFindById(), adminHtmlDelete())
adminApi.get('/', adminApiList())
adminApi.post('/', adminApiPost())
-adminApi.get('/:id([0-9]+)', adminApiGet())
-adminApi.put('/:id([0-9]+)', adminApiPut())
-adminApi.delete('/:id([0-9]+)', adminApiDelete())
+adminApi.get('/:id([0-9]+)', adminFindById(), adminApiGet())
+adminApi.put('/:id([0-9]+)', adminFindById(), adminApiPut())
+adminApi.delete('/:id([0-9]+)', adminFindById(), adminApiDelete())
function adminHtmlList() {
return function (req, res) {
@@ -55,41 +57,25 @@ function adminHtmlDelete() {
}
function adminApiList() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.list()
}
function adminApiPost() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.post()
}
function adminApiGet() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.get()
}
function adminApiPut() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.put()
}
function adminApiDelete() {
- return function (req, res, next) {
- var err = new Error('not implemented')
- err.status = 500
- next(err)
- }
+ return scaffold.delete()
+}
+
+function adminFindById() {
+ return scaffold.findById()
}
body-parserのインストール
コマンドを下記に示します。
npm install --save body-parser
app/index.jsの編集
変更後の内容を下記に示します。
app/index.js
'use strict';
var env = process.env.NODE_ENV || 'development'
var url = require('url')
var path = require('path')
var morgan = require('morgan')
var express = require('express')
var bodyParser = require('body-parser')
var serveStatic = require('serve-static')
var proxyMiddleware = require('proxy-middleware')
var pages = require('./routes/pages')
var account = require('./routes/account')
var reports = require('./routes/reports')
var projects = require('./routes/projects')
var siteConfig = require('./config/site')[env]
module.exports = factory
function factory(options) {
options = options || {}
var staticPath = options.staticPath
var app = express()
app.set('strict routing', true)
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'jade')
app.locals.baseUrl = siteConfig.baseUrl
app.use(morgan('dev'))
app.use(bodyParser.json())
app.get('/', pages.anonymous.home())
app.get('/admin/', pages.admin.dashboard())
app.use('/admin/account', account.admin.html)
app.use('/admin/reports', reports.admin.html)
app.use('/admin/projects', projects.admin.html)
app.use('/admin/api/v1/account', account.admin.api)
app.use('/admin/api/v1/reports', reports.admin.api)
app.use('/admin/api/v1/projects', projects.admin.api)
if (env === 'development') {
app.use('/static', proxyMiddleware(url.parse('http://127.0.0.1:8080')))
} else {
app.use('/static', serveStatic(staticPath))
}
return app
}
変更点は下記の通りです。
@@ -6,6 +6,7 @@ var url = require('url')
var path = require('path')
var morgan = require('morgan')
var express = require('express')
+var bodyParser = require('body-parser')
var serveStatic = require('serve-static')
var proxyMiddleware = require('proxy-middleware')
@@ -31,6 +32,7 @@ function factory(options) {
app.locals.baseUrl = siteConfig.baseUrl
app.use(morgan('dev'))
+ app.use(bodyParser.json())
app.get('/', pages.anonymous.home())
app.get('/admin/', pages.admin.dashboard())
diff --git a/app/package.json b/app/package.json
index 764b260..3ee5be5 100644
動作確認
appとstaticを起動して下記のURLなどにアクセスします。
- http://127.0.0.1:3000/admin/api/v1/projects
- http://127.0.0.1:3000/admin/api/v1/projects/1
- http://127.0.0.1:3000/admin/api/v1/reports
- http://127.0.0.1:3000/admin/api/v1/reports/1
データがJSON形式で表示されれば正常に動作しています。
おわりに
次回はstaticのモデル作成について解説します。