DenoでサクッとWebアプリケーションを立ち上げる

こちらはDeno Advent Calendar一日目の記事です。

Denoが登場して早一年半ともなりますが、まだ触ったことの無い方も多いのではないでしょうか？
最初の一歩で何を作るか、と言うのはいつも悩ましいですが、
やっぱりここはみんな大好きWebアプリケーションかな？と言う事で、簡単なWebアプリケーションをDenoで作って動かす方法を紹介します。

## 今回使うmodule

* [dinatra](https://github.com/syumai/dinatra): Small web app framework for Deno
* [dejs](https://github.com/syumai/dejs): EJS engine for Deno

の2つを使います。

※なお、dinatraは内部的にdeno_stdのhttp serverを使用していますが、まだ不安定なため、より安定したWebサーバーの構築をされたい方にはkeroxpさんの[servest](https://servestjs.org/)をオススメします。

## dinatraを使ったWebアプリの最小構成

まず、[Denoをインストール済み](https://deno.land/)と言う前提で話を進めます。

dinatraアプリは下記の内容を保存したtsファイルをDenoで実行することで動作します。

```ts
import { app, get } from 'https://denopkg.com/syumai/dinatra/mod.ts';

app(get('/hello', () => 'hello'));
```

dinatraのハンドラーは、登録したいHTTPメソッド名の関数に、パスと、返すコンテンツ（string | Deno.Reader）を渡してやることで登録が出来ます。

試しに、この内容を `example.ts` として保存して実行してみましょう。
なお、Denoはファイルアクセスや、ネットワークへのアクセスに権限の許可を必要とするので、 `--allow-net` を指定している点にご注意ください。

```sh
$ deno --allow-net example.ts
listening on http://0.0.0.0:8080/
```

次に、今起動したDenoのHTTPサーバーに対してブラウザやcurlでアクセスしてみましょう。

```sh
$ curl localhost:8080/hello
hello
```

ちゃんと `hello` と言う文字列が表示されるのが確認できましたね！
dinatraの基本的な使い方はたったこれだけです。

postやput, patch, deleteメソッドのハンドラを登録したければ、それらをimportしてapp関数に渡せばOKです。
（※deleteについては言語の制約上、 `del` と言う名前になっています）
その他の詳しい使い方については[公式のREADME](https://github.com/syumai/dinatra#dinatra)を参照ください。

## dinatraでPOSTの送信を受け取る

次に、Formで送信した内容をDenoのサーバー側で取得するサンプルを作ってみます。
受け付けたいのはPOSTリクエストです。
なので、POSTハンドラ登録用の関数をimportします。
また、ルート (localhost:8080) へのアクセスがされたら、テンプレートのHTMLを表示するようにします。
このHTMLには、フォームとメッセージ表示領域を用意して、フォームが送信されたらメッセージを表示するようにします。

イメージはこちらです。

![Screenshot from 2019-12-01 02-12-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45427/afd380b8-68a5-e44f-2afa-032cd4f42a01.png)

今回は、EJSで作ったViewのファイルと、サーバーとを分離してみます。

Viewは次のような内容になっています。

```erb:index.ejs
<html>
  <head>
    <meta charset="utf-8">
    <title>Deno form example</title>
  </head>
  <body>
    <form action="/posts" method="POST">
      <label>メッセージを入れてね！</label><br>
      <input type="text" name="message" placeholder="message" /><br>
      <button>送信</button>
    </form>
    <div>受け取ったメッセージ: <%= message %></div>
  </body>
</html>
```

サーバーは次のような内容になっています。

```ts:mod.ts
import { app, get, post } from 'https://denopkg.com/syumai/dinatra/mod.ts';
import { renderFile } from 'https://deno.land/x/dejs@0.3.3/mod.ts';

const templatePath = `${Deno.cwd()}/index.ejs`;

app(
  get('/', async () => await renderFile(templatePath, { message: '' })),
  post(
    '/posts',
    async ({ params: { message } }) =>
      await renderFile(templatePath, { message })
  )
);
```

dinatraでは、ハンドラに登録するレスポンスの形式として、Deno.Readerを返すasync functionをサポートしています。
dejsのrenderFileは、Deno.Readerを返すasync functionなので、これを使用してViewの描画を行っています。

では、上記の2ファイルをそれぞれ `index.ejs`, `mod.ts` として保存し、次のように実行してみましょう。

```sh
$ deno --allow-net --allow-read mod.ts
```

この状態で `localhost:8080` にアクセスすると、フォームが動作することが確認出来ると思います！

![Screenshot from 2019-12-01 02-21-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45427/6f0cb924-63b1-7c3c-09d0-c54a8e5c8a2f.png)

ぜひ、今回の内容をカスタマイズして、Denoで動作するWebアプリケーションを作ってみてください！
これをもっとちゃんとした雰囲気の掲示板アプリにした[Denoboard](https://github.com/syumai/denoboard)があるので、興味がある方はこちらもご覧ください。

![Screenshot from 2019-12-01 02-53-19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45427/71a92e50-9d6a-8f5c-00d8-a110789d9223.png)

Denoに関する質問があれば、[deno-jaのSlack](https://scrapbox.io/deno-ja/Slack%E3%81%AE%E5%8F%82%E5%8A%A0%E6%96%B9%E6%B3%95)に投げるとガシガシ回答が付いていくと思います。

これから28日、全ては埋まっていないですが、Denoに関する記事がガンガン投下されていくと思うとワクワクしますね！
明日は@hashrockさんの記事です！
それでは皆さん、楽しいDeno Lifeを！

### 補足

* 実は今回の内容では `Content-Type: text/html` を送るのもサボっています。ちゃんとやる方法は[公式のREADME](https://github.com/syumai/dinatra#dinatra)に記載があります