Qiita::Markdownを使ってMarkdown APIサーバを書いた

Markdownには様々な方言があり、記法の解釈にも微妙に差があったりする。 一つの言語で開発しているのであれば、同じライブラリを使えば問題にならないが、それぞれ別の言語で書いた複数のアプリケーションで全く同じレンダリング結果を得るのは難しい。 一番ベーシックな記法だけに絞れば差分を減らすことは可能だが、 Fenced code blockTable 記法はあるとないとでは大分使い勝手が変わるので出来れば使いたい。

と、実際に困ることがあったので、どの言語で開発しても統一した記法が使えるよう、Markdownをレンダリングするサーバを書いた。

https://github.com/nkwhr/qiita-markdown-server

デモ: https://qiita-markdown.herokuapp.com/

書いたと言ってもPOSTされた内容をQiita::Markdownに投げて結果を返しているだけなので大したことはしていない。 むしろQiita::Markdownがすごい。デフォルトでサニタイズシンタックスハイライトもしてくれるし絵文字まで使える。

APIの仕様は基本的にGitHubMarkdown APIに合わせているので、GitHubのMarkdown APIを使ったことがある人は違和感なく使うことが出来ると思う。 READMEに書いてある通り、提供しているAPI/markdown/markdown/rawだけ。

使い方

/markdowntextフィールドにMarkdownテキストを入れたJSONをPOSTすれば変換された結果がtext/htmlで戻ってくる。

$ curl -X POST -H 'Content-Type: application/json' -d '
{
  "text": "Hello my twitter ID is @_nao8 :smile:",
  "options": {
    "asset_root": "http://localhost:8080/images",
    "base_url": "https://twitter.com"
  }
}' http://localhost:8080/markdown
< HTTP/1.1 200 OK
< Content-Type: text/html;charset=utf-8
< Content-Length: 262
< Access-Control-Allow-Origin: *

<p>Hello my twitter ID is <a href="https://twitter.com/_nao8" class="user-mention" title="_nao8">@_nao8</a> <img class="emoji" title=":smile:" alt=":smile:" src="http://localhost:8080/images/emoji/unicode/1f604.png" height="20" width="20" align="absmiddle"></p>

(GitHubのMarkdown APIはなぜか/markdown/rawの方しかContent-Typeをチェックしていなかったが、片方だけ何でもいいというのは気持ちが悪かったので、/markdownの方もチェックするようにしている。)

オプション( qiita-markdownのcontext )は起動時に config/qiita-markdown.yml で指定するか、上の例のようにリクエスト毎に指定することができる。(ただし/markdownのみ) この辺もQiita::Markdown.new#callの両方で受け取ってくれる仕様だったので簡単に実装できた。ありがたい。

/markdown/rawの方はもっとシンプルで、変換したいテキストをtext/plainまたはtext/x-markdownでPOSTするだけ。

$ curl -X POST -H 'Content-Type: text/plain' -d '
# Task List
- [x] task1
- [ ] task2
' http://localhost:8080/markdown/raw
< HTTP/1.1 200 OK
< Content-Type: text/html;charset=utf-8
< Content-Length: 342
< Access-Control-Allow-Origin: *

<h1>
<span id="task-list" class="fragment"></span><a href="#task-list"><i class="fa fa-link"></i></a>Task List</h1>

<ul>
<li class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled>task1</li>
<li class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled>task2</li>
</ul>

Dockerで動かす

コンテナ一つ手元に転がしておけば開発が捗るかなと思い、Docker化した。

nkwhr/qiita-markdown-server

実行例

$ docker run \
    --rm \
    --env WEB_CONCURRENCY=$(nproc) \
    --env ASSET_ROOT=http://example.com/images \
    -p 8080:8080 \
    nkwhr/qiita-markdown-server

highlight.jsなどシンタックスハイライトに対応したJS/CSSや、絵文字の画像データを一緒にパッケージできれば、コンテナを一台動かして、そこを参照するようにアプリケーションを書くことができて楽なのだが、調べた限りコンテナイメージ内に絵文字の画像データ含めるのは、ライセンス的にアウトなようなので諦めた。

Herokuで動かす

前々からHeroku Buttonを使ってみたかったというのもあり、2クリックでHerokuへデプロイ出来るようにした。

Deploy to Heroku

buildpack周りは以下のエントリを参考にさせて頂いた。

qiita-markdownを含んだGemfileをHerokuにpushするには

多分何も見ずにデプロイしていたらハマっていたと思う。ありがとうございます。