2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptのFetch APIを学ぶためにDenoでWebサーバを作った

Last updated at Posted at 2023-12-29

ここで説明するコード一式をGitHubで公開しています。

動機

わたしは Deno を触り始めてまだ1ヶ月の初心者です。Denoについて自分用のメモを書こうとおもう。最初に動機を述べる。

第一の動機。わたしは長いあいだJavaScriptをちゃんと学習せず とりあえず動いた で立ち止まっていた。2023年12月、わたしは書籍

を読んだ。この本を読んでJavaScriptを深く学ぶことができた。ただし本格本にひとつ不満があった。《10.4 非同期通信の基本を理解する - Fetch API》のサンプルコードを実行するのにWebサーバを立てる必要があるのだが、PHP言語によるWebサーバの実装例が紹介されていた。ところがわたしはPHPがわからない。JavaScriptを学ぶのにPHPも合わせて学ばなければならないのはつらい …​ そうだ、Deno があるじゃないか と思った。WebサーバをTypeScriptで書きDenoで動かそう。クライアントはもちろんJavaScriptで書く。やってみよう。

第二の動機。2023年9月にDeno 1.37がリリースされて、JupyterカーネルとしてDenoが選べるようになった。

VS Codeに拡張モジュールをインストールすれば、JupyterでNotebookファイルを作りコードを素早く実行することができる。DenoがJupyterカーネルとして使えるならば、ipynb ファイルにTypeScriptのコードを書き、Ctrl+Enterで素早く実行して、結果をJupyterのなかで見ることができる。しかもVSCodeの中でそれができる。この道具は魅力的だ。Jupyterカーネルとしての Deno を使ってみよう。

作業環境を作る

前提する環境

わたしのマシンは MacBook Air M1、OSは macOS Sonoma 14.2.1 です。WindowsでもLinuxでも同じことができるはずですが、わたしは検証していません。以下のツールがインストール済みで実行可能であると仮定します。

  • Pythonがインストール済みで、コマンドラインでバージョンを確認できる。
$ python --version
Python 3.11.5
  • Jupyter Notebookがインストール済みで、コマンドラインでバージョンを確認できる。
$ jupyter --version
Selected Jupyter core packages...
IPython          : 8.18.1
...
  • Microsoft Visual Studio Codeがインストール済みで起動できる。

  • VSCodeに Jupyter拡張がインストール済み

この環境をあなたがすでに持っている前提します。つまり、あなたが自分のマシンでVSCodeを起動し、ファイル名拡張子が .ipynb であるNotebookファイルを作りダブルクリックすればJupyterが開く。JupyterのセルにPythonコードをひとつ書いて、Ctrl+EnterすればPythonカーネルによってコードが実行されて、メッセージが表示される。こんなふうに:

1 Jupyter Python VSCode

この環境をどうやって作ればいいのか?検索すれば参考になる記事がたくさん見つかる。そちらを参考にしてください。

Denoの本体をインストールする

Denoの本家サイト deno.com にインストール方法が説明されているのでそれに従ってDenoをインストールします。わたしはMacなのでHomebrewでやりました。

$ brew install deno

コマンドラインで deno コマンドが使えるようになるはずです。確かめましょう。

$ deno --version
deno 1.39.1 (release, aarch64-apple-darwin)
v8 12.0.267.8
typescript 5.3.3

JupyterでDenoを使うにはdeno 1.37以降が必要です。

VSCodeにDeno Extensionをインストールする

VSCodeのなかでTypeScriptコードをDenoで実行したい。そのためにはVSCodeに拡張をインストールする必要があります。ドキュメントはこちら:

使い方がこのように説明されている。

# Usage
1. Install the Deno CLI.
2. Install this extension.
3. Ensure deno is available in the environment path, or set its path via the deno.path setting in VSCode.
4. Open the VS Code command palette with Ctrl+Shift+P, and run the Deno: Initialize Workspace Configuration command.

項番4として "run the Deno: Initialize Workspace Configuraiton Command" と書いてあるが何のことやら?少し先にこう書いてある。

We recognize that not every TypeScript/JavaScript project that you might work on in VSCode uses Deno — therefore, by default, this extension will only apply the Deno language server when the setting deno.enable is set to true. This can be done via editing the settings or using the command Deno: Initialize Workspace Configuration.

While you can enable Deno globally, you probably only want to do that if every JavaScript/TypeScript workspace you work on in VSCode is a Deno based one.

