Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
TypeScript でバックもやるって実際どう? 実運用で困ったこと3選
Search
Yuichiro SERITA
November 27, 2024
Programming
18
8.9k
TypeScript でバックもやるって実際どう? 実運用で困ったこと3選
ROSCA株式会社さん主催のイベント
フロントからバックエンドまで、TypeScriptでシームレスな開発エクスペリエンスを
で発表させていただいた際に使用したスライドです。
Yuichiro SERITA
November 27, 2024
Tweet
Share
More Decks by Yuichiro SERITA
See All by Yuichiro SERITA
技術的負債と向き合うカイゼン活動を1年続けて分かった "持続可能" なプロダクト開発
yuichiro_serita
0
1.3k
KEPPLE DB リプレースにおける技術的負債のバランス感覚
yuichiro_serita
1
510
Other Decks in Programming
See All in Programming
Giselleで作るAI QAアシスタント 〜 Pull Requestレビューに継続的QAを
codenote
0
330
TerraformとStrands AgentsでAmazon Bedrock AgentCoreのSSO認証付きエージェントを量産しよう!
neruneruo
4
2.2k
Spinner 軸ズレ現象を調べたらレンダリング深淵に飲まれた #レバテックMeetup
bengo4com
1
210
AIの誤りが許されない業務システムにおいて“信頼されるAI” を目指す / building-trusted-ai-systems
yuya4
6
4.1k
Java 25, Nuevas características
czelabueno
0
120
Combinatorial Interview Problems with Backtracking Solutions - From Imperative Procedural Programming to Declarative Functional Programming - Part 2
philipschwarz
PRO
0
130
AI Agent Dojo #4: watsonx Orchestrate ADK体験
oniak3ibm
PRO
0
120
リリース時」テストから「デイリー実行」へ!開発マネージャが取り組んだ、レガシー自動テストのモダン化戦略
goataka
0
150
perlをWebAssembly上で動かすと何が嬉しいの??? / Where does Perl-on-Wasm actually make sense?
mackee
0
260
愛される翻訳の秘訣
kishikawakatsumi
3
360
ゆくKotlin くるRust
exoego
1
180
フルサイクルエンジニアリングをAI Agentで全自動化したい 〜構想と現在地〜
kamina_zzz
0
330
Featured
See All Featured
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
1.8k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
140
A Modern Web Designer's Workflow
chriscoyier
698
190k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
2.8k
The Art of Programming - Codeland 2020
erikaheidi
56
14k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
2
74
Producing Creativity
orderedlist
PRO
348
40k
Paper Plane
katiecoart
PRO
0
45k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
0
130
How To Stay Up To Date on Web Technology
chriscoyier
791
250k
Design in an AI World
tapps
0
100
VelocityConf: Rendering Performance Case Studies
addyosmani
333
24k
Transcript
TypeScript でバックもやるって実際どう? 実運用で困ったこと3選 芹田 悠一郎 @株式会社ケップル KEPPLE CREATORS LAB
• 株式会社ケップル KEPPLE CREATORS LAB 所属 • フルスタック的に Web まわりのエンジニア
をやっています • めちゃくちゃ夜型です。正午の登壇すら 正直言ってちょっとつらい • とにかくコーヒーが好き 芹田 悠一郎 Yuichiro SERITA
前置き
TypeScript でバックもやる
賛否両論
飛び交うポジショントーク
何も信じられない……
立ち位置 • 小さいエンジニア組織 • そのわりに多いプロダクト • つまり全員フルスタックじゃないといけない • フロントが TypeScript
なので全員 TypeScript を使える (共通項) • バックエンドも TypeScript でいいじゃん
立ち位置 • TypeScript のバックエンドに NestJS を採用 • 小規模なもので Hono を使っているものもある
• そんなに特別なことをバックエンドでやっていない DB などから取得したデータを集計して JSON にして返す程度のレベル感
弊社の代表的な構成 フロント BFF データ ソース Next.js NestJS NestJS Hono TS
以外もある
話すこと • TypeScript でバックエンドを運用してて遭遇した困りごと • その困りごとの対処方法
持って帰ってほしいこと • すでに TypeScript でバックエンドを構築している方 → TypeScript でバックエンド構築するとやっぱこうなるんだ、みんな同じだね • これから TypeScript
でバックエンドを構築したい方 → TypeScript でバックエンド構築しても、トラブルってその程度で済むんだー、 へー、なるほどねー
本題
#1 例外が遅い。date-fns が意外と遅い
当時の状況 • とある API のレスポンスが、件数が多いときにやたら遅い • 外部 API から取ってきたデータに対し、フィルタしてソートして先頭1000件を返 すだけの処理
• 数万件くらいのデータがたまにあって、30秒以上かかってタイムアウトする
調査してみる • performance.now() 使って printf デバッグ • 日付の変換処理が遅かった • 外部
API から yyyy-MM-dd の形式で string の日付が返ってくる • Date にして返す
原因のコード import { isValid, parse } from 'date-fns'; import {
zonedTimeToUtc } from 'date-fns-tz'; export function tryParseDateAsJST( dateString: string | null | undefined, ): Date | undefined { if (dateString /= null) { return undefined; } const formats = ['yyyy/MM/dd', 'yyyy/M/d', 'yyyy-MM-dd', 'MM/dd', 'M/d']; // referenceDate は年をとりたいだけなのでタイムゾーン考慮しない const referenceDate = new Date(); for (const format of formats) { try { const zonedDate = parse(dateString, format, referenceDate); if (!isValid(zonedDate)) continue; return zonedTimeToUtc(zonedDate, JST); } catch { // noop } } return undefined; }
原因 • Node.js の例外は遅い。throw して catch するだけで 1 ミリ秒くらい •
date-fns の parse は実は遅い。 0.1ミリ秒の桁 • date-fns-tz (v2) の zonedTimeToUtc も実は遅い。 0.1ミリ秒の桁
対応 • parse するところは手書き const [yyyy, MM, dd] = dateString.split('-');
if (yyyy /= null /& MM /= null /& dd /= null) { return new Date( parseInt(yyyy, 10), parseInt(MM, 10) - 1, parseInt(dd, 10), ); } • zonedTimeToUtc は 1000 件に絞ってからかける → 50倍程度高速化できた
#2 CPU bound な処理
当時の状況 • 原因不明のタイムアウト • バックエンドがまったく応答しなくなる • 鬼のように飛んでくるアラート • heartbeat すら応答しない
当時の状況 • とりあえずコンテナの実行環境のスペックを上げてみたりしたが解決しない • ひたすらログとにらめっこ • エラー発生時に特定の処理が走っていることに気づく
原因 • どうしてもなくせない CPU bound な処理があった • データが増えてきて顕在化した • Node.js
は基本的にシングルプロセス・シングルスレッド • CPU bound な処理をすると他の処理がブロックされてしまう
解決策 • Worker threads を使って別スレッドで処理する ◦ 頻繁に叩かれる API ではなかった •
他にもいろいろ手はある ◦ Lambda に逃す ◦ 別言語の別バイナリに投げる
Worker threads 呼ぶ側 (大枠) import { Worker } from "worker_threads";
new Observable((subscriber) /> { const worker = new Worker(workerFilePath); worker.postMessage(args); worker.on("message", (message) /> { subscriber.next(message); }); worker.on("error", (error) /> subscriber.error(error.message)); worker.on("exit", (code) /> { // exit code を見ていろいろやる処理がありますが省略しています worker.terminate(); subscriber.complete(); }); }).pipe( catchError((err) /> { // worker側のエラーをメインスレッド側でハンドリングしないとメインスレッドが落ちるので注意 return throwError(() /> err); }) );
Worker threads 呼ばれる側 (大枠) import { parentPort } from "worker_threads";
parentPort.once("message", async (arg) /> { // いろいろ処理する // Promise で扱うなら postMessage は 1 回だけ呼び出すほうが面倒がない parentPort.postMessage(result); }
#3 NestJS の気持ちがたまに分からない
#3 NestJS の気持ちがたまに分からない #3.1 前提の話 (なぜ NestJS か)
opinionated なフレームワークがほしい • 強い制約がある代わりに生産性が高いフレームワークを opinionated という • プロダクトを早く届けるにはレールに乗るほうがよい • 必要なものが最初から揃っている
• 規約をゼロから作らなくても 「このフレームワークの作法ではこうだから」 で 決められる
opinionated なフレームワークのデファクトスタンダードがない • Node.js の世界では express のような薄いフレームワークが人気 (たぶん) • Ruby
だったら Ruby on Rails, Java だったら Spring, PHP だったら Laravel, のように、他言語だったら opinionated でデファクトスタンダードな フレームワークがある • Node.js にはデファクトスタンダードがない • 悩んだ結果、NestJS を選択 (2020年くらいから使ってます)
NestJS を実際に使った感想 • REST API と GraphQL をまとめて扱いやすい ◦ 書き味がだいたい同じ、Logger
や Interceptor を共通化できる、などなど • NestJS の作法にしっかり従う必要がある • Spring Boot や ASP.NET Core あたりに触れたことがあると馴染みやすい • Angular に触れたことがあると馴染みやすい
#3 NestJS の気持ちがたまに分からない #3.2 困ったこと その1 RxJS
RxJS とは (ざっくり) • 非同期処理の枠組みで、 Observable と呼ばれる非同期でデータを出力するス トリームが土台になっている • Observable
に対して宣言的に処理を書いていく 例:1,2,3 が流れるストリームを10倍する of(1, 2, 3).pipe(map(x /> 10 * x)) https://rxjs.dev/api/operators/map より抜粋
RxJS が (我々にとって) オーバースペック • RxJS で簡単にできて Promise では面倒なことはたくさんあるが、 ほぼ使っていない
(クライアントがコネクションを切ったら全処理を中断するとか) • 学習コストが高い • 記述量がかなり増えてしまう ◦ 複数の非同期処理が絡むとき、Promise なら await を書けばいいだけだが forkJoin や concatMap が必要 ◦ テストでは毎回 subscribe して done を呼ぶ
Promise を使っていいことにしました • 新たに実装する Resolver, Controller, Service は Promise を使う
• @nestjs/axios の HttpService は引き続き使用する。 firstValueFrom で Observable から Promise に変換する • リクエストのキャンセルなどが必要になったら別途考える
NestJS のリクエストのライフサイクル ここだけ Promise にする Middleware Guard Interceptor Pipe Controller
/ Resolver Service Interceptor Exception filter
RxJS と @nestjs/axios に関する余談 • @nestjs/axios の HttpService は、 Observable
の Teardownlogic でリクエストをキャンセルするように作られている • しかし、その処理は 2020年5月から2024年10月までずっと壊れていた ◦ https://github.com/nestjs/nest/pull/4803 ここで壊れて ◦ https://github.com/nestjs/axios/issues/1217 この issue がきっかけで修正された • Observable の恩恵受けてるユーザーほぼいないのかも……
#3 NestJS の気持ちがたまに分からない #3.3 困ったこと その2 モジュールシステム
NestJS のモジュールシステム • Angular とほぼ同じモジュールシステム • import していないモジュールの Service を
DI して使おうとするとエラー https://docs.nestjs.com/modules より抜粋
モジュールシステムが (我々にとって) オーバースペック • モジュールシステムが必要になるほどの規模のバックエンドを作ることがない • テストを書くときに独特の作法が必要で学習コストが高い ◦ 公式ドキュメントに書いてない (読み取れない)
ことが多い
ドキュメントとコミュニケーションで解決 • 分からないところは NestJS ソースコードを読む • ドキュメントを残す ◦ PR のコメントに書く、コード中にコメントを書く、程度でもよい
• 知見を共有する ◦ レビューやオンボーディングでしっかり共有する
#3 NestJS の気持ちがたまに分からない #3.4 困ったこと その3 Logger が独特
Logger が独特 • デフォルトの ConsoleLogger の日時の出力フォーマットが MM/dd/yyyy, h:mm:ss AA で固定
(例: 11/21/2024, 5:48:32 PM) • 引数が独特すぎて変更できないため、カスタムロガーを実装するにしても苦しい ◦ 最後の引数が string だったら context とみなす、stacktrace は引数の数を2 つにして2つ目に string で入れる、stacktrace かどうかの判定は正規表現で やっている、引数の数が2つではない場合は別の判定、Error オブジェクトを受け取 れるようになっていない、などなど……全部実装する必要がある
日付のフォーマットの対応 • Custom Logger を実装して対応 (まだやってない) ◦ Custom Logger 自体はあるが、ConsoleLogger
に移譲している • 今すごく困っているかと言われると微妙なので、後回しにしている
引数が独特すぎる問題の対応 • 引数を1つだけ渡すようにし、意図しない挙動を踏み抜くことを防ぐ ◦ Node.js の util.inspect を通して string で渡す
• 標準の BaseExceptionFilter など、内部で呼び出しているものに関しては 特になにもしない
おわりに
困ったことが全然出てこなくて資料作成に困った • 3選と銘打ったはいいものの、実運用で困ったことが出てこなくて困った • CPU bound な処理がキツいのなんて最初から分かってることなので、 実際のところ実運用で困ったとは言い難い • NestJS
に対していろいろ言いましたが、何の文句もないフレームワークなんて 存在しないと思う
NestJS は実運用に耐えます • NestJS 嫌いな人がネット上に多い • でも余裕で実運用できます (我々のようなスタートアップにおいては特に) ◦ 20チームで分担して1つの大きなバックエンドを作っていて……
みたいな組織には向かないと思います • バグ踏んで困った回数で言えば Next.js のほうが全然多い • 怖がる必要はない
TypeScript でバックもやるって実際どう?
普通に運用できます!
おわり
エンジニア絶賛募集中です! • TypeScript が好き • スタートアップが好き • 「プロダクトエンジニア」に興味がある ぜひお話ししましょう! https://lab.kepple.co.jp/