Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
BFFなしでも型を諦めない_APIドキュメントから始めるTypeScript型自動生成.pdf
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
shinya tsuchida
June 25, 2026
350
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
BFFなしでも型を諦めない_APIドキュメントから始めるTypeScript型自動生成.pdf
shinya tsuchida
June 25, 2026
Featured
See All Featured
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
570
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
850
Exploring anti-patterns in Rails
aemeredith
4
540
Why Our Code Smells
bkeepers
PRO
340
58k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
520
We Are The Robots
honzajavorek
0
400
Raft: Consensus for Rubyists
vanstee
142
7.8k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.6k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3.4k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
1k
Practical Orchestrator
shlominoach
192
12k
Transcript
2026.06.11 BFFなしでも型を諦めない!APIドキュメント から始めるTypeScript型⾃動⽣成
2 © Safie Inc.| ⾃⼰紹介 2 © Safie Inc.| 名前
⼟⽥ 真哉 出⾝ 熊本県⽟名市 経歴 2023年4⽉ 新卒1期⽣としてセーフィー株式会社に⼊社 フロントエンドチームに配属され3年ほどして最近はSafie Entranceというプロダク トをフルスタックに開発中 趣味‧特技 バドミントン‧ジョギング 年齢 27歳
3 © Safie Inc.| 会社紹介 データ蓄積・活用 防犯 顧客分析 店舗内映像 接客映像
好事例店舗の 分析/共有 現場映像 デジタルツイン 遠隔監視/操作 クラウド録画サービス セーフィー株式会社(Safie Inc.) 社員数 616名(連結∕2026年4⽉1⽇時点) ビジョン 映像から未来を作る 会社名
4 © Safie Inc.| セーフィーのバックエンド事情 PythonやGoで実装 フロントエンドはTypescriptであるが、型情報の共有が 困難
APIドキュメントの形式が乱⽴ SwaggerスキーマやreStructuredText(.rst)形式など が混在し、統⼀的な管理が課題 BFF(Backend For Frontend)ではなくても、型安全にAPIを利⽤したい
5 © Safie Inc.| Safie APIパッケージ セーフィーの API をフロントエンドから型安全に呼び出すための社内ライブラリ
3層構造のクラス設計 通信‧認証‧エンドポイント呼び出しの責務を 分離 サービス層を「型のついた API クライアント」 として薄く保つ APIドキュメントからの⾃動⽣成 薄く保ったサービス層を数百のエンドポイント 分機械的に量産する ツール名: safieAPICodeGenerator
6 © Safie Inc.| 3層構造 3層構造にしたことによる恩恵 横断的関⼼事の共通化 認証‧XSRF‧タイムゾーン‧ case
変換を上の層に集約 テスト容易性 通信層は interface 経由で DI モックを差し込むだけ サービス層が極端に薄い テンプレート同然 → ⾃動⽣成と相性が良い 層 役割 通信層 HTTP 共通処理を担う(axiosの設定とか)。interface で切ってあるのでテスト時はモックに差し替え可能 ベース層 API バージョンごとのベースクラス。認証‧XSRF‧タイムゾーンなど API バージョン共通の前処理を吸収 サービス層 ベース層を継承して、各エンドポイントに対応するメソッドだけを並べる。1 エンドポイント = 1 メソッド
7 © Safie Inc.| 3層構造 export class AppV1CamerasServiceClient extends AppV1RequestClient
implements AppV1CamerasService { /** * @description カメラの一覧を返します * @docs https://internal-api-docs.example.com/webapi/app-v1/cameras/list.html */ async fetchAppV1Cameras( params: ReqObjAppV1FetchCameras, ): Promise<ResObjAppV1FetchCameras> { return await this.get<ResObjAppV1FetchCameras>({ url: "/app/v1/cameras", params: params, }); } }
8 © Safie Inc.| 3層構造 通信層 ベース層 サービス層
9 © Safie Inc.|9 © Safie Inc.| コード⾃動⽣成ツール: safieApiCodeGenerator セーフィーはマイクロサービスがいくつもあり、エンドポイントは100個以上存在する
⼿書きでやろうとすると次の問題が⽣じる 命名揺れ fetchUserList / listUsers... 写経ミス 名前の綴りやurlなどのtypo に気づかない safieApiCodeGeneratorというTypescript⾃作ツールを使⽤ STEP 1 PlaywrightでAPIドキュメントを開く STEP 2 HTMLからメタ情報を抽出する STEP 3 テンプレートに流し込んでTypescriptコードを出⼒する
10 © Safie Inc.| APIドキュメントをPlaywrightで開く セーフィーの API ドキュメントは社内認証付き。 普通の fetch
では取得できないため、以下の⼿順でアクセスします。 STEP 1Playwright で Chromium を起動 STEP 2ログイン済みの Cookie を注⼊ STEP 3対象の API ドキュメントページに遷移 ⾃動⽣成 Step 1 / 3
11 © Safie Inc.| HTML からメタ情報を抽出する API バージョンによってドキュメントの HTML 構造が違うため、複数のセレクタを順に試すフォールバックを実装
命名規則を辞書で機械化する // AppV1 系 let dom = await page.$("section#id2 table tr:nth-child(2) td:nth-child(2)"); // EnterpriseV1 系 dom = await page.$("section#id3 dl dd p"); const PREFIX = { GET: "fetch", POST: "create", PUT: "update", PATCH: "update", DELETE: "delete", }; ⾃動⽣成 Step 2 / 3
テンプレートに流し込んでTypeScriptを出⼒する テンプレートが1個で済むのは、まさにベース層がほぼ全部をやってくれているから。 this.get などを呼ぶだけの薄いコードがエンドポイント数ぶん⾃動で並びます。 © Safie Inc. | 11 export
const METHOD_TEMPLATE = ` /** * @description {$API_DESCRIPTION} * @docs {$API_DOCUMENT_URL} */ async {$METHOD_NAME}(params: {$REQ_OBJ_NAME}): Promise<{$RES_OBJ_NAME}> { return await this.{$HTTP_METHOD}<{$RES_OBJ_NAME}>({ url: '{$API_PATH}', {$PARAM_KEY_NAME}: params, }) } `; 抽出した情報を下記のテンプレートリテラルに流し込みます ⾃動⽣成 Step 3 / 3
13 © Safie Inc.|13 © Safie Inc.| ⼿動定義の理由 • 「optionalの表記揺れ」「enumの網羅性」「ネスト
構造」などがドキュメントによってまちまち。 • TypeScriptの型に落とすには情報が不⾜している。 やりかた • quicktype などのWebツールを活⽤。 • APIレスポンスのサンプル(JSON)から⽣成された 型を⼿動⼊⼒。 export type ReqObjAppV1FetchCameras = { // TODO: 型を定義する }; リクエスト/レスポンスの型本体 ReqObjXxx や ResObjXxx の中⾝(プロパティ)は⾃動では作っていない
14 © Safie Inc. | 13 クライアント側の使い⽅ エンドポイントが何百個増えようと、呼び出し側のコードはこの形のまま。 「型のついた API
を、毎回同じ書き⽅で呼ぶことができる」 // サービスのインスタンス化 const cameraService = new AppV1CamerasServiceClient( new AxiosRequestClient({ contentType: 'application/json', axiosConfig: { ... } }) ) // アプリ内での呼び出し方( params に型補完が効く)、resにも型がつく const res = await cameraService.fetchAppV1Cameras({ /* params */ })
© Safie Inc. | 15 まとめ • 3層構造による責務の分離 • APIドキュメントからコード⾃動⽣成
TypeScriptライブラリとして フロントエンドから型安全にAPI呼び出しを実現
16 © Safie Inc.| ご清聴ありがとうございました Thank you!