あなたがTypeScript含むプロジェクトをVSCodeで開いたときDenoを使うとはかぎらない(Nodeを選ぶかもしれない)とDeno Extensionの開発者は考えた。だからDeno ExtensionをVSCodeにインストールしただけではVSCodeとDenoとは連携しない。VSCodeのSettingsを開き Workspaceの deno.enabletrue にする必要がある。この設定を施してはじめてVSCodeとDenoの連携が有効化される。

2 VSCode Settings Deno.Enable

Jupyterと連携するためのモジュールをDenoに追加する

以上でVSCodeのなかでDenoを使えるようになった。さらに一歩進もう。JupyterがDenoをカーネルの一つとして使わせてくれるようにしたい。そのためにはDenoに拡張モジュールを追加する必要がある。わたしはこうやりました。

$ deno jupyter --unstable --install

下記のQiita記事を参考にしました。

最初の一歩: VSCodeのなかでJupyterでTypeScriptコードを書きDenoで実行しよう

VSCodeでJupyterを開きNotebookファイルをひとつ作ろう。そこにごく短いTypeScriptコードを書こう。そのコードをDenoで実行して結果を見よう。以下、手順を説明する。

VSCodeを起動し適当なフォルダを開く。そこにNotebookを作る。ファイル名はなんでもいいのだがここでは test.ipynb としよう。ipynb ファイルをダブルクリックするとJupyterが立ち上がり、Notebookを編集可能な状態になる。

セルにTypeScriptコードを入力しよう。なんでもいいのだがここでは

console.log("Hello, world!");

と入力したとする。すると次のような画面になるだろう。

4.1 Jupyter still Python kernel is appointed

上のスクリーンショットをみると、JupyterがこのNotebookのためにPythonのカーネルを採用していることがわかる。また console.log(…​) というコードをPythonのコードだと認識している、ということがわかる。これではまずい。JupyterがこのNotebookのためのカーネルとしてDenoを採用するように設定することが必要だ。どうすればいいのか?以下に手順を示す。

現状このNotebookが採用しているカーネル名が表示されている箇所をマウスでクリックするとドロップダウンが開く。このドロップダウンのなかに Select Another Kernel…​ というメニューが現れるはずだ。

4.2 Select Another Kernel

Select Another Kernel…​を選択するとさらに次のドロップダウンが開く。Jupyter Kernel…​ を選択しよう。

4.3 Select Jupyter Kernel

次のドロップダウンが開き、そのなかに Deno が現れるはずだ。

4.4 Select Deno

Denoをカーネルとして選択しよう。すると console.log(…​) というコードがPythonではなくてTypeScriptとして認識された。

4.5 settings completed

このセルをマウスで選択して Ctrl+Enter しよう。コードが実行されて

Hello, world!

と表示されるはずだ。こんなふうに。

4.6 Hello

できた!

以上で、VSCodeのなかでJupyterを動かし、TypeScriptのコードを書き、Denoカーネルで実行できるようになった。

TypeScriptで素朴なWebサーバを書いた

わたしが何をしたいかというと、《改訂3版 JavaScript本格入門》(山田祥寛 著 2023年2月 技術評論社 刊、以下で「本格本」と略記)の《10.4 非同期通信の基本を理解する - Fetch API》に掲載されたHTTPクライアントとしてのJavaScriptを実行したいのだが、リクエストに応答してくれるWebサーバを自分で書きたい。TypeScriptで書きたい。それをDenoで動かしたい。HTTPリクエストのURLとパラメータを解析し適切なファイルを選んで応答できればいい。どういうコードを書けばいいのだろう?元ネタはないかしら?とネットで漁ったら、この記事を見つけた。

ぴったりだった。この記事もとづきWebサーバを自作した。ソースコードは全部で150行程度。短い。初心者のわたしでも全部理解できる。これを以下で説明する。

ソースコードのありか

ここで説明するコード一式をGitHubで公開しています。

$ tree .
.
├── app.ts
├── appstart.sh
├── apptest.ipynb
├── book.json
├── ...
├── fetch_basic.html
├── ...
├── index.html
├── native-router
│   └── mod.ts
├── scripts
│   ├── ...
│   ├── fetch_basic.js

最初の一歩

VSCodeで src/chap10/fetch/apptest.ipynb を開きます。このファイルはJupyterのNotebookファイルです。Simplest Request and Response と題したセルのあたりに下記のTypeScriptコードがあります。

