🦕

DenoのDemo

2022/05/22に公開

執筆の動機

Deno誕生から4年たち、MDN Web Docsの「ブラウザーの互換性」欄にDenoの情報が掲載されるようになっていたので、 そろそろDenoを触ってみようとおもいました。

https://twitter.com/deno_land/status/1525119512277725184?s=20&t=v958ELbgf5bQV0XjL5pTRw

Denoとは?

Node.jsの作者Ryan Dahlが、Node.jsで後悔している事を克服するために開発をはじめた、新しいJavaScript及びTypeScriptのランタイム環境です。

  1. セキュリティ強化
  2. ES Moduleだけ使う
  3. Typescriptビルドイン
  4. 単体の実行ファイルで動く
  5. モダンな開発環境で使う
  6. 可能な限りブラウザ互換にする

下記のアナグラムがプロジェクトをよく表していますね。。。

https://twitter.com/deno_land/status/1262517004159913985
Developers Summit 2022のスライド にDeno Landの日野澤さんがポイントをまとめているので、 詳しくスライドを御覧ください🙇🏻

https://kt3k.github.io/talk_devsumi_2022_deno/#1

Deno ja Slack Community

https://scrapbox.io/deno-ja/Slackの参加方法

Deno Example

サンプルコードはこちらをチェック
https://examples.deno.land/

Deno install

とりあえず、公式に従ってDenoをinstallします。

$ deno --version
# 執筆時時点のバージョンは下記になります。
deno 1.22.0 (release, x86_64-apple-darwin)
v8 10.0.139.17
typescript 4.6.2

Visual Studio Codeの設定

Deno Official: Using Visual Studio Codeに従い、Visual Studio Codeの設定を行います。

拡張機能の追加

  1. vscode_deno 拡張機能を追加します。
  2. Denoプロジェクト用のワークスペースを作成する。

VSCodeのコマンドパレットから下記コマンドでsettingsファイルが作成されます。

 > Deno: Initialize Workspace Configuration 

settings.json

  • VSCodeで保存時にformatterが動くように設定
  • deno.configで設定ファイルを指定
    • この設定ファイルで組み込みTypeScriptコンパイラ・フォーマッタ、リンタをカスタマイズできる(後続で設定します)
.vscode/settings.json
{
  "deno.enable": true,
  "deno.lint": true,
  "deno.unstable": true,
  "deno.config": "./deno.jsonc",
  "[typescript]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "denoland.vscode-deno"
  }
}

つくるもの

今回はURLをわたすとQRコードを生成する簡単なアプリを作成していきます。

ディレクトリ構成

下記ディレクトリ構成でファイルを作成していきます。

.
├── README.md
├── deno.jsonc
├── lock.json
└── src
    ├── deps.ts
    ├── server.ts
    └── validator
        ├── isUrl.ts
        └── mod.ts

設定ファイル:deno.jsonc

  • この設定ファイルで組み込みTypeScriptコンパイラ・フォーマッタ、リンタをカスタマイズできる。
    • JSONJSONCがサポートされている。
      • JSONCはコメントが自由にかけるので、今回はJSONCで記載します。

TypeScriptの設定

Configuration

deno.jsonc
{
  // TypeScriptの設定
  "compilerOptions": {
    "lib": ["deno.window"],
    "strict": true
  },
}

Linterの設定

Linter

deno.jsonc
{
 // Linter設定
  "lint": {
    "files": {
      "include": ["src"],
      "exclude": ["public"]
    },
    "rules": {
      "tags": ["recommended"],
      "exclude": [
      ]
    }
  },
}

Formatterの設定

Code Formatter

deno.jsonc
{
  // Formatter設定
  "fmt": {
    "files": {
      "include": ["src/"],
      "exclude": [".vscode", ".env"]
    },
    "options": {
      "useTabs": true,
      "lineWidth": 100,
      "indentWidth": 2,
      "singleQuote": true,
      "proseWrap": "preserve"
    }
  }
}

Code

今回は、Denoの基本的な機能を使っていきたいので、シンプルにDenoの標準ライブラリ(Standard Library)を使って、QRコードを生成して、HTMLを返します。

Denoでは、コアチームによって動作の保証されたStandard LibraryThird Party Modulesがあります。どちらもDeno landから確認できます。

もうすこし複雑なアプリケーションをつくるなら、Oakなどのフレームワークを利用するのがいいでしょう。

まずは、/src/server.tsを作成します。

./src/server.ts
import { log, qrcode, serve } from './deps.ts';
import { isUrl } from './validator/mod.ts';

async function handler(req: Request): Promise<Response> {
  const url = new URL(req.url);
  const targetUrl = url.searchParams.get('url') || '';
  const qrCode = await qrcode(targetUrl);

  if (!isUrl(targetUrl)) {
    const body = JSON.stringify({ message: 'Not Found' });
    return new Response(body, {
      status: 404,
      headers: {
        'content-type': 'application/json; charset=utf-8',
      },
    });
  }

  return new Response(
  `<html>
    <head>
    </head>
    <body>
      <h1>QR Code Generator</h1>
      <img src="${qrCode}">
    </body>
  </html>`,
  {
    headers: {
      'content-type': 'text/html; charset=utf-8',
    },
  });
}

const PORT = parseInt(Deno.env.get('PORT') ?? '8000');
log.info(`🦕  Starting server on port ${PORT}....`);

serve(handler, {
  port: PORT,
});

deps.ts

Denoでは、deps.tsで外部モジュールimportをまとめて、depsから参照するのがお作法になっている。 依存関係をまとめることで、バージョン変更などdepsを変更するだけでよくなる。

