去年MBaaSのバックエンドの調査で少しLoopBackを使ってからしばらく触ってませんでした。今回は一般的なREST APIを公開するプロダクション環境用のフレームワークとして調査しています。alternativesにはExpress、restify、 Hapi.js、Koa、actionhero.jsといったフレームワークがあります。LoopBackの良い点はモデルを定義するだけでREST APIを公開できる手軽さにあります。他と比べて圧倒的に書くコード数が少ないです。Hapi.jsなどの軽量フレームワークより依存するライブラリが多くサイズも大きくなりますが、メリットは大きいです。
プロジェクト
LoopBackやMongoDBのコンテナをDocker Composeで構成管理します。コードはリポジトリにpushしています。
$ cd ~/node_apps/docker-loopback
$ tree .
.
├── Dockerfile
├── README.md
└── docker-compose.yml
0 directories, 3 files
slcコマンドを非rootユーザーで実行してアプリ作成します。アプリのコードはDockerホストにマウントして編集したいので作業ユーザーを作ります。strongloopパッケージはグローバルインストールするので、nvmで仮想環境を用意してもよいですが今回はsudoをつけるようにしました。ユーザーはDockerホストの作業ユーザーと同じuidを指定します。
FROM node:0.12
MAINTAINER Masato Shimizu <ma6ato@gmail.com>
RUN apt-get update && \
apt-get install -y sudo && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN adduser --disabled-password --gecos '' --uid 1000 docker && \
adduser docker sudo && \
echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
chown -R docker:docker /app
RUN npm install -g --unsafe-perm strongloop
USER docker
ENTRYPOINT ["npm", "start"]
CMD []
Dockerイメージをビルドします。
$ docker build -t loopback .
slcコマンドからしてアプリを作成します。
$ docker-compose run --rm slc loopback
_-----_
| | .--------------------------.
|--(o)--| | Let's create a LoopBack |
`---------´ | application! |
( _´U`_ ) '--------------------------'
/___A___\
| ~ |
__'.___.'__
´ ` |° ´ Y `
? What's the name of your application? spike-todo
create spike-todo/
info change the working directory to spike-todo
...
Next steps:
Change directory to your app
$ cd spike-todo
Create a model in your app
$ slc loopback:model
Optional: Enable StrongOps monitoring
$ slc strongops
Run the app
$ slc run .
Removing dockerloopback_slc_run_1...
アプリの設定
slcコマンドで作成したアプリ名をdocker-compose.ymlのworking_dir
に指定します。コンテナの作業ディレクトリは/app
なので上記のアプリの場合は、/app/spike-todo
になります。
MongoDBのコンテナはサービスをmongo
とつけてserver
サービスからlinkします。server
サービスの/etc/hosts
にmongo
の名前で自動的に登録されるためコンテナ間の名前解決が簡単になります。
slc: &defaults
image: loopback
volumes:
- .:/app
working_dir: /app/spike-todo
links:
- mongo
entrypoint: ["slc"]
server:
<<: *defaults
entrypoint: ["slc","run"]
ports:
- "3000:3000"
npm:
<<: *defaults
entrypoint: ["npm"]
mongo:
image: mongo
volumes:
- ./mongo:/data/db
MondoDBサービス
docker-compose.ymlに定義したnpmサービスを使ってMongoDBコネクタのloopback-connector-mongodbパッケージをインストールします。--save
フラグをつけるとpackage.json
に依存関係を追記してくれます。
$ docker-compose run --rm npm install loopback-connector-mongodb --save
...
loopback-connector-mongodb@1.11.0 node_modules/loopback-connector-mongodb
├── debug@2.2.0 (ms@0.7.1)
├── loopback-connector@2.2.1
├── async@1.2.1
└── mongodb@2.0.33 (readable-stream@1.0.31, mongodb-core@1.1.32)
Removing dockerloopback_npm_run_1...
slcコマンドで作成したアプリのspike-todo
ディレクトリにあるデータベース接続の設定ファイルを編集します。dbディレクティブはデフォルトで定義されています。mongodb_devという名前でディレクティブを作成します。
"db": {
"name": "db",
"connector": "memory"
},
"mongodb_dev": {
"host": "mongo",
"port": 27017,
"database": "devDB",
"username": "devUser",
"password": "",
"name": "",
"connector": "mongodb"
}
}
モデルの作成
MongoDBコネクタをインストールして、datasources.jsonにエントリを追加したのでslcコマンドのdata-sourceの選択にmongodbが表示されます。ベースクラスはPersistedModelを選択してデータベースのCRUD操作ができるようにします。続くモデルのプロパティ設定はToDoアプリっぽくstringのtitie
とbooleanのcreated
の2つを追加します。
$ docker-compose run --rm slc loopback:model
? Enter the model name: Todo
? Select the data-source to attach Todo to: mongodb_dev (mongodb)
? Select model's base class: PersistedModel
? Expose Todo via the REST API? Yes
? Custom plural form (used to build REST URL): Todos
Let's add some Todo properties now.
Enter an empty property name when done.
? Property name: title
invoke loopback:property
? Property type: string
? Required? Yes
Let's add another Todo property.
Enter an empty property name when done.
? Property name: completed
invoke loopback:property
? Property type: boolean
? Required? No
Let's add another Todo property.
Enter an empty property name when done.
? Property name:
Removing dockerloopback_slc_run_1...
サーバーの起動
docker-compose up
からサービスを起動するとdocker-compose.ymlに定義したホストへのポートマップが有効になります。Dockerホストの外側からはDockerホストのIPアドレスを指定してhttp://xxx..xxx.xxx:3000/explorer
をブラウザで開きます。
$ docker-compose up server
Creating dockerloopback_mongo_1...
Creating dockerloopback_server_1...
Attaching to dockerloopback_server_1
server_1 | INFO strong-agent v1.6.0 profiling app 'spike-todo' pid '1'
server_1 | INFO strong-agent[1] started profiling agent
server_1 | INFO supervisor reporting metrics to `internal:`
server_1 | supervisor running without clustering (unsupervised)
server_1 | INFO strong-agent not profiling, agent metrics requires a valid license.
server_1 | Please contact sales@strongloop.com for assistance.
server_1 | Browse your REST API at http://0.0.0.0:3000/explorer
server_1 | Web server listening at: http://0.0.0.0:3000/
Swagger UIとREST APIの実行
3000ポートを開くとLoopBackサーバーのuptimeが表示されます。
$ curl http://localhost:3000
{"started":"2015-06-09T02:31:05.307Z","uptime":37.229}
DBの設定ファイルを定義してslcコマンドからモデルを作成しただけで、ここまでコードは1行も書いていませんがこれだけでSwagger UIとREST APIが使えるようになります。初めてRailsを触ったときのようなちょっとした感動があります。Swagger UIの画面上からAPIの実行をすることもできます。
今回はcurlでREST APIを実行します。JSON形式でPOSTしてレコードを作成します。
$ curl -X POST -H "Content-Type:application/json" \
-d '{"title": "サンプル", "completed": false}' \
http://localhost:3000/api/ToDos
{"title":"サンプル","completed":false,"id":"55766dcba3148801001e6e42"}
今回はクエリ文字列に日本語を使っているので、素ではURIエンコードは書けませんがGETメソッドを使いフィルタしたレコードを取得することができます。
$ curl -X GET localhost:3000/api/Todos?filter=%7B%22where%22%3A%7B%22title%22%3A%22%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%22%7D%7D
[{"title":"サンプル","completed":false,"id":"55766dcba3148801001e6e42"}]
上記のクエリ文字列は以下のWhere句をURIエンコードしたものです。Swagger UIの場合クエリをフィールドに入力すれば自動的にURIエンコードしたURLを生成して実行してくれます。
{"where":{"title":"サンプル"}}
念のため起動中のMongoDBコンテナに入りレコードが追加されたことを確認します。
$ docker exec -it dockerloopback_mongo_1 mongo devDB
MongoDB shell version: 3.0.3
connecting to: devDB
> db.Todo.find({title:"サンプル"});
{ "_id" : ObjectId("55766dcba3148801001e6e42"), "title" : "サンプル", "completed" : false }