backend-dev-guidelines について
Langfuseのweb、worker、sharedパッケージ向けに、バックエンド開発の指針をまとめたスキルです。ルーティング、サービス層、データアクセス、検証、可観測性、テストの実装パターンを扱います。
主な機能
- 01tRPCとREST APIの規約
- 02BullMQワーカーのパターン
- 03サービス層とリポジトリの分離
- 04Zodによる入力検証
- 05データベースの分離とアクセス
- 06可観測性とテストの指針
活用シーン
- 01tRPC機能や公開エンドポイントの追加
- 02BullMQキュープロセッサーの実装
- 03データベースやサービスロジックの変更
- 04バックエンド機能や修正のテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ovachiever/ droid-tings --skill backend-dev-guidelinesインストール後、Claude Code で/backend-dev-guidelinesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
resources/architecture-overview.mdconfiguration.mddatabase-patterns.mdmiddleware-guide.mdrouting-and-controllers.mdservices-and-repositories.mdtesting-guide.md詳細
バックエンド開発ガイドライン
目的
Next.js 14、tRPC、BullMQ、TypeScript のパターンを使用する Langfuse のバックエンドパッケージ(web、worker、packages/shared)全体で、一貫性とベストプラクティスを確立します。
このスキルを使用するタイミング
以下の作業時に自動的に有効になります。
- tRPC ルーターとプロシージャの作成または変更
- パブリック API エンドポイント(REST)の作成または変更
- BullMQ キューのコンシューマーとプロデューサーの作成または変更
- ビジネスロジックを含むサービスの構築
- API リクエストの認証
- エンタイトルメントに基づくリソースへのアクセス
- ミドルウェア(tRPC、NextAuth、パブリック API)の実装
- Prisma(PostgreSQL)または ClickHouse を使用したデータベース操作
- OpenTelemetry、DataDog、logger、traceException を使用したオブザーバビリティ
- Zod v4 による入力検証
- 環境変数による環境設定
- バックエンドのテストとリファクタリング
クイックスタート
UI:新しい tRPC 機能のチェックリスト(Web)
- ルーター:
features/[feature]/server/*Router.tsに定義します - プロシージャ:適切なプロシージャタイプ(protected、public)を使用します
- 認証:ミドルウェアを介して JWT 認可を使用します。
- エンタイトルメントの確認:リソースとロールに基づいてリソースにアクセスします
- 検証:入力用の Zod v4 スキーマ
- サービス:サービスファイルにビジネスロジックを記述します
- エラー処理:traceException ラッパーを使用します
- テスト:
__tests__/に単体テストと統合テストを作成します - 設定:env.mjs 経由でアクセスします
SDK:新しいパブリック API エンドポイントのチェックリスト(Web)
- ルートファイル:
pages/api/public/に作成します - ラッパー:
withMiddlewaresとcreateAuthedProjectAPIRouteを使用します - 型:
features/public-api/types/に定義します - 認証:Basic 認証による認可
- 検証:クエリ/ボディ/レスポンス用の Zod スキーマ
- バージョン管理:API パスと、クエリ/ボディ/レスポンス用の Zod スキーマでバージョンを管理します
- テスト:
__tests__/async/にエンドツーエンドテストを追加します
新しいキュープロセッサーのチェックリスト(Worker)
- プロセッサー:
worker/src/queues/に作成します - キューの型:
packages/shared/src/server/queuesにキューの型を作成します - サービス:
features/またはworker/src/features/にビジネスロジックを記述します - エラー処理:キュー処理を失敗させるエラーと、成功イベントにつながるエラーを区別します。
- キューの登録:app.ts の WorkerManager に追加します
- テスト:worker に vitest テストを追加します
アーキテクチャの概要
階層型アーキテクチャ
# Web Package (Next.js 14)
┌─ tRPC API ──────────────────┐ ┌── Public REST API ──────────┐
│ │ │ │
│ HTTP Request │ │ HTTP Request │
│ ↓ │ │ ↓ │
│ tRPC Procedure │ │ withMiddlewares + │
│ (protectedProjectProcedure)│ │ createAuthedProjectAPIRoute│
│ ↓ │ │ ↓ │
│ Service (business logic) │ │ Service (business logic) │
│ ↓ │ │ ↓ │
│ Prisma / ClickHouse │ │ Prisma / ClickHouse │
│ │ │ │
└─────────────────────────────┘ └─────────────────────────────┘
↓
[optional]: Publish to Redis BullMQ queue
↓
┌─ Worker Package (Express) ──────────────────────────────────┐
│ │
│ BullMQ Queue Job │
│ ↓ │
│ Queue Processor (handles job) │
│ ↓ │
│ Service (business logic) │
│ ↓ │
│ Prisma / ClickHouse │
│ │
└─────────────────────────────────────────────────────────────┘
主な原則:
- Web:UI 用の tRPC プロシージャ、または SDK 用のパブリック API ルート → サービス → データベース
- Worker:キュープロセッサー → サービス → データベース
- packages/shared:Web と Worker の共有コード
詳しくは、architecture-overview.md を参照してください。
ディレクトリ構成
Web パッケージ(/web/)
web/src/
├── features/ # Feature-organized code
│ ├── [feature-name]/
│ │ ├── server/ # Backend logic
│ │ │ ├── *Router.ts # tRPC router
│ │ │ └── service.ts # Business logic
│ │ ├── components/ # React components
│ │ └── types/ # Feature types
├── server/
│ ├── api/
│ │ ├── routers/ # tRPC routers
│ │ ├── trpc.ts # tRPC setup & middleware
│ │ └── root.ts # Main router
│ ├── auth.ts # NextAuth.js config
│ └── db.ts # Database client
├── pages/
│ ├── api/
│ │ ├── public/ # Public REST APIs
│ │ └── trpc/ # tRPC endpoint
│ └── [routes].tsx # Next.js pages
├── __tests__/ # Jest tests
│ └── async/ # Integration tests
├── instrumentation.ts # OpenTelemetry (FIRST IMPORT)
└── env.mjs # Environment config
Worker パッケージ(/worker/)
worker/src/
├── queues/ # BullMQ processors
│ ├── evalQueue.ts
│ ├── ingestionQueue.ts
│ └── workerManager.ts
├── features/ # Business logic
│ └── [feature]/
│ └── service.ts
├── instrumentation.ts # OpenTelemetry (FIRST IMPORT)
├── app.ts # Express setup + queue registration
├── env.ts # Environment config
└── index.ts # Server start
共有パッケージ(/packages/shared/)
shared/src/
├── server/ # Server utilities
│ ├── auth/ # Authentication helpers
│ ├── clickhouse/ # ClickHouse client & schema
│ ├── instrumentation/ # OpenTelemetry helpers
│ ├── llm/ # LLM integration utilities
│ ├── redis/ # Redis queues & cache
│ ├── repositories/ # Data repositories
│ ├── services/ # Shared services
│ ├── utils/ # Server utilities
│ ├── logger.ts
│ └── queues.ts
├── encryption/ # Encryption utilities
├── features/ # Feature-specific code
├── tableDefinitions/ # Table schemas
├── utils/ # Shared utilities
├── constants.ts
├── db.ts # Prisma client
├── env.ts # Environment config
└── index.ts # Main exports
インポートパス(package.json の exports):
共有パッケージでは、用途別に特定のインポートパスを公開しています。
| インポートパス | 対応先 | 用途 |
|---|---|---|
@langfuse/shared | dist/src/index.js | 一般的な型、スキーマ、ユーティリティ、定数 |
@langfuse/shared/src/db | dist/src/db.js | Prisma クライアントとデータベースの型 |
@langfuse/shared/src/server | dist/src/server/index.js | サーバー側のユーティリティ(キュー、認証、サービス、計装) |
@langfuse/shared/src/server/auth/apiKeys | dist/src/server/auth/apiKeys.js | API キー管理ユーティリティ |
@langfuse/shared/encryption | dist/src/encryption/index.js | 暗号化と署名のユーティリティ |
使用例:
// General imports - types, schemas, constants, interfaces
import {
CloudConfigSchema,
StringNoHTML,
AnnotationQueueObjectType,
type APIScoreV2,
type ColumnDefinition,
Role,
} from "@langfuse/shared";
// Database - Prisma client and types
import { prisma, Prisma, JobExecutionStatus } from "@langfuse/shared/src/db";
import { type DB as Database } from "@langfuse/shared";
// Server utilities - queues, services, auth, instrumentation
import {
logger,
instrumentAsync,
traceException,
redis,
getTracesTable,
StorageService,
sendMembershipInvitationEmail,
invalidateApiKeysForProject,
recordIncrement,
recordHistogram,
} from "@langfuse/shared/src/server";
// API key management (specific path)
import { createAndAddApiKeysToDb } from "@langfuse/shared/src/server/auth/apiKeys";
// Encryption utilities
import { encrypt, decrypt, sign, verify } from "@langfuse/shared/encryption";
配置先:
共有パッケージは、ウェブパッケージとワーカーパッケージの両方で使用する型、ユーティリティ、サーバーコードを提供します。フロントエンドとバックエンドのアクセスを制御する5 つのエクスポートパスがあります。
| インポートパス | 使用方法 | 含まれるもの |
|---|---|---|
@langfuse/shared | ✅ フロントエンド+バックエンド | Prisma の型、Zod スキーマ、定数、テーブル定義、ドメインモデル、ユーティリティ |
@langfuse/shared/src/db | 🔒 バックエンドのみ | Prisma クライアントインスタンス |
@langfuse/shared/src/server | 🔒 バックエンドのみ | サービス、リポジトリ、キュー、認証、ClickHouse、LLM 統合、計装 |
@langfuse/shared/src/server/auth/apiKeys | 🔒 バックエンドのみ | API キー管理(循環依存を避けるため分離) |
@langfuse/shared/encryption | 🔒 バックエンドのみ | データベースフィールドの暗号化/復号 |
命名規則:
- tRPC ルーター:
camelCaseRouter.ts-datasetRouter.ts - サービス:機能ディレクトリ内の
service.ts - キュープロセッサー:
camelCaseQueue.ts-evalQueue.ts - 公開 API:
kebab-case.ts-dataset-items.ts
コア原則
1. tRPC プロシージャはサービスに処理を委譲します
// ❌ NEVER: Business logic in procedures
export const traceRouter = createTRPCRouter({
byId: protectedProjectProcedure
.input(z.object({ traceId: z.string() }))
.query(async ({ input, ctx }) => {
// 200 lines of logic here
}),
});
// ✅ ALWAYS: Delegate to service
export const traceRouter = createTRPCRouter({
byId: protectedProjectProcedure
.input(z.object({ traceId: z.string() }))
.query(async ({ input, ctx }) => {
return await getTraceById(input.traceId);
}),
});
2. 設定には env.mjs 経由でアクセスし、process.env は絶対に使用しないでください
// ❌ NEVER (except in env.mjs itself)
const dbUrl = process.env.DATABASE_URL;
// ✅ ALWAYS
import { env } from "@/src/env.mjs";
const dbUrl = env.DATABASE_URL;
3. すべての入力を Zod v4 で検証します
import { z } from "zod/v4";
const schema = z.object({
email: z.string().email(),
projectId: z.string(),
});
const validated = schema.parse(input);
4. サービスでは、単純な CRUD には Prisma を直接使用し、複雑なクエリにはリポジトリを使用します
// Services use Prisma directly for simple CRUD
import { prisma } from "@langfuse/shared/src/db";
const dataset = await prisma.dataset.findUnique({
where: { id: datasetId, projectId }, // Always filter by projectId for tenant isolation
});
// Or use repositories for complex queries (traces, observations, scores)
import { getTracesTable } from "@langfuse/shared/src/server";
const traces = await getTracesTable({
projectId,
filter: [...],
limit: 1000,
});
6. 可観測性:OpenTelemetry + DataDog(バックエンドでは Sentry を使用しない)
Langfuse はバックエンドの可観測性に OpenTelemetry を使用し、トレースとログを DataDog に送信します。
// Import observability utilities
import {
logger, // Winston logger with OpenTelemetry/DataDog context
traceException, // Record exceptions to OpenTelemetry spans
instrumentAsync, // Create instrumented spans
} from "@langfuse/shared/src/server";
// Structured logging (includes trace_id, span_id, dd.trace_id)
logger.info("Processing dataset", { datasetId, projectId });
logger.error("Failed to create dataset", { error: err.message });
// Record exceptions to OpenTelemetry (sent to DataDog)
try {
await operation();
} catch (error) {
traceException(error); // Records to current span
throw error;
}
// Instrument critical operations (all API routes auto-instrumented)
const result = await instrumentAsync(
{ name: "dataset.create" },
async (span) => {
span.setAttributes({ datasetId, projectId });
// Operation here
return dataset;
},
);
注:フロントエンドでは Sentry を使用しますが、バックエンド(tRPC、API ルート、サービス、ワーカー)では OpenTelemetry + DataDog を使用します。
7. 包括的なテストが必須です
新機能やバグ修正には、すべてテストを作成してください。詳しい例はtesting-guide.mdを参照してください。
テストの種類:
| 種類 | フレームワーク | 場所 | 目的 |
|---|---|---|---|
| 統合 | Jest | web/src/__tests__/async/ | API エンドポイント全体のテスト |
| tRPC | Jest | web/src/__tests__/async/ | 認証付きの tRPC プロシージャ |
| サービス | Jest | web/src/__tests__/async/repositories/ | リポジトリ/サービス関数 |
| ワーカー | Vitest | worker/src/__tests__/ | キュープロセッサーとストリーム |
簡単な例:
// Integration Test (Public API)
const res = await makeZodVerifiedAPICall(
PostDatasetsV1Response, "POST", "/api/public/datasets",
{ name: "test-dataset" }, auth
);
expect(res.status).toBe(200);
// tRPC Test
const { caller } = await prepare(); // Creates session + caller
const response = await caller.automations.getAutomations({ projectId });
expect(response).toHaveLength(1);
// Service Test
const result = await getObservationsWithModelDataFromEventsTable({
projectId, filter: [...], limit: 1000, offset: 0
});
expect(result.length).toBeGreaterThan(0);
// Worker Test (vitest)
const stream = await getObservationStream({ projectId, filter: [] });
const rows = [];
for await (const chunk of stream) rows.push(chunk);
expect(rows).toHaveLength(2);
主な原則:
- テスト間の干渉を避けるため、一意の ID(
randomUUID())を使用する - テストデータをクリーンアップするか、一意のプロジェクト ID を使用する
- テストは互いに独立しており、どの順序でも実行できる必要があります
- テストで
pruneDatabaseを使用しない
8. テナントを分離するため、常に projectId でフィルタリングする
// ✅ CORRECT: Filter by projectId for tenant isolation
const trace = await prisma.trace.findUnique({
where: { id: traceId, projectId }, // Required for multi-tenant data isolation
});
// ✅ CORRECT: ClickHouse queries also require projectId
const traces = await queryClickhouse({
query: `
SELECT * FROM traces
WHERE project_id = {projectId: String}
AND timestamp >= {startTime: DateTime64(3)}
`,
params: { projectId, startTime },
});
共通のインポート
// tRPC (Web)
import { z } from "zod/v4";
import {
createTRPCRouter,
protectedProjectProcedure,
} from "@/src/server/api/trpc";
import { TRPCError } from "@trpc/server";
// Database
import { prisma } from "@langfuse/shared/src/db";
import type { Prisma } from "@prisma/client";
// ClickHouse
import {
queryClickhouse,
queryClickhouseStream,
upsertClickhouse,
} from "@langfuse/shared/src/server";
// Observability - OpenTelemetry + DataDog (NOT Sentry for backend)
import {
logger, // Winston logger with OTEL/DataDog trace context
traceException, // Record exceptions to OpenTelemetry spans
instrumentAsync, // Create instrumented spans for operations
} from "@langfuse/shared/src/server";
// Config
import { env } from "@/src/env.mjs"; // web
// or
import { env } from "./env"; // worker
// Public API (Web)
import { withMiddlewares } from "@/src/features/public-api/server/withMiddlewares";
import { createAuthedProjectAPIRoute } from "@/src/features/public-api/server/createAuthedProjectAPIRoute";
// Queue Processing (Worker)
import { Job } from "bullmq";
import { QueueName, TQueueJobTypes } from "@langfuse/shared/src/server";
クイックリファレンス
HTTP ステータスコード
| コード | 用途 |
|---|---|
| 200 | 成功 |
| 201 | 作成済み |
| 400 | 不正なリクエスト |
| 401 | 認証されていません |
| 403 | 禁止 |
| 404 | 見つかりません |
| 500 | サーバーエラー |
参考にできる機能の例
実装パターンを確認するには、既存の Langfuse の機能を参照してください。
- データセット(
web/src/features/datasets/) - tRPC ルーター、パブリック API、サービスを備えた完全な機能 - プロンプト(
web/src/features/prompts/) - バージョン管理とテンプレートを備えた機能 - 評価(
web/src/features/evals/) - ワーカーとの連携を含む複雑な機能 - パブリック API(
web/src/features/public-api/) - ミドルウェアとルートのパターン
避けるべきアンチパターン
❌ ルート/プロシージャにビジネスロジックを記述する
❌ process.envを直接使用する(常に env.mjs/env.ts を使用してください)
❌ エラーハンドリングが不足している
❌ 入力検証がない(常に Zod v4 を使用してください)
❌ テナントスコープのクエリで projectId フィルターが不足している
❌ logger/traceException(OpenTelemetry)の代わりに console.log を使用する
ナビゲーションガイド
| 必要なこと... | 参照先 |
|---|---|
| アーキテクチャを理解する | architecture-overview.md |
| ルート/コントローラーを作成する | routing-and-controllers.md |
| ビジネスロジックを整理する | services-and-repositories.md |
| ミドルウェアを作成する | middleware-guide.md |
| データベースにアクセスする | database-patterns.md |
| 設定を管理する | configuration.md |
| テストを作成する | testing-guide.md |
リソースファイル
architecture-overview.md
3 層アーキテクチャ(tRPC/パブリック API → サービス → データアクセス)、tRPC/パブリック API/ワーカーのリクエストライフサイクル、Next.js 14 のディレクトリ構造、2 種類のデータベースシステム(PostgreSQL + ClickHouse)、関心の分離、複雑なクエリ向けのリポジトリパターン
routing-and-controllers.md
Next.js のファイルベースルーティング、tRPC ルーターのパターン、パブリック REST API ルート、階層化アーキテクチャ(エントリーポイント → サービス → リポジトリ → データベース)、サービスレイヤーの構成、避けるべきアンチパターン
services-and-repositories.md
サービスレイヤーの概要、依存性注入のパターン、シングルトンのパターン、データアクセス向けのリポジトリパターン、サービス設計の原則、キャッシュ戦略、サービスのテスト
middleware-guide.md
tRPC ミドルウェア(withErrorHandling、withOtelInstrumentation、enforceUserIsAuthed)、7 種類の tRPC プロシージャタイプ(publicProcedure、authenticatedProcedure、protectedProjectProcedure など)、Public API ミドルウェア(withMiddlewares、createAuthedProjectAPIRoute)、認証パターン(tRPC の NextAuth、Public API の Basic Auth)
database-patterns.md
デュアルデータベースアーキテクチャ(Prisma 経由の PostgreSQL と直接クライアント経由の ClickHouse)、PostgreSQL の CRUD 操作、ClickHouse のクエリパターン(queryClickhouse、queryClickhouseStream、upsertClickhouse)、複雑なクエリ向けのリポジトリパターン、projectId フィルタリングによるテナント分離、データベースの使い分け
configuration.md
Zod による環境変数の検証、パッケージ固有の設定(t3-oss/env-nextjs を使用する web/env.mjs、worker/env.ts、shared/env.ts)、NEXT_PUBLIC_LANGFUSE_CLOUD_REGION の使用方法、エンタープライズ機能向けの LANGFUSE_EE_LICENSE_KEY、環境変数管理のベストプラクティス
testing-guide.md
統合テスト(makeZodVerifiedAPICall を使用した Public API のテスト)、tRPC テスト(createInnerTRPCContext、appRouter.createCaller)、サービスレベルのテスト(リポジトリ/サービス関数)、ワーカーテスト(ストリームを使用する vitest)、テスト分離の原則、テストの実行(web は Jest、worker は vitest)
関連スキル
- database-verification - カラム名とスキーマの一貫性を検証します
- skill-developer - スキルの作成と管理のためのメタスキルです
スキルのステータス: 完了 ✅ 行数: 約 540 行 段階的開示: リソースファイル 7 個 ✅