const response = await fetch("http://localhost:3000/hello");
if (response.status === 200) {
    const text = await response.text();
    console.log(text);
} else {
    console.log(response);
}

Fetch APIを介して http://localhost:3000/hello にHTTP GETリクエストをあげる。応答を受けたらHTTPステータスを調べる。ステータスが200正常ならば、応答のボディ部分のテキストをconsoleに表示する。ステータスが200正常でなかったらResponseオブジェクトそのままconsoleに表示する。コードのセルを選択した状態でCTRLとENTERキーを同時に押そう。JupyterがコードをDenoに渡す。コードが実行される。Webサーバが応答を返すはずだ。

5.1 Simplest Request and Response failure

おっと、エラーになりました。まだWebサーバを立ち上げていなかったから、こうなるのは当然だ。Webサーバを起動しよう。VSCodeのTerminalウインドウを開き、src/chap10/fetch ディレクトリにcdします。そして シェルスクリプト appstart.sh を実行します。

$ cd <プロジェクトのディレクトリ>/src/chap10/fetch
$ ./appstart.sh

シェルスクリプトの中身は一行だけで、TypeScirpt ファイル app.tsdeno で実行しています。

deno run --allow-net --allow-read --allow-write --allow-env app.ts

5.2 Simplest Request and Response success

今度は、Webサーバが "Hello" と応答してくれました。

Webサーバをどうやって停止するか? ./appstart.sh を実行したのと同じTerminalウインドウで CTRLキーとCキーを押下してプロセスをkillします。

Webサーバのソースコードを読む

http://localhost:3000/hello をGETするHTTPリクエストに応答したのはTypeスクリプト app.ts の下記の部分です。

// https://medium.com/deno-the-complete-reference/native-router-in-deno-16595970daae
import { Router } from "./native-router/mod.ts";
import { serve } from "https://deno.land/std/http/mod.ts";

const router = new Router();

router.get("/hello", async (req: Request, params: Record<string, string>) => {
    let text = 'Hello';
    const u = new URL(req.url);
    const name = u.searchParams.get('name');
    if (name !== null) {
        text = `Hello, ${name}!`
    }
    return new Response(text, 
                        { headers:{"Content-Type": "text/plain; charset=utf-8"}});
});


async function reqHandler(req: Request) {
    console.log(`\n[serve.ts#reqHandler] Request:  ${req.method} ${req.url}`);
    return await router.route(req);
}
serve(reqHandler, { port: 3000 });

"/hello" というURLPatternにマッチするHTTPリクエストがWebサーバに到来したら Hello と応答する、ただそれだけのことをしています。

Native Routerライブラリ in Deno

app.ts./native-router/mod.ts というコードをimportして利用しています。このコードは下記のサイトからダウンロードしました。

記事 "Native Router in Deno"がnative-routerライブラリについて解説しています。わたしはこの記事を読み ./native-router/mod.ts を使って app.ts を書きました。

以下、app.ts の中身をユースケース毎に分けて説明していきます。

クエリーつきGETリクエストに応答するケース

  • クライアント apptest.ipynb のコード
const response = await fetch("http://localhost:3000/hello?name=decoy");
if (response.status === 200) {
    const text = await response.text();
    console.log(text);
} else {
    console.log(response);
}

このTypeScriptコードはFetch APIを使ってHTTP GETリクエストを投げます。URLのなかにクエリー文字列 ?name=decoy が埋め込まれていることに注意。これを実行すると下記のメッセージが表示される。

Hello, decoy

このリクエストに応答するのはWebサーバの下記のコードです。

  • Webサーバ app.ts のコード
router.get("/hello", async (req: Request, params: Record<string, string>) => {
    let text = 'Hello';
    const u = new URL(req.url);
    const name = u.searchParams.get('name');
    if (name !== null) {
        text = `Hello, ${name}!`
    }
    return new Response(text, 
                        { headers:{"Content-Type": "text/plain; charset=utf-8"}});
});

RequestのなかにURLクエリーつまり ?name=値 の形で埋め込まれた値を取り出しています。まずURL文字列を URL オブジェクトに変換する。name=decoy がURLオブジェクトの searchParams プロパティに変換されているので、そのなかから name プロパティの値を取り出しています。

POSTリクエストに応答するケース

  • クライアント apptest.ipynb のコード
