Deno入門 ─ 新しいTypeScript/JavaScript実行環境でWebアプリ開発とデータベース接続の基本を体験しよう

新しいJavaScript実行環境として、Deno(ディノ)の存在感はますます大きくなっています。Deno Land社のメンバーとして開発にあたる日野澤歓也(@kt3k)さんに、機能的な特徴の概説から、基本的な環境構築と開発のチュートリアルまでを執筆してもらいました。

Deno入門 ─ 新しいTypeScript/JavaScript実行環境でWebアプリ開発とデータベース接続の基本を体験しよう

Denoは、2018年に開発が始められた新しいサーバーサイドのJavaScriptおよびTypeScriptの実行環境です。

Deno - A modern runtime for JavaScript and TypeScript

本記事では、Denoの基本的な特徴を紹介します。 また後半では、Denoを使って基本的なWebサーバーを立ち上げるサンプルコードを紹介します。

なお、本記事のサンプルコードは、執筆時点での最新版であるバージョン1.22で動作確認を行っています。

Denoが開発されてきた意図と経緯

サーバーサイドのJavaScript実行環境というと、Node.jsが有名です。 Node.jsは2009年から開発が始められ、今ではWeb・モバイル・IoTとあらゆる開発シーンで活用が進む、人気の言語実行環境です。 AWS LambdaやGoogle App Engineといったクラウドプラットフォームでもデフォルトの選択肢の1つとしてNode.jsが提供されており、PaaSやサーバーレスサービスでも主役級のポジションを占めています。

これほど人気の高いNode.jsが既にある中で、Denoはどういう目的を持って開発されているのでしょうか?

実はDenoは、Node.jsの作者と同じライアン・ダール(Ryan Dahl)という人が開発を始めたプロジェクトです。 2018年のJSConf EUというカンファレンスで、ライアン・ダールは「Node.jsについて後悔している10のこと」と題した発表を行いました。

この発表でライアン・ダールは、Node.jsのデザインにおいて後悔している点として次の7つを挙げました1

  • 後悔1. Promiseを使わなかった
  • 後悔2. V8のセキュリティサンドボックスを活用できなかった
  • 後悔3. GYP(ビルドシステム)を使い続けてしまった
  • 後悔4. package.jsonという仕組みを導入してしまった
  • 後悔5. node_modules以下にモジュールをインストールする仕組みにしてしまった
  • 後悔6. モジュール解決時の拡張子省略可能にしてしまった
  • 後悔7. index.jsを省略可能にしてしまった

これらを克服するためにDenoというプロジェクトを始めると発表し、次のような目標を掲げました。

  • V8のセキュリティサンドボックスを活用する
  • ES Modulesを使ったシンプルなモジュールシステム
  • TypeScriptビルトイン
  • 単体の実行ファイルで動かす
  • モダンな開発環境を使う
  • 可能な限りWebと互換なAPIを使う

つまりDenoは、作者が「Node.jsのデザインミスである」と考えるいくつかの問題点を修正する、という意図を持って開発が始められたプロジェクトです。

発表の詳細については当日の資料「Design Mistakes in Node」(PDF)も参照してください。ここでは各論点に深入りしませんが、特にDenoに特徴的な点をいくつか次節で説明します。

Denoにはどんな特徴があるのか?

この節では、Denoの特徴を以下の4つの点から紹介していきます。

  • TypeScriptサポート
  • ビルトイン開発ツール
  • Web標準
  • セキュリティサンドボックスの活用

TypeScriptサポート

Denoには、TypeScriptがビルトインされています。 つまり、TypeScriptをそのまま動かすことができます。

Node.jsの世界では、TypeScriptはあくまでもサードパーティ製ツールの1つです。 そのままTypeScriptを実行できないので、いったんJavaScriptに変換してNode.jsに読みこませる必要があります。 そのステップを省略するts-nodeというツールがありますが、これも別途インストールが必要です。

