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

# はじめに
この記事ではAPIのルーティングについて解説します。

# 必要となるAPI
必要となるAPIを下記に示します。

- アカウント
  - ログイン (PUT /login)
  - ログアウト (PUT /logout)
- プロジェクト
  - 一覧 (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)

# ソースコードの編集

## app/index.jsの編集
ソースコードの内容を下記に示します。

```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 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.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
}
```

変更点は下記の通りです。

```
@@ -37,6 +37,9 @@ function factory(options) {
   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')))
```

## app/routes/account.jsの編集
ソースコードの内容を下記に示します。

```js:app/routes/account.js
'use strict';

var express = require('express')

var adminHtml = express.Router({ strict: true })
var adminApi = express.Router({ strict: true })

module.exports = {
  admin: {
    html: adminHtml,
    api: adminApi,
  },
}

adminHtml.get('/login/', adminHtmlLogin())
adminHtml.get('/logout/', adminHtmlLogout())
adminApi.put('/login', adminApiLogin())
adminApi.put('/logout', adminApiLogout())

function adminHtmlLogin() {
  return function (req, res) {
    res.render('account/admin-login')
  }
}

function adminHtmlLogout() {
  return function (req, res) {
    res.render('account/admin-logout')
  }
}

function adminApiLogin() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiLogout() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}
```

変更点は下記の通りです。

```
@@ -3,15 +3,19 @@
 var express = require('express')
 
 var adminHtml = express.Router({ strict: true })
+var adminApi = express.Router({ strict: true })
 
 module.exports = {
   admin: {
     html: adminHtml,
+    api: adminApi,
   },
 }
 
 adminHtml.get('/login/', adminHtmlLogin())
 adminHtml.get('/logout/', adminHtmlLogout())
+adminApi.put('/login', adminApiLogin())
+adminApi.put('/logout', adminApiLogout())
 
 function adminHtmlLogin() {
   return function (req, res) {
@@ -24,3 +28,19 @@ function adminHtmlLogout() {
     res.render('account/admin-logout')
   }
 }
+
+function adminApiLogin() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiLogout() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
```

## app/routes/projects.jsの編集
ソースコードの内容を下記に示します。

```js:app/routes/projects.js
'use strict';

var express = require('express')

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]+)/', adminHtmlDetail())
adminHtml.get('/:id([0-9]+)/edit/', adminHtmlEdit())
adminHtml.get('/:id([0-9]+)/delete/', 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())

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 function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiPost() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiGet() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiPut() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiDelete() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}
```

変更点は下記の通りです。

```
@@ -3,10 +3,12 @@
 var express = require('express')
 
 var adminHtml = express.Router({ strict: true })
+var adminApi = express.Router({ strict: true })
 
 module.exports = {
   admin: {
     html: adminHtml,
+    api: adminApi,
   },
 }
 
@@ -16,6 +18,12 @@ adminHtml.get('/:id([0-9]+)/', adminHtmlDetail())
 adminHtml.get('/:id([0-9]+)/edit/', adminHtmlEdit())
 adminHtml.get('/:id([0-9]+)/delete/', 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())
+
 function adminHtmlList() {
   return function (req, res) {
     res.render('projects/admin-list')
@@ -45,3 +53,43 @@ function adminHtmlDelete() {
     res.render('projects/admin-delete')
   }
 }
+
+function adminApiList() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiPost() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiGet() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiPut() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiDelete() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
```

## app/routes/reports.jsの編集
ソースコードの内容を下記に示します。

```js:app/routes/reports.js
'use strict';

var express = require('express')

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]+)/', adminHtmlDetail())
adminHtml.get('/:id([0-9]+)/edit/', adminHtmlEdit())
adminHtml.get('/:id([0-9]+)/delete/', 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())

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 function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiPost() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiGet() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiPut() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}

function adminApiDelete() {
  return function (req, res, next) {
    var err = new Error('not implemented')
    err.status = 500
    next(err)
  }
}
```

変更点は下記の通りです。

```
@@ -3,10 +3,12 @@
 var express = require('express')
 
 var adminHtml = express.Router({ strict: true })
+var adminApi = express.Router({ strict: true })
 
 module.exports = {
   admin: {
     html: adminHtml,
+    api: adminApi,
   },
 }
 
@@ -16,6 +18,12 @@ adminHtml.get('/:id([0-9]+)/', adminHtmlDetail())
 adminHtml.get('/:id([0-9]+)/edit/', adminHtmlEdit())
 adminHtml.get('/:id([0-9]+)/delete/', 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())
+
 function adminHtmlList() {
   return function (req, res) {
     res.render('reports/admin-list')
@@ -45,3 +53,43 @@ function adminHtmlDelete() {
     res.render('reports/admin-delete')
   }
 }
+
+function adminApiList() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiPost() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiGet() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiPut() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
+
+function adminApiDelete() {
+  return function (req, res, next) {
+    var err = new Error('not implemented')
+    err.status = 500
+    next(err)
+  }
+}
```

# 動作確認
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

404 Not Foundが表示されなければ正常に動作してします。

# おわりに
次回はAPIの仮組みについて解説します。