const formData = new FormData();
formData.set('name', 'Ippei');
const response = await fetch("http://localhost:3000/hello", {
    method: 'POST',
    body: formData,
})
if (response.status === 200) {
    const text = await response.text();
    console.log(text);
} else {
    console.log(response);
}

このTypeScriptコードはHTTP POSTリクエストを投げます。リクエストのbodyのなかにFormデータとして name パラメータと値 Ippei を渡しています。これを実行すると下記のメッセージが表示される。

Hello, Ippei

このリクエストに応答するのはWebサーバの下記のコードです。

  • Webサーバ app.ts のコード
router.post("/hello", async (req: Request, params: Record<string, string>) => {
    let text = 'Hello';
    const formData = await req.formData();
    const name = formData.get('name');
    if (name !== '') {
        text = `Hello, ${formData.get('name')}`;
    }
    return new Response(text, 
                        { headers: {"Content-Type": "text/plain; charset=utf-8"}});
});

POSTリクエストによって渡されたRequestオブジェクトのなかからFormデータを読み出し、name パラメータの値 Ippei を取り出しています。

クライアントとWebサーバがどちらもTypeScriptで書いてあって、どちらもFormData クラス を使っている。習うことが少なくて嬉しい。クライアントがJavaScriptでWebサーバがPHPというように異言語混成だと2つの流儀を習わなければならず面倒だ。

サーバが受けたRequestオブジェクトを処理するやり方については下記のドキュメントが参考になります。

URLのPathがパラメータ化されているリクエストに応答するケース

  • クライアント apptest.ipynb のコード
const response = await fetch("http://localhost:3000/hello/dekopin");
if (response.status === 200) {
    const text = await response.text();
    console.log(text);
} else {
    console.log(response);
}

このTypeScriptコードはURLパス /hello/dekopin にたいしてHTTP GETリクエストを投げます。これを実行すると下記のメッセージが表示される。

Hello, dekopin

URLパス /hello/dekopin の二番目の階層にあたる文字列 dekopin がパラメータの値として解釈されています。URLパスが /hello/yoshinobu ならば、応答はこうなるでしょう。

Hello, yoshinobu

このリクエストに応答するのはWebサーバの下記のコードです。

  • Webサーバ app.ts のコード