ユニットテストでTypeScriptを実行する際も同様です。 Node.jsではテストフレームワーク(Jest、Mocha、ほか)に別途、TypeScriptをコンパイルしながらテストを実行させる設定(@babel/preset-typescriptないしts-jestといったツールのインストール)が必要です。

Denoを使った場合は、単純にTypeScriptでテストを書いて、deno testコマンドを実行するだけです。 後は、Denoが全てをうまく処理してくれます。

このようにDenoでは、TypeScriptファーストな開発を、特殊な設定や特殊なツールのインストールなしに始めることができます。

ビルトインされた開発ツール

Denoの大きな特徴の1つとして、多くの開発ツールがビルトインで提供されています。

ここで、Node.jsを使って新しい開発プロジェクトを立ち上げることを考えてみましょう。 今どきのNode.jsプロジェクトならば、次のような手順で開発環境を整えることになるでしょう。

  1. まず、TypeScriptをインストールする
  2. 次にリンターツールとして、ESLintをインストールする
  3. 同時にリンターのプラグインで、typescript-eslintなどもインストール
  4. さらにフォーマッターとして、最近であればprettierをインストール
  5. そしてテストツールとして、JestないしMochaを……(以下省略)

このようにNode.jsでプロジェクトを始めるためには、開発立ち上げ時にとても多くの開発必須ツールを次々とインストールする必要があります。 各ツールには多くの設定項目があり、品質の高いプロジェクトを構築するには、そういった設定にも熟知している必要があります。

また、これらは全てサードパーティ製なので、全てのツールに競合があり、自分のインストールしているツールが本当に妥当かどうかを常に再確認する必要があります。

Denoではこれらの開発必須ツールが、例えば次のようにほぼ全てビルトインで提供されます。

  • TypeScriptの型チェックは、deno runないしdeno checkコマンドで行う
  • リンターとしては、deno lintコマンドが提供されている
  • コードのフォーマットは、deno fmtコマンドで行う
  • テストフレームワークは、Deno.testAPIとdeno testコマンドで行う

さらに全てのツールで、Denoコミュニティによって議論されたおすすめの設定がビルトインされており、特に追加設定しなくても使い始めることができます。 このため、Denoでプロジェクトを開始するにはDeno本体のみをインストールすればほぼ十分という状況が整っており、開発体験においてNode.jsに対して大きなアドバンテージを持っていると言えます。

他にも多くのツールがビルトインで提供されており、詳細はマニュアルの「Tools」を参照してください。

なお、このような開発必須ツールを言語処理系にビルトインするというアイデアは、Go言語の処理系が持っているgo rungo fmtgo testなどのビルトインツールの成功から着想を得ています。

Webブラウザとの互換性を重視した機能実装

Denoのデザイン方針の1つとして、可能な限りWebブラウザと互換なAPIで機能を実装することがあります。

例えばHTTPクライアントの機能であれば、WebブラウザにはfetchAPIがありますが、Denoでも(ほぼ)同じfetchを使うことができます。 他にも、バイナリ処理であればTyped Arrayオブジェクト(Uint8Arrayなど)、暗号処理であればWeb Crypto API(crypto)というように、できる限りWebブラウザと同じものがDenoでも動くように実装されています。

このメリットの1つは、WebブラウザとDenoの両方で動作するスクリプトが書けることです。 次のスクリプトは、GitHub APIからIDが“1”のユーザーのデータを取得する例ですが、DenoとWebブラウザ両方で全く同じように動きます。

const resp = await fetch("https://api.github.com/users/1");
console.log(await resp.json());

もう1つのメリットとして、Web互換APIは仕様としての質が高いということがあります。

言語処理系の独自APIは、仕様策定にかけられる工数にどうしても限りがあり、仕様自体のバグやエッジケースの考慮漏れが発生しがちです。 Web互換APIは、Webブラウザベンダを始めとした関係者が長い時間をかけて議論を積み重ねつつ策定されるため、考慮漏れが残っていることが少なく、またテストケースもしっかりしていて、安心して使えます。

