Slide 1

Slide 1 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. T O R A N O A N A L a b Fresh用ミドルウェアを Fresh 1.3 のプラグインに対応させよう 2023/08/02 toranoana.deno #13 虎の穴ラボ 奥谷 一陽

Slide 2

Slide 2 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. 自己紹介 奥谷 一陽 所属:虎の穴ラボ株式会社 担当:Fantia、とらコインなどの開発 興味:TypeScript、Deno おすすめコンテンツ:   『王様戦隊キングオージャー』 Twitter:@okutann88

Slide 3

Slide 3 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. Fresh 使ってますか? 

Slide 4

Slide 4 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. Fresh 1.3.0 が公開されました

Slide 5

Slide 5 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. Fresh 1.3.0 の変更点(一部抜粋) - 非同期ルートコンポーネントに対応 - Deno.serve をサポート - 新しいリンタールールの適用 - プラグインでルートとミドルウェアを追加できる!!!

Slide 6

Slide 6 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. Fresh 1.2 までのミドルウェア - routes 以下で _middleware.ts にハンドラを渡す // _middleware.ts import { loggerHandler } from "https://deno.land/x/[email protected]/mod.ts"; export const handler = [loggerHandler];

Slide 7

Slide 7 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. Fresh 1.3 からのミドルウェア設定方法 - プラグイン経由でハンドラを渡す /// /// /// /// /// import "$std/dotenv/load.ts"; import { start } from "$fresh/server.ts"; import manifest from "./fresh.gen.ts"; import twindPlugin from "$fresh/plugins/twind.ts"; import twindConfig from "./twind.config.ts"; import { getLoggerHandler } from "https://deno.land/x/fresh-logger/mod.ts"; await start(manifest, { plugins: [ getLoggerHandler(), // <= プラグインに追加 twindPlugin(twindConfig), ], });

Slide 8

