やること
ExpressとMongoDBで作成するアプリケーションにRedisを導入します。
作るもの
図書館を模したアプリケーションを作成します。
データ -> 書籍 データへのリクエスト -> 書籍が作成されたり、取り出されたり データストレージ(MongoDB)-> 書棚 キャッシュ(Redis) -> 共用のデスク
のような模し方をしています。
要は、いちいち書棚まで本を探しに行くのは面倒なので、よく使う本は共用のデスクに置いておきたいよねという話。
この記事はCaching a MongoDB Database with Redisを参考にしています。
前提
Node.js, npm, MongoDB, Redisのセットアップは済んでいる前提です。
Mongo起動
起動しておきます。
$ mongod --dbpath=/data --port 27017
アプリケーション起動スクリプト
Nodeアプリケーションの作成。
$ npm init -y $ npm i --save express mongodb redis body-parser $ touch index.js
Mongoに接続して、アプリを立ち上げる処理。
// index.js const express = require('express') const MongoClient = require('mongodb').MongoClient const app = express() const bodyParser = require('body-parser') const mongoUrl = 'mongodb://localhost:27017/textmonkey' app.use(bodyParser.urlencoded({ extended: false })) app.use(bodyParser.json()) MongoClient.connect(mongoUrl, (err, db) => { if(err) { throw 'Error connecting to database - ' + err } app.listen(8000, () => { console.log('Listening on port 8000') }) })
基本的なエンドポイント作成
ExpressとMongoDBを利用した基本的なエンドポイントを作成。
$ touch access.js
// access.js // 書籍の保存 module.exports.saveBook = (db, title, author, text, callback) => { db.collection('text').save({ title: title, author: author, text: text }, callback) } // 書籍の取得 module.exports.findBookByTitle = (db, title, callback) => { db.collection('text').findOne({ title }, (err, doc) => { callback((err || !doc) ? null : doc.text) }) }
index.jsに追記
// index.js // ... 略 // new const access = require('./access.js') MongoClient.connect(mongoUrl, (err, db) => { if(err) { throw 'Error connecting to database - ' + err } // new app.post('/book', (req, res) => { if (!req.body.title || !req.body.author) { res.status(400).send("Please send a title and an author for the book") } else if (!req.body.text) { res.status(400).send("Please send some text for the book") } else { access.saveBook(db, req.body.title, req.body.author, req.body.text, (err) => { if (err) { res.status(500).send("Server error") } else { res.status(201).send("Saved") } }) } }) // new app.get('/book/:title', (req, res) => { if (!req.params['title']) { res.status(400).send("Please send a proper title") } else { access.findBookByTitle(db, req.params['title'], (text) => { if (!text) { res.status(500).send("Server error") } else { res.status(200).send(text) } }) } }) // ...略 })
これで基本的な書籍の保存と取得が実装できました。
キャッシュ機能を追加
Redisサーバを立ち上げておきます。
$ redis-server
redisに必要なクライアントを呼び出し。
// index.js // new const redisClient = require('redis').createClient const redis = redisClient(6379, 'localhost')
findBookByTitleCached関数を追加
access.jsにて、関数を追加。
// access.js module.exports.findBookByTitleCached = (db, redis, title, callback) => { redis.get(title, (err, reply) => { if (err) { callback(null) } else if (reply) { // キャッシュにて書籍を発見 callback(JSON.parse(reply)) } else { // キャッシュに書籍が無かったのでDBにクエリ発行 db.collection('text').findOne({ title }, (err, doc) => { if (err || !doc) { // DBにも書籍が無かった場合 callback(null) } else { // DBにて書籍を発見 // キャッシュにセーブして、クライアントにリターンする redis.set(title, JSON.stringify(doc), () => { callback(doc) }) } }) } }) }
index.jsにて書籍取得にfindBookByTitleCached
を使うように変更。
// index.js app.get('/book/:title', (req, res) => { if (!req.params['title']) { res.status(400).send("Please send a proper title") } else { access.findBookByTitleCached(db, redis, req.params['title'], (text) => { if (!text) { res.status(500).send("Server error") } else { res.status(200).send(text) } }) } })
これで、redisにデータがあればそれを返すことができます。書棚までいちいち見に行かずとも、共用のデスクで見つけることができた的な。
アプリケーションを起動
$ node index.js
Mongo, Redisサーバ, Expressが起動しており、問題がなければlocalhostでアプリケーションが立ち上がるはずです。
PostManのようなHTTPクライアントで書籍のPOSTやGETを試してみると、アプリケーションが動いていることが確認できます。
Codes on Github
実際のコードを見たい方はこちらからどうぞ。
https://github.com/chuck0523/chuck-personal-repo/tree/master/langs/redis/mongo-node-redis