ところでWeb互換であることは、開発開始当初は確かにDenoの特徴でした。 しかし現在では、Node.jsでも取り込みが活発に進んでいます。 例えば、Node.jsバージョン18からfetchAPIがビルトインされるようです。 またWeb Crypto APIは、cryptoモジュール中のcrypto.webcrypto名前空間から提供されています。 この点に関してDenoとNode.jsはほぼ同じ方向に向かっており、差分は少なくなっていると言えます。

セキュリティサンドボックスの活用

Denoが内部的に使っているJavaScriptのエンジンは、GoogleがChromeのために開発しているV8というものです。 Webブラウザ用のエンジンであるため、信用できないコードが動作する前提で設計されており、V8の中で動くJavaScriptは、厳格なセキュリティサンドボックスから外に出られないようになっています。

Node.js開発時には、このセキュリティサンドボックスを活用しようという発想がなかったため、無作為にさまざまなNative Bindingが定義されています。 Node.jsの中のプログラムは、このNative Bindingを経由して、V8の外の世界にさまざまな影響を及ぼすことができてしまいます。

Denoでは、このセキュリティサンドボックス機能がうまく活用されています。 DenoのNative Bindingは、全てOp(Operation操作)単位で管理されており、あるOpを発行するときには「そのOpを実行してよい権限を持っているか?」という検証が、Op実行毎に行われます。

具体的には、以下の7つのセキュリティフラグが定義されています。 これらの組み合わせにより、プログラムに特定の権限だけを与えて実行できます。

フラグ 説明
--allow-read ファイル読み取り
--allow-write ファイル書き込み
--allow-net ネットワーク
--allow-env 環境変数読み取り
--allow-run プロセス実行
--allow-ffi ネイティブ拡張の使用を許可
--allow-hrtime 高精度タイマーの使用を許可

例えばmain.tsというスクリプトに対して、ファイルの読み取りだけを許可したい場合は、以下のようにコマンドを実行します。

$ deno run --allow-read main.ts

このときmain.tsプログラムはファイルの読み取りだけが可能になるため、ファイルの書き込みやネットワークアクセスをするとPermissionErrorによる実行時エラーになります。 なお、実行時にフラグを何も与えなければ、どの権限も持っていない状態になります。

各フラグにはパラメータを指定でき、例えば次のように実行すると/home/userディレクトリの読み込みだけが許可されます(--allow-writeフラグも同様)。

$ deno run --allow-read=/home/user main.ts

また、--allow-netを次のように指定すると、特定のドメインとポートだけのアクセスを許可できます。

$ deno run --allow-net=example.com:80 main.ts

ネットワークのパラメータを、きちんと最低限の範囲だけに与えることで、不正なネットワークアクセスを防ぐことができます。 万が一、サプライチェーンアタックなどで不正なコードが依存関係に含まれてしまった場合でも、被害を最小限に抑えることができます。

このようなパーミッションの仕組みは、Denoに特徴的な機能です。 Node.jsでも同様の機能を入れたいという議論がされているようですが、まだ目処は立っておらず、実現性があるのか不明な状態です。 PythonやRubyといった他の動的言語処理系でもこのような機能が実装された例はなく、他の言語と比較しても特徴的な機能となっています。

Note: Deno Land Inc.について

Denoは、作者のライアン・ダール(ry)をはじめとする有志によって2018年に開発が開始され、2020年5月にバージョン1.0がリリースされました。 1.0リリース後の2020年9月ごろ、ライアン・ダールともう1人のコアメンバーであるバート・ベルダー(Bert Belder、piscisaureus)によって、Deno Land Inc.が設立されました。

The Company | Deno

