Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fresh用ミドルウェアを Fresh 1.3 のプラグインに対応させよう

Fresh用ミドルウェアを Fresh 1.3 のプラグインに対応させよう

Deno 向け WebフレームワークのFreshが Fresh1.3.0にアップグレード。
追加されたプラグインで、routesとmiddleearesを追加する機能を紹介。
既存のmiddleeareをプラグイン化してみた中での知見を紹介

虎の穴ラボ株式会社

August 03, 2023
Tweet

More Decks by 虎の穴ラボ株式会社

Other Decks in Technology

Transcript

  1. 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 虎の穴ラボ 奥谷 一陽
  2. Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. 自己紹介

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

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

    1.3 からのミドルウェア設定方法 - プラグイン経由でハンドラを渡す /// <reference no-default-lib="true" /> /// <reference lib="dom" /> /// <reference lib="dom.iterable" /> /// <reference lib="dom.asynciterable" /> /// <reference lib="deno.ns" /> 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), ], });
  6. 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 の中でも複数組み込める
  7. Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. プラグインの中身を眺める

    プラグインの関連の型定義は次のような形 export interface Plugin { name: string; entrypoints?: Record<string, string>; render?(ctx: PluginRenderContext): PluginRenderResult; renderAsync?(ctx: PluginAsyncRenderContext): Promise<PluginRenderResult>; routes?: PluginRoute[]; middlewares?: PluginMiddleware[]; } export interface PluginMiddleware { path: string; middleware: Middleware; } export interface PluginRoute { path: string; component?: ComponentType<PageProps> | ComponentType<AppProps>; handler?: Handler<any, any> | Handlers<any, any>; } 参照:https://github.com/denoland/fresh/blob/main/src/server/types.ts
  8. 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<Logger> = async function ( _req: Request, ctx: MiddlewareHandlerContext<Logger> ) { ctx.state.logger = new LogModule(logSender); return await ctx.next(); }; return { name: "loggerPlugin", middlewares: [ { middleware: { handler: handler as MiddlewareHandler<Record<"logger", unknown>>, }, path: "/", }, ], }; }
  9. 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<Logger> = async function ( _req: Request, ctx: MiddlewareHandlerContext<Logger> ) { ctx.state.logger = new LogModule(logSender); return await ctx.next(); }; return { name: "loggerPlugin", middlewares: [ { middleware: { handler: handler as MiddlewareHandler<Record<"logger", unknown>>, }, path: "/", }, ], }; } handlerに割り当てると、 型チェックにパスできなくなるので、アサーションを書かざるを得ない
  10. Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. プラグイン適用

    handlerに割り当てると、 型チェックにパスできなくなるので、アサーションを書かざるを得ない /// <reference no-default-lib="true" /> /// <reference lib="dom" /> /// <reference lib="dom.iterable" /> /// <reference lib="dom.asynciterable" /> /// <reference lib="deno.ns" /> 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), ], });
  11. 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 ( <> <Head> </Head> <div class="px-4 py-8 mx-auto bg-[#86efac]"> {<!-- 省略 -->} </div> </> ); } // 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 ( <> <Conponent /> </> ); }
  12. 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" }
  13. Copyright (C) 2023 Toranoana Lab Inc. All Rights Reserved. まとめ

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

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

    - ミドルウェアの型がうまく適合しない件 返ってきた プルリクも立ったマージされたので、 近々 as MiddlewareHandler<Record<"hoge", unknown>> は書かなくてよくなりそう。