LoginSignup
4
4

More than 5 years have passed since last update.

Node.js で小規模なアプリケーションを開発する方法 その12

Posted at

はじめに

この記事では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などにアクセスします。

データがJSON形式で表示されれば正常に動作しています。

おわりに

次回はstaticのモデル作成について解説します。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4