Ryan Dahl氏がNode.jsで後悔していたpackage.jsonの解になる部分かなと(Node.jsでpackages.jsonを作ったことを後悔している)

./src/deps.ts
// Standard library dependencies
export * as log from 'https://deno.land/std@0.140.0/log/mod.ts';
export { serve } from 'https://deno.land/std@0.140.0/http/server.ts';

// Third-party dependencies
export { qrcode } from 'https://deno.land/x/qrcode@v2.0.0/mod.ts';

Lockファイル

Node.jsでいうyarn.lock package.lock.json
リモートのモジュールに依存すると、リモート側のファイルに変更があった場合に本番モジュールがローカルモジュールとは異なる依存関係のコードを実行される可能性があります。
下記コマンドでlockファイルを生成する

$ deno cache --lock=lock.json --lock-write ./src/deps.ts

リモートのモジュールの内容が変更されていると、lockファイルのhash値が変わるため、比較して整合性をチェックできる
下記コマンドで整合性チェックをしつつ、リモートモジュールのダウンロードができる

$ deno cache --reload --lock=lock.json src/deps.ts

Task runnerの設定

Nodeでいうscriptsをここで設定できる。
Denoにはビルドインコマンド が用意されているため、
nodeのようにわざわざrimrafとかいれなくてもOSを気にしなくてよいので、いいですね。

Task runner

deno.jsonc
{
  ...
  // Taskの設定
  "tasks": {
    "dev": "deno run -A --watch ./src/server.ts"
  }
}

Taskを実行

早速タスクを実行してみましょう。

$ deno task dev

Deno Task

今回は、queryParamのurlからQRコードを生成するので、http://localhost:8000/?url=https://www.google.comでアクセスします。
するとQRコードが表示されます。

QR Code

スマホからQRコードを読み取ると無事googleのページが開きます。

Deno Deploy

Deno Deployは、JavaScript、TypeScript、およびWebAssemblyを、世界中のエッジでユーザーの近くで実行できるようにする分散システムです。
supabaseやNetlifyでdeno deployが利用されているようです。

Netlify Edge Functions
Supabase Functions

今回はdeno deployを利用して、先程のQRコードGeneratorをデプロイしてみます。

コスト

そのうち料金がアナウンスされるみたいですが、2022/05/22時点では、無料で利用できるようです。 詳細はPrice & Limitをご確認ください。

Rate Limitをこえると、logにでるみたい。

2022/05/25追記

Deno Deploy Beta 4 がリリースされました。
Free PlanとProプランができるっぽい

The Free plan includes 100k requests/day, 10ms CPU time per request, and up to 100 GiB data transfer a month. The Free plan also includes GitHub integration, which allows you to deploy on push with both Private and Public repositories.

The Pro plan starts at $10/month, which includes up to 5 million requests/month, 50ms CPU time per request, wild card subdomains, and data transfer at $0.30/GiB. Requests over 5 million will cost $2/million requests.

Freeプランでは、
100kリクエスト/日、1リクエストあたりのCPU時間10ms、1ヶ月100GiBまでのデータ転送が可能です。
また、FreeプランにはGitHubインテグレーションが含まれており、PrivateとPublic両方のリポジトリを使ってPushでデプロイすることができます。

Proプランは月額10ドルからで、
最大500万リクエスト/月、1リクエストあたり50msのCPU時間、ワイルドカードサブドメイン、0.30ドル/GiBのデータ転送が含まれます。500万回を超えるリクエストは、2ドル/100万回のリクエストとなります。

Region

Regions

Tokyo (asia-northeast1)
Osaka (asia-northeast2)

Deployしてみる

  1. Deno Deployからgithubでログインする
  2. 右上のNew Projectから新しいプロジェクトを作成する
    New Project
  3. 対象のレポジトリ・ブランチ・エンドポイントを指定するとプロジェクトが作成され、デプロイされます。
    Setting
  4. 「View」ボタンから確認すると無事デプロイされれいることが確認できます。

https://itwillrain-deno-qr.deno.dev/?url=https://google.com

めっちゃ簡単ですね 🎉
また、設定したブランチに変更をpushすると、自動的にデプロイが走ります。
反映もはやい🚄

フロントエンドでのDenoの可能性

DenoのフレームワークはFresh.jsAleph.jsがあるみたいですが、
まだ、Productionでは利用できなさそうなので、今後の展開に期待したいですね。

https://zenn.dev/uki00a/articles/frontend-development-in-deno-2022-spring

最近ではRome ToolchainBunといったtoolChainの統合が話題にあがっており、
フロント開発環境がどのようになっていくのか楽しみですね。

https://rome.tools/
https://bun.sh/

Denoでは、バンドラー・コンパイラー・フォーマッター・リンター・テスト・ベンチマークといった必ず必要になってくるツール群が組み込まれているのは、 良いなぁとおもいました。

Denoのフォーマッター・リンターはTypeScriptなら動くようなので、既存のプロジェクトのeslintを置き換えても動くようです。
今後は、eslintからdeno lintへ移行するということもあるのかなぁとおもいました。

https://zenn.dev/mizchi/articles/just-lint-by-deno
https://zenn.dev/magurotuna/articles/66618f26475702

まとめ

firebaseのcloud functionsなどは、デプロイに時間がかかりイライラした記憶がありますが、
deno deployは、ちょっと試したいときにサクッとつくれるので、いいなと思いました。

今後は、supabaseをつかってDB連携し、Denoでなにか作ってみたいですね。
(おぼえたてのGIPHY Captureを利用したくて動くGIF画像を多用してしまいました。)

Discussion