Slide 8 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. プラグインの中身を眺める { name: "sameRoutePlugin", middlewares: [{ middleware: { handler: sameHandler }, path: "/", }], routes: [{ path: "/_app", component: sameComponent }], } import twindPlugin from "$fresh/plugins/twind.ts"; import twindConfig from "./twind.config.ts"; import { getLoggerHandler } from "https://deno.land/x/fresh-logger/mod.ts"; await start(manifest, { plugins: [ getLoggerHandler(), // <= プラグインを追加 twindPlugin(twindConfig), ], }); middleware と routes を生やす場合、 middlewares と routesをもったオブジェクトを返せば良い プラグインを複数受け付けた上で、 middleware の中でも複数組み込める

Slide 9

Slide 9 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. プラグインの中身を眺める プラグインの関連の型定義は次のような形 export interface Plugin { name: string; entrypoints?: Record; render?(ctx: PluginRenderContext): PluginRenderResult; renderAsync?(ctx: PluginAsyncRenderContext): Promise; routes?: PluginRoute[]; middlewares?: PluginMiddleware[]; } export interface PluginMiddleware { path: string; middleware: Middleware; } export interface PluginRoute { path: string; component?: ComponentType | ComponentType; handler?: Handler | Handlers; } 参照:https://github.com/denoland/fresh/blob/main/src/server/types.ts

Slide 10

Slide 10 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. 既存のミドルウェアを プラグインに対応させよう

Slide 11

Slide 11 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. プラグイン実装 import {type Plugin, type MiddlewareHandlerContext, type MiddlewareHandler} from "../deps.ts"; import {LogModule, type Logger, type RemoteLogSenderFunction} from "./loggerModule.ts"; export function getLoggerHandler(logSender?: RemoteLogSenderFunction): Plugin { const handler: MiddlewareHandler = async function ( _req: Request, ctx: MiddlewareHandlerContext ) { ctx.state.logger = new LogModule(logSender); return await ctx.next(); }; return { name: "loggerPlugin", middlewares: [ { middleware: { handler: handler as MiddlewareHandler>, }, path: "/", }, ], }; }

Slide 12

Slide 12 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. プラグイン実装 import {type Plugin, type MiddlewareHandlerContext, type MiddlewareHandler} from "../deps.ts"; import {LogModule, type Logger, type RemoteLogSenderFunction} from "./loggerModule.ts"; export function getLoggerHandler(logSender?: RemoteLogSenderFunction): Plugin { const handler: MiddlewareHandler = async function ( _req: Request, ctx: MiddlewareHandlerContext ) { ctx.state.logger = new LogModule(logSender); return await ctx.next(); }; return { name: "loggerPlugin", middlewares: [ { middleware: { handler: handler as MiddlewareHandler>, }, path: "/", }, ], }; } handlerに割り当てると、 型チェックにパスできなくなるので、アサーションを書かざるを得ない

Slide 13

Slide 13 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. 動作確認

Slide 14

Slide 14 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. プラグイン適用 handlerに割り当てると、 型チェックにパスできなくなるので、アサーションを書かざるを得ない /// /// /// /// /// import "$std/dotenv/load.ts"; import { start } from "$fresh/server.ts"; import manifest from "./fresh.gen.ts"; import twindPlugin from "$fresh/plugins/twind.ts"; import twindConfig from "./twind.config.ts"; import { getLoggerHandler } from "https://deno.land/x/fresh-logger/mod.ts"; await start(manifest, { plugins: [ getLoggerHandler(), // <= プラグインに追加 twindPlugin(twindConfig), ], });

Slide 15

Slide 15 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. 動作確認 handlerに割り当てると、 型チェックにパスできなくなるので、アサーションを書かざるを得ない // routes/index.tsx import { Head } from "$fresh/runtime.ts"; import { useSignal } from "@preact/signals"; import Counter from "../islands/Counter.tsx"; import {type Logger} from "https://deno.land/x/fresh-logger/mod.ts"; export default function Home({state}: {state: Logger} ) { const count = useSignal(3); state.logger.log("Home"); // <= 導入したロガー return ( <>
{}
); } // routes/_app.tsx import { Appprops } from "$fresh/server.ts"; import {type Logger} from "https://deno.land/x/fresh-logger/mod.ts"; export default function App({Conponent, state}: Appprops & {state: Logger} ) { state.logger.log("App"); // <= 導入したロガー return ( <> ); }

Slide 16

Slide 16 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. 動作確認 ロガーがプラグインで適用できたことが確認できる。 $ deno task start Task start deno run -A --watch=static/,routes/ dev.ts Watcher Process started. The manifest has been generated for 5 routes and 1 islands. 🍋 Fresh ready Local: http://localhost:8000/ { log_id: "5a76vAfNDkRkCU_IulOhk", level: "log", body: "App" } { log_id: "5a76vAfNDkRkCU_IulOhk", level: "log", body: "Home" } { log_id: "h2RlWaAsTkAIT2s7iBowj", level: "log", body: "App" } { log_id: "h2RlWaAsTkAIT2s7iBowj", level: "log", body: "Home" }

Slide 17

Slide 17 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. まとめ - Fresh に プラグインで routes と middleware を生やす機能が追加 - 既存の middleware も比較的簡単にプラグイン化できる - プラグインで基盤機能をモジュールで提供できそう - 認証機能を一つのプラグインで... - ログインページ:routes を生やす - 検証処理:middleware を生やす ができる!!

Slide 18

Slide 18 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. おまけ - ミドルウェアの型がうまく適合しない件 issue を出してみた。 参照: https://github.com/denoland/fresh/issues/1531

Slide 19

Slide 19 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. おまけ - ミドルウェアの型がうまく適合しない件 返ってきた プルリクも立ったマージされたので、 近々 as MiddlewareHandler> は書かなくてよくなりそう。

Slide 20

Slide 20 text

Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. ありがとうございました!