Deno製のWEBフレームワーク Fresh でチャットアプリを作成するまでの軌跡 〜第一章〜
はじめに
Deno
を知ったのは、デブサミで登壇していたDeno
コントリビュータのセッションがきっかけです。
日頃はNode.js
を利用して開発をしてますので、Node.js
との違いに着目しながら、Deno
の特徴をまとめたいと思います。
Deno
の特徴は別サイトでも列挙されているものを取り上げますが、私独自の視点で感じた事などをナレッジとして書き残します。
想定読者
日頃Node.js
を利用して開発している方やNode.js
を使って開発したことがある方
ゴール
-
以下の
Deno
の特徴を深掘りして理解すること- ライブラリ読込方式がパッケージマネージャではなく、URL直指定である
- パーミッションをプログラムに対して明示的に与える必要がある
- ブラウザ互換APIが利用できる
- TypeScriptを標準でサポートしている
- deno.landに一度公開されたモジュールは何があっても削除されない
-
Deno
に興味を持ってくれる人が増えること
特徴1
ライブラリ読込方式がパッケージマネージャではなく、URL直指定である
読込方式がURL直指定になったことによるメリット・デメリットは下記です。
メリット
- プロジェクトサイズの肥大化を抑えることができる点
- 動作未確認のバージョンが勝手に入ってしまうことがない点
デメリット
- npmのようなエコシステムが使えない点
メリット1
プロジェクトサイズの肥大化を抑えることができる点
Node.js
の場合
npm
やyarn
などのパッケージマネージャを利用して、必要なライブラリをpackage.json
で管理してました。リモートコードの実態をnode_modules
に保管するので、どんどん肥大化していましたね。依存パッケージが依存しているパッケージは無数にあり、結局node-modules/
ディレクトリが数百MB〜1GB
以上に膨れ上がったりしますよね。
肥大化の弊害として例えば下記があります。
devDependencies
の切り分けを実施していても、Node.js
製のシステムをAWS Lambda
にアップロードする際に容量制限に引っかかったことが何度もあります。
Deno
の場合
以下のようにURLから直接ライブラリをインポートできます。プロジェクトルートに保存されません。
import { helpers, RouterContext } from 'https://deno.land/x/oak@v6.5.0/mod.ts';
初回はダウンロードするのに数秒かかりますが、2回目はホストにキャッシュされますので早いです。ダウンロードされたライブラリの情報はdeno info
コマンドで確認が可能です。
また、関数実行時にライブラリをダウンロードしてくるため、それよりも前にインポートしたければ下記で対応可能です。
deno cache https://deno.land/std/log/mod.ts
Deno
の場合は1MB以下に抑えることが可能と思います。
素朴な疑問その1
素朴な疑問その2
メリット2
動作未確認のバージョンが勝手に入ってしまうことがない点
Node.js
の場合
Semantic Versioning
を使ってバージョン解決してました。
"axios": "^0.24.0",
"crypto-js": "^4.1.1",
"express": "^4.17.1",
これの弊害として下記があります。
例えば、ライブラリの作者が悪意のあるコードをパッチリリースでリリースした場合に、Semantic Versioning
で管理をしていると、npm
が「あ、パッチバージョンが上がってるやんけ!アップデートしといたろ!」と気を利かせてくれます。そこで自動でライブラリのアップデートが行われ、動作確認されていないバージョンのコードが紛れ混む可能性があります。
2022年1月にも以下のような事案が発生しています。
Deno
の場合
下記のようにライブラリ名の後に@v1.0.0
のようにバージョン指定します。
import { copy } from "https://deno.land/std@0.74.0/fs/copy.ts";
Semantic Versioning
を使わず特定のバージョンを指し示すことができるため、動作未確認のバージョンが勝手に入ってしまうことはありません。悪意のあるバージョンが勝手に依存関係に入り込むこともありません。
ちなみにバージョン(上記で言うと、@0.74.0
の部分)は省略可能です。その場合、最新版をダウンロードしてくることになりますが、Deno
の公式ドキュメントでは、明示的にバージョン指定することを強く推奨してました。
最新版はいつでも変更される可能性があり、コンパイルエラーや予期できない動作を起こす可能性があるマスターブランチのコードにリンクするより、変更されないイミュータブルのライブラリバージョン
を指定せよ、とのことです。
デメリット1
npmのようなエコシステムが使えない点
Deno
を使ってみて思ったこととして、npm というエコシステムが使えないのはデメリットである
が挙げられるなとも思います。npmで提供されるさまざまなライブラリヤフレームワークを利用することができませんでした。
とはいえ、以下のような動きもあるようです。
また、Denoによる開発においては、deno.land/x
やnest.land
といったDeno標準のライブラリ
を用いることが一般的ですが、GitHub
にあるコードもimport
可能です。
ただし、少し注意が必要です。例えば
https://github.com/takanabe-test/deno-sample/master/hoge.ts
からfuga
関数をimport
したい場合、そのままimport
しようとすると、mediaTypeがUnknown
というエラーが発生します。
レスポンスのContent-Type
がtext/html
なので怒られる模様。
レスポンスのContent-Type
がtext/plain
になるように調整すればエラーが起きずに実行できます。
特徴2
パーミッションをプログラムに対して明示的に与える必要がある
Node.js
の場合
プログラムが環境変数、ローカルファイル、ネットワークにアクセスし放題でした。
動作するサーバに、万が一悪意あるプログラムが紛れ込んだ場合、読み書きできるディレクトリを制限したり、 接続できるネットワークを制限してないので、やられたい放題となります。
Deno
の場合
デフォルト
で下記が禁止されています。
- ネットワークアクセス(ドメインをカンマ区切りで指定してドメイン許可リストを提供することも可)
- ファイルシステムの読み取りアクセス(ディレクトリまたはファイルをカンマ区切りで指定して、許可リストを提供することも可)
- ファイルシステムの書き込みアクセス(ディレクトリまたはファイルをカンマ区切りで指定して、許可リストを提供することも可)
- サブプロセスの実行
- 環境変数の取得や設定などの環境アクセス
- 高分解能時間計測(タイミング攻撃やフィンガープリントに使われます)
読み書きできるファイルやディレクトリを局所的に設定することが可能であり、必要最小限の権限を局所的に与えるこの考え方(実行されるコードは信頼できないものとみなすこの姿勢)が素晴らしいと思いました。
脳死状態で--allow-all
オプションを付与するのは絶対に辞めましょう。
パーミッションリストについては、Denoの日本語ドキュメントにもまとまっています。
特徴3
ブラウザ互換APIが利用できる
Web API
はWebブラウザに組み込まれている機能です。fetch API
などが代表的ですね。
Node.js
ではこれらを使用することはできません。これらのAPIはブラウザ(Google Chrome, Firefox)で利用できるものであり、Node.js
はブラウザではないからです。
Node.js
でfetch API
を利用しようとすると fetch is not defined
で怒られます。
例として、Deno
の場合には、標準でFetch API
がサポートされているため、インストールすることなく以下のように使用することができます。
const res = await fetch('https://deno.land/testing')
const json = res.json()
const data = await json
console.log(data)
ブラウザAPIの良い所は下記だと日野さんは言っています。
- ブラウザと共通して使えるコードを書ける
- ブラウザ API はとてもきちんと定義されている
a. 議論の質が高い
b. 仕様書の質が高い
c. 自動テストがある - 仕様策定プロセスがあるため、簡単に変わることはない
ただし、一方でこういう意見もあります。
賛否両論あるんだよ、という背景をしっかり理解しておきたいですね。
特徴4
TypeScriptを標準でサポートしている
ベストプラクティスと考えられているTypeScriptの設定がデフォルトで入っているので、設定無しで TypeScript
が使えます。デフォルトから外れたい場合は、自分でコンパイラオプションを書くことも可能です。
Node.js
で同じことをやろうとすると、typescript
やtsc
をinstall
して、ついでに型定義ファイルも追加して、tsconfig.json
を生成して、自身でルールを決めて、あっ・・・開発効率が悪いからts-node-dev
導入して...などちょっと面倒です。
この辺をすっ飛ばしてTypeScript
が使えるので、敷居の低さはメリットと言えるのかもしれません。
しかしながら、気になる記事も・・・
こちらもnpm
の話に加えて今後の動向に要注意ですね!
特徴5
deno.landに一度公開されたモジュールは何があっても削除されない
deno.landに公開されているモジュールは、モジュール管理者が勝手に既存のモジュールを削除することはないと書いてありました。
以前起きたleft-pad問題のような事案と同じ失敗を踏まないで済むのかと思います。
気軽に小さなライブラリに依存し過ぎてしまう文化も良く無いのかもしれないですね。
最後に
このページでは、Deno
の特徴を深掘りして、Deno
の基礎を学びました。次のフェーズでは2022年7月にv1.0がリリースされたDenoのフルスタック Web フレームワーク Fresh
を使ってサンプルアプリを製作し、Deno Deploy
を使ったプロダクションデプロイまで実施してみた結果を記事にしました。こちらも是非読んでいただければと思います。
Discussion