現在のDenoは、このDeno Land社の主導で開発が続けられています。 Deno Land社が(筆者のような)フルタイムの開発者を雇うようになったため、以前と比較してより活発に開発される状態が続いています。

チュートリアル1: Denoで簡単なWebサーバーを立ててみる

この章では、Denoを使って簡単なWebサーバーを立ち上げる例を紹介します。

Denoをまだインストールしていない場合は、以下のコマンドでインストールしてください。

▶ Windowsの場合(パワーシェル環境で実行)

> iwr https://deno.land/install.ps1 -useb | iex

▶ LinuxおよびmacOS

$ curl -fsSL https://deno.land/install.sh | sh

また、Scoop、Chocolatey、Homebrewといったインストーラからでもインストール可能です。

HTTPサーバーを記述する

DenoでHTTPサーバーを立ち上げるのは非常に簡単です。 Deno標準のstd/httpモジュールから、serve関数をインポートします。

import { serve } from "https://deno.land/std@0.139.0/http/server.ts";
serve((req) => new Response("Hello, world!"));

上のソースコードをserver.tsというファイルに保存して、次のように実行してください(なお、パーミッションフラグの指定が面倒な場合は、開発中であれば-Aという全てを許可するフラグを使ってもかまいません)。

$ deno run --allow-net server.ts
Listening on http://localhost:8000/

実行すると上のようにメッセージが表示されるので、このhttp://localhost:8000をWebブラウザで開いてみてください。 画面にHello, world!と表示されるはずです。

簡単なHTMLでWebページを出力する

次に、もう少し本格的なWebページを出力してみましょう。 Webの表示には、nanossr(ナノSSR)という外部モジュールを利用するのが便利です。

/** @jsx h */
import { serve } from "https://deno.land/std@0.139.0/http/server.ts";
import { h, ssr } from "https://crux.land/nanossr@0.0.4";

serve(() => ssr(() => <App />));

function App() {
  return (
    <div>
      <h1>Hello Deno</h1>
      <p>Welcome to example page!</p>
    </div>
  );
}

このソースコードをファイルserver.tsxとして保存し(JSX記法を使っているため拡張子は.tsxになります)、次のように実行してください。

$ deno run --allow-net server.tsx

そしてhttp://localhost:8000/にアクセスすると、上記のソースコードに記述したHTMLの内容が画面に表示されます。

deno2

CSSでスタイルを指定する

上記のサンプルではHTMLがそのまま表示されるだけでしたが、これにCSSで適当なスタイルを当ててみましょう。 nanossrでは、Tailwind CSSというCSSフレームワークの記法が自動的にサポートされていますから、簡単なスタイルはtailwindクラスを指定するのが便利です。

先ほどのserver.tsxApp()にCSSを指定すると、例えば以下のようになります。

function App() {
  return (
    <div class="px-8 py-4">
      <h1 class="font-semibold text-2xl">Hello Deno</h1>
      <p class="mt-2 text-gray-500">Welcome to example page!</p>
    </div>
  );
}

px-8font-semiboldなどの指定が、Tailwind CSSのクラスです。

  • px-8py-4で、全体のパディングを調整
  • font-semiboldtext-2xlで、見出しのフォントを調整
  • 本文はtext-gray-500でテキスト色、mt-2でマージンを調整

これを同様に実行すると、ややスタイリッシュに表示されます。

deno3

tailwindクラスの詳細は、Tailwind のホームページで確認してください。

チュートリアル2: DenoでWebサーバーからデータベースに読み書きする

続いて、チュートリアル1で立ち上げたWebサーバーからPostgresデータベースに接続して、簡単なコメントを表示・保存するサンプルを作ってみましょう。

Postgresデータベースを使った簡単な実験をしたい場合には、無料枠で500MBまでのPostgresサーバーをすぐに使うことができるSupabaseというサービスが便利です。

The Open Source Firebase Alternative | Supabase

ここでは、Supabase上にPostgresサーバーを立てる前提で説明していきます。

