GWが終わってから、なにがとは決めていないけれど Webサービス作るか…… という気持ちがふつふつと沸いてきた。
Node.js はやりたいがサーバーのお守りはやりたくない
なにかしらのWebアプリを作ってみたいと以前から考えていましたが、実行するサーバーなどインフラのメンテナンスのことを考えると億劫で手が出ませんでした。
AWS EC2でサーバー立てて forever.js でNode.jsをデーモン化したりするのは避けたいところです……と考えていたのが2年くらい前のこと。いままで何度かNode.jsに手を出そうとして、まとまった時間が取れなかったりして挫折していました。一番のネックとなっていたのは、やはり書いたプログラムの実行環境が用意できない(あるいは用意するのが面倒くさい)ところでした。
この酢ろぐが pikiwikiからWordPress、WordPressからはてなブログ、と引っ越してきたのもサーバーのお世話をしたくないからです。知り合いから「ブログ落ちてるよー」「再起動したよー」というやりとりはもうしたくないのです。
Firebase Hosting と Cloud Functions でインフラのことを考えなくてよくなった
つい先日 Firebase Hosting と Cloud Functions を使って、動的コンテンツの配信ができることをたまたま知りました。この方法がうまくいけば、前述したインフラ関係を気にすることなく firebase deploy
だけで書いたアプリをデプロイすることが可能です。
このドキュメントは TypeScript に対応したものではなかったので、試行錯誤しながら組み立てていくことになりました。
import * as functions from 'firebase-functions'; import * as express from 'express'; const app = express(); app.get('/', (req, res) => { const date = new Date(); const hours = (date.getHours() % 12) + 1; // London is UTC + 1hr; res.send(` <!doctype html> <head> <title>Time</title> <link rel="stylesheet" href="/style.css"> <script src="/script.js"></script> </head> <body> <p>In London, the clock strikes: <span id="bongs">${'BONG '.repeat(hours)}</span></p> <button onClick="refresh(this)">Refresh</button> </body> </html>`); }); app.get('/api', (req, res) => { const date = new Date(); const hours = (date.getHours() % 12) + 1; // London is UTC + 1hr; res.json({bongs: 'BONG '.repeat(hours)}); }); exports.app = functions.https.onRequest(app);
上記の index.ts
をデプロイして、サンプルが実行されたときにはオォーとなりました。
さらにexpress-generator
を使ってアプリの雛形(スケルトン)を生成する方法が Qiitaで 紹介されていました。
最初は文章ばかりで何を指しているのかわかりませんでしたが、前述の公式ドキュメントを一通り試してから読むと理解できるようになっていました。Expressの雛形を functions にデプロイしてみました。
うまくいってる!!!
以上で、 Firebase Hosting と Cloud Functions を基盤にして、Express.jsを使ったWebアプリの開発が可能な状態となりました。
Express.jsを使って Webアプリを作りたい
Node.js開発では Express を使うのが当たり前なのか、「Express.jsを使ってWebアプリを作ろう!」的な書籍を発見することができませんでした。比較的、最近執筆された「JavaScriptでのWeb開発 ~ Node.js + Express + MongoDB + ReactでWebアプリを開発しよう 〜 その1 〜(改訂版三版)」を購入して、読み進めることにしました。
JavaScriptでのWeb開発 ~ Node.js + Express + MongoDB + ReactでWebアプリを開発しよう 〜 その1 〜(改訂版三版)
- 作者:ナカノヒトシ
- 発売日: 2018/07/28
- メディア: Kindle版
Express単体では HTTPリクエストを受けてレスポンスを返す機能しかなく、ミドルウェアを追加して使うことがわかりました。
const app = express(); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug');
レンダリングエンジンには何を使うべきか?
pug(jade) を使っておけば安牌? htmlエディタでもインテリセンスの効く ejs の方がよいかのか?
とりあえず pug を使うことにした。
ExpressとFirestoreを連携させる
本書では MongoDB を使っていましたが、前述の通りインフラに触れたくないと考えています。Cloud Functionを選択した理由と同じで、ここではFirestoreを使うことにする。MongoDBの記述となんとなくで Firestoreに読み替えて実装していきました。
過去に調べていた「Cloud Functions for FirebaseでFirestoreのデータを返す - 酢ろぐ!」を参考にして、firebase-admin
を使うことになるので、firebase-adminsdk.json
をダウンロードしてきました。
export GOOGLE_APPLICATION_CREDENTIALS="/Users/ch3cooh/firebase/***/firebase-adminsdk.json" firebase serve --only functions,hosting
メッセージ投稿画面( messages.pug
と messages.js
)は雑にこんな感じです。
doctype html html(lang="ja") head meta(charset="utf8") body h1 メッセージ h2 メッセージを保存 form(action="messages" method="POST") input(type="text" name="username" placeholder="名前") textarea(name="message" placeholder="メッセージ") button(type="submit") 送信 h2 メッセージ一覧 section each msg in messages div h3 #{msg.name} p #{msg.message}
const firebaseAdmin = require('firebase-admin'); var express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { const db = firebaseAdmin.firestore(); const messages = db.collection('messages'); messages.get() .then(snapshot => { let list = []; snapshot.forEach(doc => list.push(doc.data())); res.render('messages', { messages: list }); }) }); router.post('/', function(req, res, next) { const db = firebaseAdmin.firestore(); const messages = db.collection('messages'); messages.add({ name: req.body.username, message: req.body.message, }) .then(ref => { res.redirect('/messages') }); }); module.exports = router;
実行する。
読んでいてよくイメージができなかったところ
本書はターゲットが Webアプリ開発者なのか Rubyを引き合いに出すことがあり、RoRをよく知らない僕としては「RubyのSinatra」「Ruby on RailsのようなDBのモデル設定」とはどんなものなのか?とちょくちょく調べ物が発生しました。
RubyのSinatraに似たRESTful設計になっており、Node.jsの薄いラッパーとしてシンプルな構成になっています。
Express自体は、WebサーバーとしてHTTPリクエストを取り扱う最低限の機能しかありません。
Ruby on RailsのようなDBのモデル設定や認証機能などは一切備わっておらず、... (略)
Nakano Hitoshi. JavaScript (Japanese Edition) (Kindle の位置No.498-499). Kindle 版.