Backend Dev Guidelines logo

Backend Dev Guidelines

Community
Microck
backend-dev-guidelines

Comprehensive backend development guide for Langfuse's Next.js 14/tRPC/Express/TypeScript monorepo. Use when creating tRPC routers, public API endpoints, BullMQ queue processors, services, or working with tRPC procedures, Next.js API routes, Prisma database access, ClickHouse analytics queries, Redis queues, OpenTelemetry instrumentation, Zod v4 validation, env.mjs configuration, tenant isolation patterns, or async patterns. Covers layered architecture (tRPC procedures → services, queue processors → services), dual database system (PostgreSQL + ClickHouse), projectId filtering for multi-tenant isolation, traceException error handling, observability patterns, and testing strategies (Jest for web, vitest for worker).

Overview

PublisherMicrock
Repositoryordinary-claude-skills
Skill namebackend-dev-guidelines
Stars
398
Forks
53
Bundled files
1
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 1 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by Microck on GitHub. Read the source before you install it.

Installation

Install the Backend Dev Guidelines AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/Microck/ordinary-claude-skills.git /tmp/ordinary-claude-skills
mkdir -p .claude/skills
cp -r /tmp/ordinary-claude-skills/skills_all/backend-dev-guidelines .claude/skills/backend-dev-guidelines
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Backend Dev Guidelines in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Backend Dev Guidelines on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Backend Dev Guidelines is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Backend Development Guidelines

Purpose

Establish consistency and best practices across Langfuse's backend packages (web, worker, packages/shared) using Next.js 14, tRPC, BullMQ, and TypeScript patterns.

When to Use This Skill

Automatically activates when working on:

  • Creating or modifying tRPC routers and procedures
  • Creating or modifying public API endpoints (REST)
  • Creating or modifying BullMQ queue consumers and producers
  • Building services with business logic
  • Authenticating API requests
  • Accessing resources based on entitlements
  • Implementing middleware (tRPC, NextAuth, public API)
  • Database operations with Prisma (PostgreSQL) or ClickHouse
  • Observability with OpenTelemetry, DataDog, logger, and traceException
  • Input validation with Zod v4
  • Environment configuration from env variables
  • Backend testing and refactoring

Quick Start