Supabaseでプロジェクトを作成してデータベースを用意する

まず、Supabaseにサインインしましょう。GitHubアカウントが必要です。

deno4

Supabaseから権限の認可を聞かれるので、許可しましょう。

deno5

サインインが完了したら「New Project」ボタンからプロジェクトを作りましょう。

deno6

ここでは「Denoサンプルプロジェクト」というプロジェクトを作成します。この際にデータベースのパスワードを決められるので、メモしておきましょう。この後でDenoからデータベースに接続する際に利用します。

なお、このパスワードは再入手できないため、控え忘れるとプロジェクトを再作成することになります。

deno7

「Create new project」ボタンを押して少し待つと、プロジェクトが作成されて下記の画面に遷移します。

deno8

次節では、このデータベースをSQLで操作します。左メニューから「SQL Editor」を選んでください。

データベースにテーブルとレコードを用意する

SQL Editorを開いたら「New Query」ボタンを押してください。 SQL入力欄が現れるので、以下のSQLを入力し、「Run」ボタンを押してコメント用のテーブルを作成しましょう。

create table Comments (
  id bigint generated by default as identity primary key,
  name text,
  comment text,
  date timestamp with time zone default timezone('utc'::text, now()) not null
);

名前、コメント内容、日付だけを持ったシンプルなコメントデータが記録されます。

次に、あらためて「New Query」ボタンを押し、以下のようなSQLを入力して「Run」ボタンを押してください。 ダミーのデータが作成されます。

insert into Comments
  (name, comment)
values
  ('アリス', 'ハロー'),
  ('ボブ', 'こんにちは'),
  ('アリス', '調子どう?');

データがインサートされていることを確認してみます。左メニューからTable Editorを開き、Commentsテーブルを選んでください。

deno9

上記のように表示されているはずです。これでデータベースの用意ができました。

最後に、左メニューからDatabaseを開き、Connection Pooling設定を選んでください。 ここに記載されたデータベースのホスト名を控えてください。この後でDenoからデータベースに接続する際に利用します。

Denoでデータベースクライアントを作成してデータベースサーバーに接続する

前節で用意したデータベースにDenoから接続してみます。Postgresデータベースへの接続にはdeno-postgresというモジュールを使用します。

以下のソースコードでは、/x/postgresモジュールからClientクラスをインポートしています。

import { Client } from "https://deno.land/x/postgres@v0.15.0/mod.ts";

const client = new Client({
  user: "postgres",
  database: "postgres",
  hostname: "<控えたホスト名>",
  password: "<控えたパスワード>",
  port: 6543,
});

await client.connect();

{
  const result = await client.queryObject("select * from Comments");
  console.log(result.rows);
}

hostnamepasswordには、先ほど控えたデータベースのホスト名とパスワードを指定します。

作成したclientに対して.connect()することで、データベースとの認証が行われます。 実際のデータベース問い合わせは、.queryObject()で行っています。

これをlist-comments.tsとして保存して、次のように実行してみてください。

$ deno run --allow-net --allow-env list-comment.ts
...略...
[
  { id: 1n, name: "アリス", comment: "ハロー", date: 2022-05-18T12:23:11.896Z },
  { id: 2n, name: "ボブ", comment: "こんにちは", date: 2022-05-18T12:23:11.896Z },
  { id: 3n, name: "アリス", comment: "調子どう?", date: 2022-05-18T12:23:11.896Z }
]

うまくいくと、先ほど入力したダミーデータを表示できるはずです。

データベースクライアントをWebサーバーに組み込む

上記のデータベースクライアントを、先ほどのWebサーバーと合わせてみましょう。次のようなソースコードになります。

/** @jsx h */
import { serve } from "https://deno.land/std@0.139.0/http/server.ts";
import { h, ssr } from "https://crux.land/nanossr@0.0.4";
import { Client } from "https://deno.land/x/postgres@v0.15.0/mod.ts";
const client = new Client({
  user: "postgres",
  database: "postgres",
  hostname: "<控えたホスト名>",
  password: "<控えたパスワード>",
  port: 6543,
});
const connection = client.connect();