router.get("/hello/:name", async (req: Request, params: Record<string, string>) => {
    return new Response(`Hello, ${params.name}!`, 
                        { headers: {"content-type": "text/plain; charset=utf-8"}});

Routerクラスがgetメソッドの第2引数としての無名関数の第二引数としてRecordオブジェクトを引き渡してくれる。このRecordオブジェクトのなかに name の値が dekopin だという情報が入っている。唐突な感じだがすごく便利だ。どうなっているのか?Routerクラスは内部においてURLPatternクラスを利用しています。

さらに分け入るならば、URLPatternは path-to-regexp ライブラリを利用しています。

path-to-regexpはたとえば "/hello/:name" のようなパターン文字列を受け取ってそれと意味的に等価な正規表現を生成します。path-to-regexpの使用例を下記に示す。

6 path to regexp

path-to-regexpが生成した正規表現を "/hello/dekopin" という文字列に適用すれば、パラメータ name に該当する値として dekopin という文字列が取り出されました。へえ、なるほど、感心しました。

HTMLファイルを応答するケース

  • クライアント apptest.ipynb のコード
const response = await fetch("http://localhost:3000/fetch_basic.html");
if (response.status === 200) {
    const text = await response.text();
    console.log(text);
} else {
    console.log(response);
}

このTypeScriptコードはURLパス /fetch_basic.html にたいしてHTTP GETリクエストを投げます。これを実行すると下記の内容が表示される。

<html>
  <head>
    <meta charset="UTF-8" />
    <title>JavaScript本格入門</title>
  </head>
  <body>
    <input id='btn' type="button" value="print message in console"/>
    <script type="text/javascript" src="scripts/fetch_basic.js"></script>
  </body>
</html>

このリクエストに応答するのはWebサーバの下記のコードです。

  • Webサーバ app.ts のコード
router.get("/:filename.html", async (req: Request, params: Record<string, string>) => {
    const html = await Deno.readTextFile(`${params.filename}.html`);
    return new Response(html, { headers: {"content-type": "text/html; charset=utf-8"}});
});

fetch_basic.html はWebサーバーのディスク上に在るHTMLファイルです。Webサーバはこのファイルをディスクから読み取ってクライアントに応答します。

JSファイルを応答するケース

  • クライアント apptest.ipynb のコード
const response = await fetch("http://localhost:3000/scripts/fetch_basic.js");
if (response.status === 200) {
    const text = await response.text();
    console.log(text);
} else {
    console.log(response);
}

このTypeScriptコードはURLパス /fetch_basic.js にたいしてHTTP GETリクエストを投げます。これを実行すると下記の内容が表示される。

let btn = document.querySelector('#btn');
btn.addEventListener('click', function() {
    fetch('book.json')
        .then(res => {
            if (res.ok) {
                return res.json();
            }
            throw new Error('unable to access the specified resource');
        })
        .then(data => console.log(data.title))
        .catch(e => window.alert(e.message));
}, false);

このリクエストに応答するのはWebサーバの下記のコードです。

  • Webサーバ app.ts のコード
router.get("/scripts/:filename.js", async (req: Request, params: Record<string, string>) => {
    const html = await Deno.readTextFile(`scripts/${params.filename}.js`);
    return new Response(html, { headers: {"content-type": "application/javascript; charset=utf-8"}});
});

fetch_basic.js はWebサーバーのディスク上に在るJavaScriptファイルです。Webサーバはこのファイルをディスクから読み取ってクライアントに応答します。

JSONファイルを応答するケース

  • クライアント apptest.ipynb のコード
const response = await fetch("http://localhost:3000/book.json");
if (response.status === 200) {
    const text = await response.text();
    console.log(text);
} else {
    console.log(response);
}

このTypeScriptコードはURLパス /book.json にたいしてHTTP GETリクエストを投げます。これを実行すると下記の内容が表示される。

{
  "title": "JavaScript本格入門",
  "price": 3200,
  "publisher": "技術評論社"
}

このリクエストに応答するのはWebサーバの下記のコードです。

  • Webサーバ app.ts のコード
router.get("/:filename.json", async (req: Request, params: Record<string, string>) => {
    const html = await Deno.readTextFile(`${params.filename}.json`);
    return new Response(html, { headers: {"content-type": "application/json; charset=utf-8"}});
});

book.json はWebサーバーのディスク上に在るJSONファイルです。Webサーバはこのファイルをディスクから読み取ってクライアントに応答します。

本格本のサンプルコードを補足する

《改訂3版 JavaScript本格入門》(山田祥寛 著 2023年2月 技術評論社 刊、以下で「本格本」と略する)の《10.4 非同期通信の基本を理解する - Fetch API》に掲載されたHTTPクライアントとしてのJavaScriptを実行するのに必要なWebサーバを、TypeScriptで書き、Denoで実行することを試みて成功しました。以下に説明します。本格本を補足できればいいなと思っています。

コードは下記にあります。

まずWebサーバを起動することから始めましょう。

$ cd <projectディレクトリ>/src/chap10/fetch
$ ./serve-start.sh
Listening on http://localhost:3000/

シェルスクリプト serve-start.sh の中身は下記のとおり。

deno run --allow-net --allow-read --allow-write --allow-env serve.ts

TypeScriptファイル serve.ts をDenoで実行しています。

Webサーバが立ち上がったところで、任意のWebブラウザを開き URL http://localhost:3000 にアクセスします。下記のような画面が表示されるはず。

7 index

ここに表示されたリンクは、本格本の《10.4 非同期通信の基本を理解する - Fetch API》に掲載されたサンプルコードに基づいており、実際に動作することを確認済みです。JavaScriptとHTMLにかんしては本格本を参照願います。

serve.ts の中身は下記の通り。

// deno-native-router at https://deno.land/x/nativerouter@1.0.0

import { Router } from "./native-router/mod.ts";
import { serve } from "https://deno.land/std/http/mod.ts";
import Logger from "https://deno.land/x/logger@v1.1.3/logger.ts";
import { readerFromStreamReader } from "https://deno.land/std/streams/reader_from_stream_reader.ts";

const logger = new Logger()

const router = new Router();

router.get("/", async (req: Request, params: Record<string, string>) => {
        // the name "params" is based on the famous JavaScript library
        // [path-to-regexp](https://github.com/pillarjs/path-to-regexp), which works
        // behind the URLPattern class
    const html = await Deno.readTextFile("./index.html");
    return new Response(html, { headers: {"content-type": "text/html; charset=utf-8"}});
});


router.get("/:htmlfile.html", async (req: Request, params: Record<string, string>) => {
    logger.info(`htmlfile: ${params.htmlfile}`);
    try {
        const html = await Deno.readTextFile(`${params.htmlfile}.html`);
        return new Response(html, { headers: {"content-type": "text/html; charset=utf-8"}});
    } catch (e) {
        logger.error(e.message);
    }
});

router.get("/scripts/:jsfile", async (req: Request, params: Record<string, string>) => {
    logger.info(`params.jsfile: ${params.jsfile}`);
    try {
        const js = await Deno.readTextFile(`scripts/${params.jsfile}`);
        return new Response(js, { headers: {"content-type": "text/javascript"}});
    } catch (e) {
        logger.error(e.message);
    }
});


// the doc of URLPattern --- https://developer.mozilla.org/en-US/docs/Web/API/URLPattern/URLPattern
router.get("/:jsonfile.json", async (req: Request, params: Record<string, string>) => {
        logger.info(`jsonfile: ${params.jsonfile}`);
        try {
            const json = await Deno.readTextFile(`${params.jsonfile}.json`);
            return new Response(json, { headers: {"content-type": "application/json"}});
        } catch (e) {
            logger.error(e.message);
        }
    });


router.get("/bookmark", async (req: Request, params: Record<string, string>) => {
    try {
        const u = new URL(req.url);
        const bookmarkUrl = u.searchParams.get('url');
        logger.info(`bookmarkUrl : ${bookmarkUrl}`);
        let params = new URLSearchParams();
        params.set('url', bookmarkUrl);
        let text = '';
        const resp = await fetch(`https://b.hatena.ne.jp/entry/jsonlite/?${params.toString()}`).
            then(res => res.text());
        return new Response(resp, 
            { headers:{"Content-Type": "application/json"}});
    } catch (e) {
        logger.error(e.message);
    }
});

router.get("/fetch_basic", async (req: Request, params: Record<string, string>) => {
    try {
        const u = new URL(req.url);
        const name = u.searchParams.get('name');
        let text = '';
        if (name !== '') {
            text = `こんにちは、${name}さん!`
        }
        return new Response(text, 
            { headers:{"Content-Type": "text/plain; charset=utf-8"}});
    } catch (e) {
        logger.error(e.message);
    }
});

router.post("/fetch_post", async (req: Request, params: Record<string, string>) => {
    try {
        const formData = await req.formData();
        const name = formData.get('name');
        let text = '';
        if (name !== '') {
            text = `こんにちは、${formData.get('name')}さん!`
        }
        return new Response(text, 
            { headers: { "Content-Type": "text/plain; charset=utf-8" } });
    } catch (e) {
        logger.error(e.message);
    }
});

router.get("/fetch_query", async (req: Request, params: Record<string, string>) => {
    try {
        const u = new URL(req.url);
        const name = u.searchParams.get('name');
        let text = '';
        if (name !== '') {
            text = `こんにちは、${name}さん!`
        }
        return new Response(text, 
            { headers:{"Content-Type": "text/plain; charset=utf-8"}});
    } catch (e) {
        logger.error(e.message);
    }
});

// https://medium.com/deno-the-complete-reference/handle-file-uploads-in-deno-ee14bd2b16d9
router.post("/fetch_upload", async (req: Request, _params: Record<string, string>) => {
    logger.info(`/fetch_upload ${req.url}`)
    const SAVE_PATH = './uploaded/';
    try {
        const url = new URL(req.url);
        const fileName = url.searchParams.get("filename") || crypto.randomUUID();
        if (!req.body) {
            return new Response(null, { status: 400});
        }
        const reader = req?.body?.getReader();
        if (!existsSync(SAVE_PATH)) {
            await Deno.mkdir(SAVE_PATH)
        }
        const f = await Deno.open(SAVE_PATH + fileName, {
            create: true,
            write: true,
        });
        await Deno.copy(readerFromStreamReader(reader), f);
        await f.close();
        return new Response(`saved ${fileName}`);
    } catch (e) {
        logger.error(e.message);
        return new Response(null, {
            status: STATUS_CODE.InternalServerError,
            statusText: e.message
        })
    }
});

async function reqHandler(req: Request) {
    console.log(`\n[serve.ts#reqHandler] Request:  ${req.method} ${req.url}`);
    return await router.route(req);
}
serve(reqHandler, { port: 3000 });

前述した app.ts と同様に Deno Native Router ライブラリを使ってRequestに反応してResponseを返すコードを束にしています。

《FIN》

2
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?