UI: New tRPC Feature Checklist (Web)

  • Router: Define in features/[feature]/server/*Router.ts
  • Procedures: Use appropriate procedure type (protected, public)
  • Authentication: Use JWT authorization via middlewares.
  • Entitlement check: Access resources based on resource and role
  • Validation: Zod v4 schema for input
  • Service: Business logic in service file
  • Error handling: Use traceException wrapper
  • Tests: Unit + integration tests in __tests__/
  • Config: Access via env.mjs

SDKs: New Public API Endpoint Checklist (Web)

  • Route file: Create in pages/api/public/
  • Wrapper: Use withMiddlewares + createAuthedProjectAPIRoute
  • Types: Define in features/public-api/types/
  • Authentication: Authorization via basic auth
  • Validation: Zod schemas for query/body/response
  • Versioning: Versioning in API path and Zod schemas for query/body/response
  • Tests: Add end-to-end test in __tests__/async/

New Queue Processor Checklist (Worker)

  • Processor: Create in worker/src/queues/
  • Queue types: Create queue types in packages/shared/src/server/queues
  • Service: Business logic in features/ or worker/src/features/
  • Error handling: Distinguish between errors which should fail queue processing and errors which should result in a succeeded event.
  • Queue registration: Add to WorkerManager in app.ts
  • Tests: Add vitest tests in worker

Architecture Overview

Layered Architecture

# 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                                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Key Principles:

  • Web: tRPC procedures for UI OR public API routes for SDKs → Services → Database
  • Worker: Queue processors → Services → Database
  • packages/shared: Shared code for Web and Worker

See architecture-overview.md for complete details.


Directory Structure

Web Package (/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 Package (/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

Shared Package (/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

Import Paths (package.json exports):

The shared package exposes specific import paths for different use cases:

Import PathMaps ToUse For
@langfuse/shareddist/src/index.jsGeneral types, schemas, utilities, constants
@langfuse/shared/src/dbdist/src/db.jsPrisma client and database types
@langfuse/shared/src/serverdist/src/server/index.jsServer-side utilities (queues, auth, services, instrumentation)
@langfuse/shared/src/server/auth/apiKeysdist/src/server/auth/apiKeys.jsAPI key management utilities
@langfuse/shared/encryptiondist/src/encryption/index.jsEncryption and signature utilities

Usage Examples:

typescript
// 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";

What Goes Where:

The shared package provides types, utilities, and server code used by both web and worker packages. It has 5 export paths that control frontend vs backend access:

Import PathUsageWhat's Included
@langfuse/shared✅ Frontend + BackendPrisma types, Zod schemas, constants, table definitions, domain models, utilities
@langfuse/shared/src/db🔒 Backend onlyPrisma client instance
@langfuse/shared/src/server🔒 Backend onlyServices, repositories, queues, auth, ClickHouse, LLM integration, instrumentation
@langfuse/shared/src/server/auth/apiKeys🔒 Backend onlyAPI key management (separated to avoid circular deps)
@langfuse/shared/encryption🔒 Backend onlyDatabase field encryption/decryption

Naming Conventions:

  • tRPC Routers: camelCaseRouter.ts - datasetRouter.ts
  • Services: service.ts in feature directory
  • Queue Processors: camelCaseQueue.ts - evalQueue.ts
  • Public APIs: kebab-case.ts - dataset-items.ts

Core Principles

1. tRPC Procedures Delegate to Services

typescript
// ❌ 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. Access Config via env.mjs, NEVER process.env

typescript
// ❌ 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. Validate ALL Input with Zod v4

typescript
import { z } from "zod/v4";

const schema = z.object({
  email: z.string().email(),
  projectId: z.string(),
});
const validated = schema.parse(input);

4. Services Use Prisma Directly for Simple CRUD or Repositories for Complex Queries

typescript
// 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. Observability: OpenTelemetry + DataDog (Not Sentry for Backend)

Langfuse uses OpenTelemetry for backend observability, with traces and logs sent to DataDog.

typescript
// 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;
  },
);

Note: Frontend uses Sentry, but backend (tRPC, API routes, services, worker) uses OpenTelemetry + DataDog.

7. Comprehensive Testing Required

Write tests for all new features and bug fixes. See testing-guide.md for detailed examples.

Test Types:

TypeFrameworkLocationPurpose
IntegrationJestweb/src/__tests__/async/Full API endpoint testing
tRPCJestweb/src/__tests__/async/tRPC procedures with auth
ServiceJestweb/src/__tests__/async/repositories/Repository/service functions
WorkerVitestworker/src/__tests__/Queue processors & streams

Quick Examples:

typescript
// 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);

Key Principles:

  • Use unique IDs (randomUUID()) to avoid test interference
  • Clean up test data or use unique project IDs
  • Tests must be independent and runnable in any order
  • Never use pruneDatabase in tests

8. Always Filter by projectId for Tenant Isolation

typescript
// ✅ 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 },
});

Common Imports

typescript
// 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";

Quick Reference

HTTP Status Codes

CodeUse Case
200Success
201Created
400Bad Request
401Unauthorized
403Forbidden
404Not Found
500Server Error

Example Features to Reference

Reference existing Langfuse features for implementation patterns:

  • Datasets (web/src/features/datasets/) - Complete feature with tRPC router, public API, and service
  • Prompts (web/src/features/prompts/) - Feature with versioning and templates
  • Evaluations (web/src/features/evals/) - Complex feature with worker integration
  • Public API (web/src/features/public-api/) - Middleware and route patterns

Anti-Patterns to Avoid

❌ Business logic in routes/procedures ❌ Direct process.env usage (always use env.mjs/env.ts) ❌ Missing error handling ❌ No input validation (always use Zod v4) ❌ Missing projectId filter on tenant-scoped queries ❌ console.log instead of logger/traceException (OpenTelemetry)


Navigation Guide

Need to...Read this
Understand architecturearchitecture-overview.md
Create routes/controllersrouting-and-controllers.md
Organize business logicservices-and-repositories.md
Create middlewaremiddleware-guide.md
Database accessdatabase-patterns.md
Manage configconfiguration.md
Write teststesting-guide.md

Resource Files

architecture-overview.md

Three-layer architecture (tRPC/Public API → Services → Data Access), request lifecycle for tRPC/Public API/Worker, Next.js 14 directory structure, dual database system (PostgreSQL + ClickHouse), separation of concerns, repository pattern for complex queries

routing-and-controllers.md

Next.js file-based routing, tRPC router patterns, Public REST API routes, layered architecture (Entry Points → Services → Repositories → Database), service layer organization, anti-patterns to avoid

services-and-repositories.md

Service layer overview, dependency injection patterns, singleton patterns, repository pattern for data access, service design principles, caching strategies, testing services

middleware-guide.md

tRPC middleware (withErrorHandling, withOtelInstrumentation, enforceUserIsAuthed), seven tRPC procedure types (publicProcedure, authenticatedProcedure, protectedProjectProcedure, etc.), Public API middleware (withMiddlewares, createAuthedProjectAPIRoute), authentication patterns (NextAuth for tRPC, Basic Auth for Public API)

database-patterns.md

Dual database architecture (PostgreSQL via Prisma + ClickHouse via direct client), PostgreSQL CRUD operations, ClickHouse query patterns (queryClickhouse, queryClickhouseStream, upsertClickhouse), repository pattern for complex queries, tenant isolation with projectId filtering, when to use which database

configuration.md

Environment variable validation with Zod, package-specific configs (web/env.mjs with t3-oss/env-nextjs, worker/env.ts, shared/env.ts), NEXT_PUBLIC_LANGFUSE_CLOUD_REGION usage, LANGFUSE_EE_LICENSE_KEY for enterprise features, best practices for env management

testing-guide.md

Integration tests (Public API with makeZodVerifiedAPICall), tRPC tests (createInnerTRPCContext, appRouter.createCaller), service-level tests (repository/service functions), worker tests (vitest with streams), test isolation principles, running tests (Jest for web, vitest for worker)


Related Skills

  • database-verification - Verify column names and schema consistency
  • skill-developer - Meta-skill for creating and managing skills

Skill Status: COMPLETE ✅ Line Count: ~540 lines Progressive Disclosure: 7 resource files ✅

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Backend Dev Guidelines AI skill do?

Comprehensive backend development guide for Langfuse's Next.js 14/tRPC/Express/TypeScript monorepo. Use when creating tRPC routers, public API endpoints, BullMQ queue processors, services, or working with tRPC procedures, Next.js API routes, Prisma database access, ClickHouse analytics queries, Redis queues, OpenTelemetry instrumentation, Zod v4 validation, env.mjs configuration, tenant isolation patterns, or async patterns. Covers layered architecture (tRPC procedures → services, queue processors → services), dual database system (PostgreSQL + ClickHouse), projectId filtering for multi-ten...

Why use Backend Dev Guidelines on TypingMind?

Because you install it once and use it with any model. Backend Dev Guidelines is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Backend Dev Guidelines in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/Microck/ordinary-claude-skills/tree/main/skills_all/backend-dev-guidelines. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Backend Dev Guidelines?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Backend Dev Guidelines?

As many as you like. As long as a model supports skills, you can use Backend Dev Guidelines with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Backend Dev Guidelines AI skill free?

It is published on GitHub by Microck. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