type Comment = {
  name: string;
  comment: string;
  date: Date;
};

serve(async () => {
  await connection;
  const result = await client.queryObject<Comment>("select * from Comments");
  return ssr(() => <App comments={result.rows} />);
});

function App(props: { comments: Comment[] }) {
  return (
    <div class="px-8 py-4">
      <h1 class="font-semibold text-2xl">Deno Board</h1>
      <p class="mt-2 text-gray-500">Welcome to Deno Board!</p>
      <ul class="mt-4">
        {props.comments.map((comment) => (
          <li>
            {comment.name} &gt; {comment.comment}{" "}
            <span class="text-gray-500 text-sm">
              ({comment.date.toLocaleString("ja")})
            </span>
          </li>
        ))}
      </ul>
    </div>
  );
}

ここではserve関数のハンドラーの中で、先ほど説明した.queryObject()を呼び出してコメントを取得しています。 型引数に<Comment>を指定することで、クエリー結果のrowsにComment型が付くようになっています。

ハンドラー関数で取得したコメントリストをJSXのAppに渡して、それを<ul>タグの中でループしてリスト表示しています。 リストの各行では、名前 > コメントの形式で表示した後に、投稿時刻を表示しています。 dateはJSビルトインのDate型になっているので、.toLocalString("ja")で一般的に読みやすい形式に変換できます。

このサーバーに先ほどと同様にWebブラウザから接続すると、次のようにデータベースから取得したコメント一覧が表示されます。

deno10

Denoからデータベースに書き込む

最後に、このWebアプリケーションに書き込み機能を追加してみましょう。 ここでは簡単のため、POSTでリクエストが来たら書き込みであるとみなし、データベースにデータを挿入して、元のページに戻って再読み込みさせるという実装を考えます。

POSTのハンドリングは以下のようになります。ハンドラーの先頭にこれを追加します。

if (req.method === "POST") {
  const form = await req.formData();
  await client.queryObject(
    "insert into Comments (name, comment) values ($1, $2)",
    [form.get("name"), form.get("comment")],
  );
  return new Response("", {
    status: 303,
    headers: {
      Location: "/",
    },
  });
}

ここでreq.methodにリクエストメソッドが入っているので、これがPOSTの場合、書き込みの分岐に入ります。 req.formData()にフォームデータのリクエスト内容が入っているので、これを呼び出します。この処理はネットワーク読み取りのため、awaitが必要です。

取得したフォームデータからnamecommentを抜き出して、SQLに流し込みます。 SQLを作るときには、必ず$1$2のようなプレースホルダーを使います。 この記述の仕方によって、適切なエスケープ処理がなされ、SQLインジェクション等の脆弱性を防ぐことができます。

SQLが成功したら、303レスポンス(リダイレクト)を返します。 このレスポンスによって、Webブラウザが自動的に元のページに戻り、ページが再ロードされ、書き込んだコメントが反映されたように見えます。

このフォームデータを入力させるため、Appに入力フォームの記述を追加します。 特に工夫の必要はなく、単純に<form>タグで書くことができます。

<form action="/" method="POST" class="mt-4 flex gap-2">
  <input name="name" placeholder="name" class="border rounded px-2" />
  <input
    name="comment"
    placeholder="comment"
    class="border rounded px-2"
  />
  <input type="submit" value="コメント" class="rounded px-2" />
</form>;

このフォームを先ほど表示したコメントリストの下に配置すると、書き込めるコメント欄の完成です。ソースコード全体は下記のGistにあります。

kt3k/eh-deno-post-example.tsx

Postgresサーバーのホスト名とパスワードを書き換えて、ぜひ動かしてみてください。

$ deno run -A post.tsx

上記でWebサーバーを起動し、Webブラウザから接続すると次のように名前とコメントを記入できるフォームが追加されています。

deno11

入力して「コメント」をクリックすると、データがPostgresサーバーに送信され、続いてページがリロードされるため直前に送信したコメントまでが表示されます。

deno12

Note: Deno Deployにデプロイする

Deno Land社では、DenoプロジェクトのホスティングサービスDeno Deployを提供しています。

Deno Deploy

このサービスを使うと、Denoで書いたWebサーバープログラムを、日次10万リクエスト分まで無料でホスティングできます。 上のサンプルをDeno Deployにデプロイしてみましょう。

Deno Deployには、GitHub連携でプログラムをDeployする方法と、オンラインのエディタで書いたファイルを直接デプロイする方法の2通りがあります。 ここではオンラインエディタを使う方法を紹介します。

  1. 上記のリンクからDeno Deployにログインする(GitHubの権限認可を要求される)
  2. 「New Project」ボタンから、新しいプロジェクト作成画面に遷移する
  3. 画面右側のPlaygroundエリア中の「Play」ボタンを押し、少し待つとオンラインエディタが起動するので、上で作成したプログラムをテキスト欄にコピー&ペーストする
  4. 最後に、テキストエリア右上の「Save & Deploy」ボタンを押す

これでプログラムがデプロイされ、結果が画面の右側に反映されます。

deno13

正しくデプロイできれば、このような表示になります。

さらにDenoに詳しくなろう

ここまででDenoの概要とチュートリアルを終えたので、Denoを使っていく用意はできたかと思います。さらにDenoを本格的に利用したり、今後の活用を検討する上で必要になる情報を紹介します。

本格的なWeb開発を行う場合

上記では、nanossrモジュールを使って比較的簡単にWebサーバーを記述する例を紹介しました。より本格的にWeb開発を進めていきたい場合は、以下のようなツールやフレームワークの使用も検討してみてください。

Oak

Denoで定番のWebフレームワークです。 Node.js用のフレームワークkoaからインスパイアされたデザインを持っています。

柔軟なミドルウェアの仕組みを持っていて、従来通りのWeb開発ができます。 Node.jsでexpressを使った開発に慣れている人は特に違和感なく使うことができるはずです。

Aleph

Next.jsやNuxtにインスパイアされたDeno用のWebフレームワークです。 pathベースルーティングを実装していて、Next.jsに近い開発体験を提供します。

最近活発に開発されているcanaryバージョン(次期バージョン)では、Remixにインスパイアされた、簡単にAPIエンドポイントを記述できる記法や、Nested Routesなども取り入れています。 このcanaryバージョンは、Deno社内でも現在複数のプロジェクトで使っており、今後も活発な開発が続くことが期待できます。

Fresh

Freshも、Alephと同様に、Next.jsにインスパイアされたフレームワークです。 Next.jsと同様に、pathベースルーティングを提供しています。 Alephと異なり、SPA遷移は行わず、必ずSSRでページをレンダリングする点が特徴です。

Freshは、Denoの社員であるLuca Casonato(lucacasonato)によって開発が進められており、Denoのホームページも現在はFreshで実装されています。 今後も活発な開発が続けられることが期待できます。

Denoの未来となる採用事例

Denoの人気は徐々に高まっており、メガベンチャーからスタートアップまでDenoを採用するプロジェクトの事例も増えています。 ここでは、GitHub、Slack、Netlifyの発表を紹介します。

GitHubによるFlat Dataプロジェクト

2021年5月、GitHubからFlat Dataというプロジェクトが発表されました。

これは、世の中にあるさまざまなテキスト化されたデータをgitリポジトリに取り込んで、変化を可視化するテクニック(git scrapingと呼ぶ)をシステマティックに行うフレームワークです。このプロジェクトで、Denoはデフォルトのデータスクレーピング用の言語として採用されました。

GitHub Next | Flat Data

Run on Slack functions

2021年11月、Slackが新しいプラットフォームの1機能としてRun on Slack functionsというサービスを発表しました。 このサービスはユーザーが定義したServeless FunctionをSlackのインフラ上にデプロイして、Slackを使ったワークフローを効率的に開発するためのサービスです。

このサービスでは内部的にDenoが使われており、ユーザーはDenoと互換のAPI、モジュールを使って開発できます。

Netlify Edge Functions

2022年5月、NetlifyのブログでNetlify Edge Functionsという新機能が発表されました。

Netlify Edge Functions: Serverless Compute Powered by Deno

Netlifyは静的サイトのホスティングを主製品とするサービスですが、以前からサーバーレスソリューションもいくつかリリースしており、Edge Functionsはその最新のバージョンにあたります。

以前のサーバーレスサービスでは、最新のJSフレームワークを走らせられないなど、ユーザーのニーズに答えきることができていませんでした。 このEdge Functionsのリリースによって、Next.js、Nuxt、SvelteKit、Remixなどのフル機能を持ったフレームワークを動かすことに成功しています。

このEdge Functionsは、内部的にDenoを使って実装されています。Edge Functionsのユーザーは、Denoと互換のAPI、モジュールを利用して開発できます。

Denoの抱える課題

ここまでDenoの良い面を中心に解説してきましたが、最後に現状のDenoの課題をいくつか挙げていきます。

Denoの抱える最も大きな課題は、ライブラリの少なさです。 Node.jsのモジュールレジストリであるnpmには、大変多くのライブラリが登録されており、ライブラリの登録数は全言語の中でも最多であると言われています。 そのためNode.jsで何かをしたい場合、かなりの確率でそれを解決してくれるライブラリがnpm上に既にあることが期待できます。

Denoの場合、npmに相当する公式レジストリが下記のリンクに用意されていますが、現在のライブラリ登録数は4,000程度にとどまり、npmには遠く及ばない数値です。

Third Party Modules | Deno

この状況を解決するため、DenoチームはNode.js互換モードを開発しており、Deno内でnpmモジュールをそのまま動かす機能を開発しています。 互換モードは、次のコマンドで実行可能です。

$ deno run --compat --unstable

expressなどいくつかのライブラリの実行には成功していますが、完全な互換性の提供にはまだまだ時間がかかる見通しです。

また、公式レジストリの検索性の悪さという点も問題視されています。 Denoで定番とされるモジュールや高品質なモジュールが見つかりにくいという指摘が多くされています。 この点については、現在Deno社内のチームが新しいドキュメント検索システムを構築中で、今年の中盤までにリリース予定となっています。

まとめ

本記事ではDenoの基本的な特徴の紹介と、Denoを使ったWebサーバーのサンプルを紹介しました。

採用例でも見たように、Denoは海外のメガベンチャーでも採用が進みつつある状況で、処理系としての信頼度や安定性としては、十分プロダクション用途に耐えうる段階に達していると言えます。TypeScriptサポートやビルトインツールによる快適な開発体験で一歩先を行くDenoを、ぜひ使ってみましょう!

日野澤 歓也(HINOSAWA Yoshiya) twitter: @kt3k / GitHub: kt3k

deno14
京都大学在学中にコードゴルフなどでプログラミングに親しみ、いくつかの事業者でWeb開発を経験するうちにJavaScriptの可能性に気づき、メインの開発言語とする。2015年からフリーランスのフロントエンドエンジニアとして働きながら、Node.jsのコミュニティを通じてDenoを知り、2021年1月にDeno Land, Incにジョインする。
kt3k.org

編集:はてな編集部


  1. 後悔の数が発表タイトルと一致しませんが、本人に確認したところ、もともと資料と同じ「Design Mistakes in Node」という発表を意図しており、「10 Things……」はカンファレンス側が付けたものだとのことでした。