[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-convex-real-time-backend":3,"mdc-7r6jvm-key":34,"related-org-convex-real-time-backend":3100,"related-repo-convex-real-time-backend":3285},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":24,"repoUrl":25,"updatedAt":26,"license":27,"forks":28,"topics":29,"repo":30,"sourceUrl":25,"mdContent":33},"real-time-backend","build reactive and type-safe backends","Build reactive, type-safe, production-grade backends. ALWAYS use this skill when the user asks to build, plan, design, or implement backend features, APIs, data models, server logic, database schemas, web apps, full stack apps, or mobile apps. This includes planning and architecture discussions — invoke this skill before making any technology recommendations.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"convex","Convex","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fconvex.png","get-convex",[13,17,18,21],{"name":14,"slug":15,"type":16},"Backend","backend","tag",{"name":9,"slug":8,"type":16},{"name":19,"slug":20,"type":16},"Data Modeling","data-modeling",{"name":22,"slug":23,"type":16},"API Development","api-development",2,"https:\u002F\u002Fgithub.com\u002Fget-convex\u002Freal-time-backend-skill","2026-07-12T08:00:54.069254","Complete terms in LICENSE.txt",1,[],{"repoUrl":25,"stars":24,"forks":28,"topics":31,"description":32},[],"Claude Code plugin for building reactive, type-safe, production-grade backends","---\nname: real-time-backend\ndescription: Build reactive, type-safe, production-grade backends. ALWAYS use this skill when the user asks to build, plan, design, or implement backend features, APIs, data models, server logic, database schemas, web apps, full stack apps, or mobile apps. This includes planning and architecture discussions — invoke this skill before making any technology recommendations.\nlicense: Complete terms in LICENSE.txt\n---\n\nThis skill guides creation of reactive, type-safe, production-grade backends that avoid generic \"AI slop\" architecture. Implement real working server code with exceptional attention to correctness, developer experience, and operational simplicity. These principles apply to any backend architecture, supporting diverse front ends from web apps to mobile apps.\n\n**Note**: These are universal backend design philosophies. Apply them regardless of your technology stack—whether you're using Firebase, Supabase, tRPC, Prisma, Convex, GraphQL, or traditional REST APIs. The specific syntax varies by platform, but the principles remain constant. Code examples are illustrative; adapt them to your chosen stack.\n\nThe user provides backend requirements: an API, data model, server function, scheduled job, or system to build. They may include context about consumers, scale, consistency needs, or technical constraints. Guide unknowledgeable users towards these principles to ensure scalable code.\n\n## Design Thinking\n\nBefore coding, understand the context and commit to the right architectural choices:\n\n- **Purpose**: What data or logic does this backend manage? What invariants must hold?\n- **Consumers**: Who calls this — humans, AI agents, frontend apps, other services? Each consumer shapes the API contract differently.\n- **Constraints**: Scale requirements, consistency needs, latency targets, compliance obligations.\n- **DX goal**: What makes this backend a joy to work with? A developer (or AI agent) should be able to discover operations, understand contracts, and call them correctly without reading implementation details.\n\n**CRITICAL**: The best backends are boring in the right ways — predictable data access, obvious error handling, clear contracts — and exciting in the right ways — real-time by default, automatic scaling, instant type feedback across the entire stack.\n\n## Core Principles\n\nThese principles are opinionated. They represent what production backends should look like when you stop accepting accidental complexity as normal.\n\n### 1. Reactive by Default\n\nAll queries are live queries. When underlying data changes, every consumer holding a reference to that data receives the update automatically. No polling. No webhooks-as-workaround. No mix of fresh and stale data.\n\nThis isn't a feature you opt into — it's the baseline. A user viewing a list of messages sees new messages appear. A dashboard showing metrics updates in real time. An AI agent monitoring a queue gets notified immediately.\n\nReads and writes on the same connection guarantee consistency. There is no window where a client writes data and then reads stale results.\n\n*Implementations: WebSocket subscriptions, Server-Sent Events, GraphQL subscriptions, Firebase onSnapshot, Supabase realtime.*\n\n### 2. Server-Mediated Data Access\n\nAll reads and writes go through server functions. Never expose the database directly to clients.\n\nThis is the correct security model. Not row-level security policies bolted onto a raw database connection. Not \"just use RLS.\" Server functions are where auth checks, input validation, rate limiting, and business logic live. They're testable, composable, and auditable.\n\nEvery production application eventually needs server-side logic between the client and the database. Start there instead of retrofitting it later.\n\n### 3. Functions as the API\n\nDefine queries (reads), mutations (writes), and actions (side effects) as plain functions. The function signature IS the API contract.\n\nNo route files. No controller classes. No middleware chains. No REST boilerplate. The function boundary is the API boundary.\n\n```typescript\n\u002F\u002F The function signature IS the API contract.\n\u002F\u002F The function name is the endpoint. The arguments are the request.\n\u002F\u002F The return value is the response.\n\nasync function getMessages(channelId: ChannelId): Promise\u003CMessage[]> {\n  return await db.messages\n    .where(\"channelId\", \"==\", channelId)\n    .orderBy(\"createdAt\", \"desc\")\n    .limit(50);\n}\n```\n\nClients subscribing to `getMessages` receive updates whenever the underlying messages change.\n\n### 4. Schema-First Design\n\nDefine your data model with typed schemas upfront. The schema is the single source of truth — not an ORM, not a migration file, not a SQL dump.\n\nSchemas generate types, validate data at write time, and serve as living documentation. When you read a schema, you understand the data model. When you change a schema, the system tells you everywhere that breaks.\n\n```typescript\n\u002F\u002F Schema as source of truth — generates types and validates at runtime\n\u002F\u002F Adapt syntax to your stack: Prisma schema, Zod, TypeBox, JSON Schema, etc.\n\ninterface Message {\n  id: MessageId;\n  channelId: ChannelId;\n  authorId: UserId;\n  body: string;\n  createdAt: number;\n}\n\ninterface Channel {\n  id: ChannelId;\n  name: string;\n  workspaceId: WorkspaceId;\n}\n\u002F\u002F Define indexes for every query path\n\u002F\u002F messages: index on channelId\n\u002F\u002F channels: index on workspaceId\n```\n\n### 5. End-to-End Type Safety\n\nTypes flow from schema definition through server functions to client code with zero manual type definitions. Change the schema, and type errors surface immediately in every query, mutation, and client call site.\n\nNo `any` types. No manual interface definitions that drift from the actual data. No runtime surprises because a field was renamed in the database but not in the API layer.\n\nThe type system is your first line of defense. It should catch contract violations at compile time, not at 2am in production.\n\n*Implementations: tRPC, GraphQL codegen, Prisma generated types, Zod inference.*\n\n### 6. ACID Transactions by Default\n\nEvery mutation runs as a transaction on a consistent database snapshot. Reads within a mutation see a consistent view. Writes either all commit or all abort.\n\nNo partial writes. No \"eventually consistent\" surprises for operations that should be atomic. No manual locking or retry logic for basic correctness.\n\n```typescript\nasync function sendMessage(channelId: ChannelId, body: string): Promise\u003Cvoid> {\n  const user = await getAuthenticatedUser();\n  \n  \u002F\u002F Run in a transaction — both succeed or both fail\n  await db.transaction(async (tx) => {\n    const channel = await tx.channels.get(channelId);\n    if (!channel) throw new Error(\"Channel not found\");\n\n    await tx.messages.insert({\n      channelId,\n      authorId: user.id,\n      body,\n      createdAt: Date.now(),\n    });\n    \n    await tx.channels.update(channelId, {\n      lastMessageAt: Date.now(),\n    });\n  });\n}\n```\n\n### 7. No Request Waterfalls\n\nServer-side composition means loading related data in a single round trip. Don't force clients to make serial fetches.\n\nA query function can load messages AND their authors in one call. Not messages first, then N author lookups. The server has direct database access — use it.\n\n```typescript\nasync function getMessagesWithAuthors(channelId: ChannelId): Promise\u003CMessageWithAuthor[]> {\n  const messages = await db.messages\n    .where(\"channelId\", \"==\", channelId)\n    .orderBy(\"createdAt\", \"desc\")\n    .limit(50);\n\n  \u002F\u002F Batch load authors to avoid N+1\n  const authorIds = [...new Set(messages.map(m => m.authorId))];\n  const authors = await db.users.getMany(authorIds);\n  const authorMap = new Map(authors.map(a => [a.id, a]));\n\n  return messages.map(msg => ({\n    ...msg,\n    author: authorMap.get(msg.authorId),\n  }));\n}\n```\n\nClients get exactly the data shape they need in one request. If using a reactive system, any author's name change triggers an update automatically.\n\n### 8. Colocated Server Logic\n\nQueries, mutations, and helper functions live together, organized by domain. Not split across `routes\u002F`, `controllers\u002F`, `services\u002F`, `repositories\u002F` layers.\n\nUnderstanding an operation should mean reading one file, not tracing through four layers of indirection. Colocation reduces cognitive load and makes the codebase navigable for both humans and AI agents.\n\n```\nserver\u002F\n  messages.ts      # queries + mutations for messages\n  channels.ts      # queries + mutations for channels\n  users.ts         # queries + mutations for users\n  schema.ts        # the data model\n  helpers\u002F         # shared utilities (auth, validation)\nclient\u002F\n```\n\n### 9. Agent-Friendly DX\n\nFunction signatures are self-documenting. Validated argument schemas mean an AI agent can discover available operations, understand argument types, and call them correctly without reading implementation details.\n\nDesign for the \"pit of success\" — the correct implementation is the easy path. Wrong usage should fail at compile time or with a clear validation error, not silently produce incorrect results.\n\nClear contracts beat clever abstractions. An explicit function with typed args is better than a magical ORM method that hides what query it generates.\n\n### 10. Minimal Infrastructure Burden\n\nThe backend handles scaling, caching, connection pooling, and deployment automatically where possible.\n\nPrefer managed infrastructure that handles scaling, caching, and deployment automatically. The less operational burden you own, the more you can focus on product logic.\n\nBuilt-in query caching with automatic invalidation when underlying data changes. No manual cache keys. No TTLs to tune. No stale data bugs because you forgot to invalidate after a write.\n\n### 11. Use Platform Primitives\n\nAuth, file storage, scheduled jobs, vector search, and text search should be first-class features of your platform or well-integrated services. Don't bolt on five loosely-coupled external services when cohesive solutions exist.\n\nKeep external API calls (sending emails, processing payments) separate from database transactions so they don't block or degrade core performance.\n\n```typescript\n\u002F\u002F Scheduled cleanup — use your platform's native scheduler\n\u002F\u002F (cloud functions, cron jobs, built-in scheduling, etc.)\n\nasync function cleanupExpiredTokens(): Promise\u003Cvoid> {\n  const expired = await db.tokens\n    .where(\"expiresAt\", \"\u003C\", Date.now())\n    .limit(1000);\n\n  await db.transaction(async (tx) => {\n    for (const token of expired) {\n      await tx.tokens.delete(token.id);\n    }\n  });\n}\n\n\u002F\u002F Schedule: run every hour, or trigger after token creation\n```\n\n### 12. Optimistic Updates\n\nMutations can describe their expected effect so UIs update instantly, before the server confirms. Latency becomes invisible to the user.\n\nThe optimistic update runs client-side against a local cache. When the server confirms (or rejects), the real result replaces the optimistic one. Handle rollback on conflict.\n\n*Implementations: React Query optimistic updates, Apollo Client, TanStack Query, SWR mutate.*\n\n### 13. Stateless by Design\n\nServer functions should not rely on in-memory state between requests. Any state lives in the database or a dedicated cache layer. This enables horizontal scaling — add more server instances without coordination.\n\nSession data, user context, and temporary state belong in persistent storage, not process memory. A request should be handleable by any server instance.\n\n### 14. Graceful Degradation\n\nExternal dependencies fail. Design for it. Use timeouts on external calls. Implement circuit breakers for flaky services. Return partial results when possible rather than failing entirely.\n\nA user search that can't reach the recommendation service should still return basic results. A dashboard that can't load analytics should still show the data it can fetch.\n\n### 15. Rate Limiting\n\nProtect your backend from abuse and thundering herds. Implement rate limiting at the function level, not just at an API gateway. Different operations have different limits — a login endpoint needs stricter limits than a read-only query.\n\nRate limits should return structured errors with retry-after information, not just reject requests silently.\n\n## Anti-Patterns\n\nThese are the \"AI slop\" of backend architecture — patterns that look productive but create long-term pain:\n\n- **REST boilerplate factories** — GET\u002FPOST\u002FPUT\u002FDELETE scaffolds for every resource, generating hundreds of lines of routing code that all do the same thing\n- **Row-level security as the primary auth model** — hard to reason about, hard to test, hard to compose. Security belongs in server functions, not in database policy DSLs\n- **Direct client-to-database access** — every production app eventually needs server-side logic; start there instead of discovering this truth at the worst possible time\n- **ORMs that hide queries** — magic methods that generate N+1 disasters, obscure what SQL actually runs, and make performance debugging a nightmare\n- **Polling for freshness** — `setInterval(() => refetch(), 5000)` when real-time subscriptions exist and are simpler to implement correctly\n- **Separate real-time infrastructure** — bolting a WebSocket service alongside your REST API and main database, creating two sources of truth\n- **Manual cache invalidation** — scattered `cache.delete()` calls that inevitably miss an edge case, showing stale data to users\n- **Layered architecture** — splitting a single logical operation across routes, controllers, services, repositories, and DTOs. Four files to understand one database read\n- **Client-side request waterfalls** — serial fetches that should be a single server-side composed query\n- **Migration files as source of truth** — delta migration files that you have to replay mentally to understand the current schema. Declarative schemas beat imperative migrations\n- **Raw SQL string concatenation** — SQL injection waiting to happen, bypassing every type safety guarantee\n- **Hardcoded configuration values** — connection strings, API keys, and feature flags embedded in application code\n- **Missing input validation** — trusting client-sent data without validating at the function boundary\n- **Inconsistent error shapes** — some endpoints return `{ error: string }`, others throw, others return HTTP 500 with an HTML page\n- **Middleware chains 10 layers deep** — debugging requires stepping through a dozen wrappers before reaching business logic\n- **Mixed freshness** — a page showing real-time chat messages next to a user list that updates every 30 seconds. Reactivity as an afterthought creates jarring UX\n- **Unbounded queries** — `SELECT * FROM messages` without limits. Every query should have a maximum result size\n- **Synchronous external calls in hot paths** — calling a third-party API during a user request blocks the response. Move external calls to background jobs when possible\n- **Missing timeouts** — database queries or HTTP calls that can hang forever, exhausting connection pools\n- **Offset-based pagination at scale** — `OFFSET 10000` means the database still reads 10,000 rows before discarding them\n\n## Implementation Guidance\n\nWhen building backend features, follow these practices:\n\n- **Validate inputs at the function boundary** — use schema validators (Zod, TypeBox, Joi, etc.) on every query and mutation argument. Invalid data should never reach business logic.\n- **Keep server functions focused** — queries should be deterministic reads with no side effects. Mutations should be focused writes. Side effects (external API calls, emails) should be handled separately (background jobs, queues, etc.).\n\n- **Use platform-native scheduling** — scheduled functions are more reliable and observable than external cron jobs.\n- **Prefer database constraints over application checks** — unique indexes, required fields, and foreign key relationships catch bugs that application code misses.\n- **Design for idempotency** — writes that might be retried (network failures, user double-clicks) should produce the same result when executed twice.\n- **Return structured errors** — error codes and machine-readable details, not string messages. Clients (especially AI agents) need to programmatically handle errors.\n- **Log at function boundaries** — log inputs and outcomes at the query\u002Fmutation level, not deep inside helper functions. This makes debugging straightforward and keeps logs meaningful.\n- **Index every query path** — if you query by a field, index it. Full table scans are never acceptable in production.\n- **Separate reads from writes** — queries read, mutations write. Don't mix concerns. This separation enables reactive systems to track dependencies correctly.\n- **Use helper functions for shared logic** — auth checks, permission validation, and common data loading patterns should be extracted into reusable helpers, not copy-pasted across mutations.\n- **Think in documents, not joins** — model data for how it's read, not how it's normalized. Denormalization is often correct when reads vastly outnumber writes.\n- **Implement cursor-based pagination** — offset pagination breaks at scale and produces inconsistent results when data changes. Use cursors (timestamps, IDs) for stable pagination.\n- **Plan for multi-tenancy early** — even single-tenant apps often become multi-tenant. Include tenant isolation in your data model from day one; retrofitting it is painful.\n\n**IMPORTANT**: Match implementation complexity to the problem. A simple CRUD feature needs a schema, a few queries, and a few mutations — not an event-sourced architecture with CQRS. Conversely, a real-time collaborative feature needs careful thought about conflict resolution and consistency. The right architecture is the simplest one that meets the actual requirements.\n\nRemember: Claude is capable of building sophisticated backend systems. Don't default to boilerplate scaffolds. Think about what the backend actually needs to do, pick the right primitives, and implement it correctly the first time.\n",{"data":35,"body":36},{"name":4,"description":6,"license":27},{"type":37,"children":38},"root",[39,47,58,63,70,75,120,130,136,141,148,153,158,163,172,178,183,188,193,199,204,209,528,541,547,552,557,838,844,849,862,867,875,881,886,891,1476,1482,1487,1492,2102,2107,2113,2148,2153,2163,2169,2174,2179,2184,2190,2195,2200,2205,2211,2216,2221,2611,2617,2622,2627,2635,2641,2646,2651,2657,2662,2667,2673,2678,2683,2689,2694,2935,2941,2946,3079,3089,3094],{"type":40,"tag":41,"props":42,"children":43},"element","p",{},[44],{"type":45,"value":46},"text","This skill guides creation of reactive, type-safe, production-grade backends that avoid generic \"AI slop\" architecture. Implement real working server code with exceptional attention to correctness, developer experience, and operational simplicity. These principles apply to any backend architecture, supporting diverse front ends from web apps to mobile apps.",{"type":40,"tag":41,"props":48,"children":49},{},[50,56],{"type":40,"tag":51,"props":52,"children":53},"strong",{},[54],{"type":45,"value":55},"Note",{"type":45,"value":57},": These are universal backend design philosophies. Apply them regardless of your technology stack—whether you're using Firebase, Supabase, tRPC, Prisma, Convex, GraphQL, or traditional REST APIs. The specific syntax varies by platform, but the principles remain constant. Code examples are illustrative; adapt them to your chosen stack.",{"type":40,"tag":41,"props":59,"children":60},{},[61],{"type":45,"value":62},"The user provides backend requirements: an API, data model, server function, scheduled job, or system to build. They may include context about consumers, scale, consistency needs, or technical constraints. Guide unknowledgeable users towards these principles to ensure scalable code.",{"type":40,"tag":64,"props":65,"children":67},"h2",{"id":66},"design-thinking",[68],{"type":45,"value":69},"Design Thinking",{"type":40,"tag":41,"props":71,"children":72},{},[73],{"type":45,"value":74},"Before coding, understand the context and commit to the right architectural choices:",{"type":40,"tag":76,"props":77,"children":78},"ul",{},[79,90,100,110],{"type":40,"tag":80,"props":81,"children":82},"li",{},[83,88],{"type":40,"tag":51,"props":84,"children":85},{},[86],{"type":45,"value":87},"Purpose",{"type":45,"value":89},": What data or logic does this backend manage? What invariants must hold?",{"type":40,"tag":80,"props":91,"children":92},{},[93,98],{"type":40,"tag":51,"props":94,"children":95},{},[96],{"type":45,"value":97},"Consumers",{"type":45,"value":99},": Who calls this — humans, AI agents, frontend apps, other services? Each consumer shapes the API contract differently.",{"type":40,"tag":80,"props":101,"children":102},{},[103,108],{"type":40,"tag":51,"props":104,"children":105},{},[106],{"type":45,"value":107},"Constraints",{"type":45,"value":109},": Scale requirements, consistency needs, latency targets, compliance obligations.",{"type":40,"tag":80,"props":111,"children":112},{},[113,118],{"type":40,"tag":51,"props":114,"children":115},{},[116],{"type":45,"value":117},"DX goal",{"type":45,"value":119},": What makes this backend a joy to work with? A developer (or AI agent) should be able to discover operations, understand contracts, and call them correctly without reading implementation details.",{"type":40,"tag":41,"props":121,"children":122},{},[123,128],{"type":40,"tag":51,"props":124,"children":125},{},[126],{"type":45,"value":127},"CRITICAL",{"type":45,"value":129},": The best backends are boring in the right ways — predictable data access, obvious error handling, clear contracts — and exciting in the right ways — real-time by default, automatic scaling, instant type feedback across the entire stack.",{"type":40,"tag":64,"props":131,"children":133},{"id":132},"core-principles",[134],{"type":45,"value":135},"Core Principles",{"type":40,"tag":41,"props":137,"children":138},{},[139],{"type":45,"value":140},"These principles are opinionated. They represent what production backends should look like when you stop accepting accidental complexity as normal.",{"type":40,"tag":142,"props":143,"children":145},"h3",{"id":144},"_1-reactive-by-default",[146],{"type":45,"value":147},"1. Reactive by Default",{"type":40,"tag":41,"props":149,"children":150},{},[151],{"type":45,"value":152},"All queries are live queries. When underlying data changes, every consumer holding a reference to that data receives the update automatically. No polling. No webhooks-as-workaround. No mix of fresh and stale data.",{"type":40,"tag":41,"props":154,"children":155},{},[156],{"type":45,"value":157},"This isn't a feature you opt into — it's the baseline. A user viewing a list of messages sees new messages appear. A dashboard showing metrics updates in real time. An AI agent monitoring a queue gets notified immediately.",{"type":40,"tag":41,"props":159,"children":160},{},[161],{"type":45,"value":162},"Reads and writes on the same connection guarantee consistency. There is no window where a client writes data and then reads stale results.",{"type":40,"tag":41,"props":164,"children":165},{},[166],{"type":40,"tag":167,"props":168,"children":169},"em",{},[170],{"type":45,"value":171},"Implementations: WebSocket subscriptions, Server-Sent Events, GraphQL subscriptions, Firebase onSnapshot, Supabase realtime.",{"type":40,"tag":142,"props":173,"children":175},{"id":174},"_2-server-mediated-data-access",[176],{"type":45,"value":177},"2. Server-Mediated Data Access",{"type":40,"tag":41,"props":179,"children":180},{},[181],{"type":45,"value":182},"All reads and writes go through server functions. Never expose the database directly to clients.",{"type":40,"tag":41,"props":184,"children":185},{},[186],{"type":45,"value":187},"This is the correct security model. Not row-level security policies bolted onto a raw database connection. Not \"just use RLS.\" Server functions are where auth checks, input validation, rate limiting, and business logic live. They're testable, composable, and auditable.",{"type":40,"tag":41,"props":189,"children":190},{},[191],{"type":45,"value":192},"Every production application eventually needs server-side logic between the client and the database. Start there instead of retrofitting it later.",{"type":40,"tag":142,"props":194,"children":196},{"id":195},"_3-functions-as-the-api",[197],{"type":45,"value":198},"3. Functions as the API",{"type":40,"tag":41,"props":200,"children":201},{},[202],{"type":45,"value":203},"Define queries (reads), mutations (writes), and actions (side effects) as plain functions. The function signature IS the API contract.",{"type":40,"tag":41,"props":205,"children":206},{},[207],{"type":45,"value":208},"No route files. No controller classes. No middleware chains. No REST boilerplate. The function boundary is the API boundary.",{"type":40,"tag":210,"props":211,"children":216},"pre",{"className":212,"code":213,"language":214,"meta":215,"style":215},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F The function signature IS the API contract.\n\u002F\u002F The function name is the endpoint. The arguments are the request.\n\u002F\u002F The return value is the response.\n\nasync function getMessages(channelId: ChannelId): Promise\u003CMessage[]> {\n  return await db.messages\n    .where(\"channelId\", \"==\", channelId)\n    .orderBy(\"createdAt\", \"desc\")\n    .limit(50);\n}\n","typescript","",[217],{"type":40,"tag":218,"props":219,"children":220},"code",{"__ignoreMap":215},[221,232,240,249,259,339,369,435,486,519],{"type":40,"tag":222,"props":223,"children":225},"span",{"class":224,"line":28},"line",[226],{"type":40,"tag":222,"props":227,"children":229},{"style":228},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[230],{"type":45,"value":231},"\u002F\u002F The function signature IS the API contract.\n",{"type":40,"tag":222,"props":233,"children":234},{"class":224,"line":24},[235],{"type":40,"tag":222,"props":236,"children":237},{"style":228},[238],{"type":45,"value":239},"\u002F\u002F The function name is the endpoint. The arguments are the request.\n",{"type":40,"tag":222,"props":241,"children":243},{"class":224,"line":242},3,[244],{"type":40,"tag":222,"props":245,"children":246},{"style":228},[247],{"type":45,"value":248},"\u002F\u002F The return value is the response.\n",{"type":40,"tag":222,"props":250,"children":252},{"class":224,"line":251},4,[253],{"type":40,"tag":222,"props":254,"children":256},{"emptyLinePlaceholder":255},true,[257],{"type":45,"value":258},"\n",{"type":40,"tag":222,"props":260,"children":262},{"class":224,"line":261},5,[263,269,274,280,286,292,297,303,308,313,318,323,329,334],{"type":40,"tag":222,"props":264,"children":266},{"style":265},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[267],{"type":45,"value":268},"async",{"type":40,"tag":222,"props":270,"children":271},{"style":265},[272],{"type":45,"value":273}," function",{"type":40,"tag":222,"props":275,"children":277},{"style":276},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[278],{"type":45,"value":279}," getMessages",{"type":40,"tag":222,"props":281,"children":283},{"style":282},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[284],{"type":45,"value":285},"(",{"type":40,"tag":222,"props":287,"children":289},{"style":288},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic",[290],{"type":45,"value":291},"channelId",{"type":40,"tag":222,"props":293,"children":294},{"style":282},[295],{"type":45,"value":296},":",{"type":40,"tag":222,"props":298,"children":300},{"style":299},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[301],{"type":45,"value":302}," ChannelId",{"type":40,"tag":222,"props":304,"children":305},{"style":282},[306],{"type":45,"value":307},"):",{"type":40,"tag":222,"props":309,"children":310},{"style":299},[311],{"type":45,"value":312}," Promise",{"type":40,"tag":222,"props":314,"children":315},{"style":282},[316],{"type":45,"value":317},"\u003C",{"type":40,"tag":222,"props":319,"children":320},{"style":299},[321],{"type":45,"value":322},"Message",{"type":40,"tag":222,"props":324,"children":326},{"style":325},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[327],{"type":45,"value":328},"[]",{"type":40,"tag":222,"props":330,"children":331},{"style":282},[332],{"type":45,"value":333},">",{"type":40,"tag":222,"props":335,"children":336},{"style":282},[337],{"type":45,"value":338}," {\n",{"type":40,"tag":222,"props":340,"children":342},{"class":224,"line":341},6,[343,349,354,359,364],{"type":40,"tag":222,"props":344,"children":346},{"style":345},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[347],{"type":45,"value":348},"  return",{"type":40,"tag":222,"props":350,"children":351},{"style":345},[352],{"type":45,"value":353}," await",{"type":40,"tag":222,"props":355,"children":356},{"style":325},[357],{"type":45,"value":358}," db",{"type":40,"tag":222,"props":360,"children":361},{"style":282},[362],{"type":45,"value":363},".",{"type":40,"tag":222,"props":365,"children":366},{"style":325},[367],{"type":45,"value":368},"messages\n",{"type":40,"tag":222,"props":370,"children":372},{"class":224,"line":371},7,[373,378,383,388,393,398,402,407,412,417,421,425,430],{"type":40,"tag":222,"props":374,"children":375},{"style":282},[376],{"type":45,"value":377},"    .",{"type":40,"tag":222,"props":379,"children":380},{"style":276},[381],{"type":45,"value":382},"where",{"type":40,"tag":222,"props":384,"children":386},{"style":385},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[387],{"type":45,"value":285},{"type":40,"tag":222,"props":389,"children":390},{"style":282},[391],{"type":45,"value":392},"\"",{"type":40,"tag":222,"props":394,"children":396},{"style":395},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[397],{"type":45,"value":291},{"type":40,"tag":222,"props":399,"children":400},{"style":282},[401],{"type":45,"value":392},{"type":40,"tag":222,"props":403,"children":404},{"style":282},[405],{"type":45,"value":406},",",{"type":40,"tag":222,"props":408,"children":409},{"style":282},[410],{"type":45,"value":411}," \"",{"type":40,"tag":222,"props":413,"children":414},{"style":395},[415],{"type":45,"value":416},"==",{"type":40,"tag":222,"props":418,"children":419},{"style":282},[420],{"type":45,"value":392},{"type":40,"tag":222,"props":422,"children":423},{"style":282},[424],{"type":45,"value":406},{"type":40,"tag":222,"props":426,"children":427},{"style":325},[428],{"type":45,"value":429}," channelId",{"type":40,"tag":222,"props":431,"children":432},{"style":385},[433],{"type":45,"value":434},")\n",{"type":40,"tag":222,"props":436,"children":438},{"class":224,"line":437},8,[439,443,448,452,456,461,465,469,473,478,482],{"type":40,"tag":222,"props":440,"children":441},{"style":282},[442],{"type":45,"value":377},{"type":40,"tag":222,"props":444,"children":445},{"style":276},[446],{"type":45,"value":447},"orderBy",{"type":40,"tag":222,"props":449,"children":450},{"style":385},[451],{"type":45,"value":285},{"type":40,"tag":222,"props":453,"children":454},{"style":282},[455],{"type":45,"value":392},{"type":40,"tag":222,"props":457,"children":458},{"style":395},[459],{"type":45,"value":460},"createdAt",{"type":40,"tag":222,"props":462,"children":463},{"style":282},[464],{"type":45,"value":392},{"type":40,"tag":222,"props":466,"children":467},{"style":282},[468],{"type":45,"value":406},{"type":40,"tag":222,"props":470,"children":471},{"style":282},[472],{"type":45,"value":411},{"type":40,"tag":222,"props":474,"children":475},{"style":395},[476],{"type":45,"value":477},"desc",{"type":40,"tag":222,"props":479,"children":480},{"style":282},[481],{"type":45,"value":392},{"type":40,"tag":222,"props":483,"children":484},{"style":385},[485],{"type":45,"value":434},{"type":40,"tag":222,"props":487,"children":489},{"class":224,"line":488},9,[490,494,499,503,509,514],{"type":40,"tag":222,"props":491,"children":492},{"style":282},[493],{"type":45,"value":377},{"type":40,"tag":222,"props":495,"children":496},{"style":276},[497],{"type":45,"value":498},"limit",{"type":40,"tag":222,"props":500,"children":501},{"style":385},[502],{"type":45,"value":285},{"type":40,"tag":222,"props":504,"children":506},{"style":505},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[507],{"type":45,"value":508},"50",{"type":40,"tag":222,"props":510,"children":511},{"style":385},[512],{"type":45,"value":513},")",{"type":40,"tag":222,"props":515,"children":516},{"style":282},[517],{"type":45,"value":518},";\n",{"type":40,"tag":222,"props":520,"children":522},{"class":224,"line":521},10,[523],{"type":40,"tag":222,"props":524,"children":525},{"style":282},[526],{"type":45,"value":527},"}\n",{"type":40,"tag":41,"props":529,"children":530},{},[531,533,539],{"type":45,"value":532},"Clients subscribing to ",{"type":40,"tag":218,"props":534,"children":536},{"className":535},[],[537],{"type":45,"value":538},"getMessages",{"type":45,"value":540}," receive updates whenever the underlying messages change.",{"type":40,"tag":142,"props":542,"children":544},{"id":543},"_4-schema-first-design",[545],{"type":45,"value":546},"4. Schema-First Design",{"type":40,"tag":41,"props":548,"children":549},{},[550],{"type":45,"value":551},"Define your data model with typed schemas upfront. The schema is the single source of truth — not an ORM, not a migration file, not a SQL dump.",{"type":40,"tag":41,"props":553,"children":554},{},[555],{"type":45,"value":556},"Schemas generate types, validate data at write time, and serve as living documentation. When you read a schema, you understand the data model. When you change a schema, the system tells you everywhere that breaks.",{"type":40,"tag":210,"props":558,"children":560},{"className":212,"code":559,"language":214,"meta":215,"style":215},"\u002F\u002F Schema as source of truth — generates types and validates at runtime\n\u002F\u002F Adapt syntax to your stack: Prisma schema, Zod, TypeBox, JSON Schema, etc.\n\ninterface Message {\n  id: MessageId;\n  channelId: ChannelId;\n  authorId: UserId;\n  body: string;\n  createdAt: number;\n}\n\ninterface Channel {\n  id: ChannelId;\n  name: string;\n  workspaceId: WorkspaceId;\n}\n\u002F\u002F Define indexes for every query path\n\u002F\u002F messages: index on channelId\n\u002F\u002F channels: index on workspaceId\n",[561],{"type":40,"tag":218,"props":562,"children":563},{"__ignoreMap":215},[564,572,580,587,604,625,645,666,687,708,715,723,740,760,781,803,811,820,829],{"type":40,"tag":222,"props":565,"children":566},{"class":224,"line":28},[567],{"type":40,"tag":222,"props":568,"children":569},{"style":228},[570],{"type":45,"value":571},"\u002F\u002F Schema as source of truth — generates types and validates at runtime\n",{"type":40,"tag":222,"props":573,"children":574},{"class":224,"line":24},[575],{"type":40,"tag":222,"props":576,"children":577},{"style":228},[578],{"type":45,"value":579},"\u002F\u002F Adapt syntax to your stack: Prisma schema, Zod, TypeBox, JSON Schema, etc.\n",{"type":40,"tag":222,"props":581,"children":582},{"class":224,"line":242},[583],{"type":40,"tag":222,"props":584,"children":585},{"emptyLinePlaceholder":255},[586],{"type":45,"value":258},{"type":40,"tag":222,"props":588,"children":589},{"class":224,"line":251},[590,595,600],{"type":40,"tag":222,"props":591,"children":592},{"style":265},[593],{"type":45,"value":594},"interface",{"type":40,"tag":222,"props":596,"children":597},{"style":299},[598],{"type":45,"value":599}," Message",{"type":40,"tag":222,"props":601,"children":602},{"style":282},[603],{"type":45,"value":338},{"type":40,"tag":222,"props":605,"children":606},{"class":224,"line":261},[607,612,616,621],{"type":40,"tag":222,"props":608,"children":609},{"style":385},[610],{"type":45,"value":611},"  id",{"type":40,"tag":222,"props":613,"children":614},{"style":282},[615],{"type":45,"value":296},{"type":40,"tag":222,"props":617,"children":618},{"style":299},[619],{"type":45,"value":620}," MessageId",{"type":40,"tag":222,"props":622,"children":623},{"style":282},[624],{"type":45,"value":518},{"type":40,"tag":222,"props":626,"children":627},{"class":224,"line":341},[628,633,637,641],{"type":40,"tag":222,"props":629,"children":630},{"style":385},[631],{"type":45,"value":632},"  channelId",{"type":40,"tag":222,"props":634,"children":635},{"style":282},[636],{"type":45,"value":296},{"type":40,"tag":222,"props":638,"children":639},{"style":299},[640],{"type":45,"value":302},{"type":40,"tag":222,"props":642,"children":643},{"style":282},[644],{"type":45,"value":518},{"type":40,"tag":222,"props":646,"children":647},{"class":224,"line":371},[648,653,657,662],{"type":40,"tag":222,"props":649,"children":650},{"style":385},[651],{"type":45,"value":652},"  authorId",{"type":40,"tag":222,"props":654,"children":655},{"style":282},[656],{"type":45,"value":296},{"type":40,"tag":222,"props":658,"children":659},{"style":299},[660],{"type":45,"value":661}," UserId",{"type":40,"tag":222,"props":663,"children":664},{"style":282},[665],{"type":45,"value":518},{"type":40,"tag":222,"props":667,"children":668},{"class":224,"line":437},[669,674,678,683],{"type":40,"tag":222,"props":670,"children":671},{"style":385},[672],{"type":45,"value":673},"  body",{"type":40,"tag":222,"props":675,"children":676},{"style":282},[677],{"type":45,"value":296},{"type":40,"tag":222,"props":679,"children":680},{"style":299},[681],{"type":45,"value":682}," string",{"type":40,"tag":222,"props":684,"children":685},{"style":282},[686],{"type":45,"value":518},{"type":40,"tag":222,"props":688,"children":689},{"class":224,"line":488},[690,695,699,704],{"type":40,"tag":222,"props":691,"children":692},{"style":385},[693],{"type":45,"value":694},"  createdAt",{"type":40,"tag":222,"props":696,"children":697},{"style":282},[698],{"type":45,"value":296},{"type":40,"tag":222,"props":700,"children":701},{"style":299},[702],{"type":45,"value":703}," number",{"type":40,"tag":222,"props":705,"children":706},{"style":282},[707],{"type":45,"value":518},{"type":40,"tag":222,"props":709,"children":710},{"class":224,"line":521},[711],{"type":40,"tag":222,"props":712,"children":713},{"style":282},[714],{"type":45,"value":527},{"type":40,"tag":222,"props":716,"children":718},{"class":224,"line":717},11,[719],{"type":40,"tag":222,"props":720,"children":721},{"emptyLinePlaceholder":255},[722],{"type":45,"value":258},{"type":40,"tag":222,"props":724,"children":726},{"class":224,"line":725},12,[727,731,736],{"type":40,"tag":222,"props":728,"children":729},{"style":265},[730],{"type":45,"value":594},{"type":40,"tag":222,"props":732,"children":733},{"style":299},[734],{"type":45,"value":735}," Channel",{"type":40,"tag":222,"props":737,"children":738},{"style":282},[739],{"type":45,"value":338},{"type":40,"tag":222,"props":741,"children":743},{"class":224,"line":742},13,[744,748,752,756],{"type":40,"tag":222,"props":745,"children":746},{"style":385},[747],{"type":45,"value":611},{"type":40,"tag":222,"props":749,"children":750},{"style":282},[751],{"type":45,"value":296},{"type":40,"tag":222,"props":753,"children":754},{"style":299},[755],{"type":45,"value":302},{"type":40,"tag":222,"props":757,"children":758},{"style":282},[759],{"type":45,"value":518},{"type":40,"tag":222,"props":761,"children":763},{"class":224,"line":762},14,[764,769,773,777],{"type":40,"tag":222,"props":765,"children":766},{"style":385},[767],{"type":45,"value":768},"  name",{"type":40,"tag":222,"props":770,"children":771},{"style":282},[772],{"type":45,"value":296},{"type":40,"tag":222,"props":774,"children":775},{"style":299},[776],{"type":45,"value":682},{"type":40,"tag":222,"props":778,"children":779},{"style":282},[780],{"type":45,"value":518},{"type":40,"tag":222,"props":782,"children":784},{"class":224,"line":783},15,[785,790,794,799],{"type":40,"tag":222,"props":786,"children":787},{"style":385},[788],{"type":45,"value":789},"  workspaceId",{"type":40,"tag":222,"props":791,"children":792},{"style":282},[793],{"type":45,"value":296},{"type":40,"tag":222,"props":795,"children":796},{"style":299},[797],{"type":45,"value":798}," WorkspaceId",{"type":40,"tag":222,"props":800,"children":801},{"style":282},[802],{"type":45,"value":518},{"type":40,"tag":222,"props":804,"children":806},{"class":224,"line":805},16,[807],{"type":40,"tag":222,"props":808,"children":809},{"style":282},[810],{"type":45,"value":527},{"type":40,"tag":222,"props":812,"children":814},{"class":224,"line":813},17,[815],{"type":40,"tag":222,"props":816,"children":817},{"style":228},[818],{"type":45,"value":819},"\u002F\u002F Define indexes for every query path\n",{"type":40,"tag":222,"props":821,"children":823},{"class":224,"line":822},18,[824],{"type":40,"tag":222,"props":825,"children":826},{"style":228},[827],{"type":45,"value":828},"\u002F\u002F messages: index on channelId\n",{"type":40,"tag":222,"props":830,"children":832},{"class":224,"line":831},19,[833],{"type":40,"tag":222,"props":834,"children":835},{"style":228},[836],{"type":45,"value":837},"\u002F\u002F channels: index on workspaceId\n",{"type":40,"tag":142,"props":839,"children":841},{"id":840},"_5-end-to-end-type-safety",[842],{"type":45,"value":843},"5. End-to-End Type Safety",{"type":40,"tag":41,"props":845,"children":846},{},[847],{"type":45,"value":848},"Types flow from schema definition through server functions to client code with zero manual type definitions. Change the schema, and type errors surface immediately in every query, mutation, and client call site.",{"type":40,"tag":41,"props":850,"children":851},{},[852,854,860],{"type":45,"value":853},"No ",{"type":40,"tag":218,"props":855,"children":857},{"className":856},[],[858],{"type":45,"value":859},"any",{"type":45,"value":861}," types. No manual interface definitions that drift from the actual data. No runtime surprises because a field was renamed in the database but not in the API layer.",{"type":40,"tag":41,"props":863,"children":864},{},[865],{"type":45,"value":866},"The type system is your first line of defense. It should catch contract violations at compile time, not at 2am in production.",{"type":40,"tag":41,"props":868,"children":869},{},[870],{"type":40,"tag":167,"props":871,"children":872},{},[873],{"type":45,"value":874},"Implementations: tRPC, GraphQL codegen, Prisma generated types, Zod inference.",{"type":40,"tag":142,"props":876,"children":878},{"id":877},"_6-acid-transactions-by-default",[879],{"type":45,"value":880},"6. ACID Transactions by Default",{"type":40,"tag":41,"props":882,"children":883},{},[884],{"type":45,"value":885},"Every mutation runs as a transaction on a consistent database snapshot. Reads within a mutation see a consistent view. Writes either all commit or all abort.",{"type":40,"tag":41,"props":887,"children":888},{},[889],{"type":45,"value":890},"No partial writes. No \"eventually consistent\" surprises for operations that should be atomic. No manual locking or retry logic for basic correctness.",{"type":40,"tag":210,"props":892,"children":894},{"className":212,"code":893,"language":214,"meta":215,"style":215},"async function sendMessage(channelId: ChannelId, body: string): Promise\u003Cvoid> {\n  const user = await getAuthenticatedUser();\n  \n  \u002F\u002F Run in a transaction — both succeed or both fail\n  await db.transaction(async (tx) => {\n    const channel = await tx.channels.get(channelId);\n    if (!channel) throw new Error(\"Channel not found\");\n\n    await tx.messages.insert({\n      channelId,\n      authorId: user.id,\n      body,\n      createdAt: Date.now(),\n    });\n    \n    await tx.channels.update(channelId, {\n      lastMessageAt: Date.now(),\n    });\n  });\n}\n",[895],{"type":40,"tag":218,"props":896,"children":897},{"__ignoreMap":215},[898,972,1008,1016,1024,1076,1136,1203,1210,1249,1262,1291,1303,1337,1353,1361,1405,1437,1452,1468],{"type":40,"tag":222,"props":899,"children":900},{"class":224,"line":28},[901,905,909,914,918,922,926,930,934,939,943,947,951,955,959,964,968],{"type":40,"tag":222,"props":902,"children":903},{"style":265},[904],{"type":45,"value":268},{"type":40,"tag":222,"props":906,"children":907},{"style":265},[908],{"type":45,"value":273},{"type":40,"tag":222,"props":910,"children":911},{"style":276},[912],{"type":45,"value":913}," sendMessage",{"type":40,"tag":222,"props":915,"children":916},{"style":282},[917],{"type":45,"value":285},{"type":40,"tag":222,"props":919,"children":920},{"style":288},[921],{"type":45,"value":291},{"type":40,"tag":222,"props":923,"children":924},{"style":282},[925],{"type":45,"value":296},{"type":40,"tag":222,"props":927,"children":928},{"style":299},[929],{"type":45,"value":302},{"type":40,"tag":222,"props":931,"children":932},{"style":282},[933],{"type":45,"value":406},{"type":40,"tag":222,"props":935,"children":936},{"style":288},[937],{"type":45,"value":938}," body",{"type":40,"tag":222,"props":940,"children":941},{"style":282},[942],{"type":45,"value":296},{"type":40,"tag":222,"props":944,"children":945},{"style":299},[946],{"type":45,"value":682},{"type":40,"tag":222,"props":948,"children":949},{"style":282},[950],{"type":45,"value":307},{"type":40,"tag":222,"props":952,"children":953},{"style":299},[954],{"type":45,"value":312},{"type":40,"tag":222,"props":956,"children":957},{"style":282},[958],{"type":45,"value":317},{"type":40,"tag":222,"props":960,"children":961},{"style":299},[962],{"type":45,"value":963},"void",{"type":40,"tag":222,"props":965,"children":966},{"style":282},[967],{"type":45,"value":333},{"type":40,"tag":222,"props":969,"children":970},{"style":282},[971],{"type":45,"value":338},{"type":40,"tag":222,"props":973,"children":974},{"class":224,"line":24},[975,980,985,990,994,999,1004],{"type":40,"tag":222,"props":976,"children":977},{"style":265},[978],{"type":45,"value":979},"  const",{"type":40,"tag":222,"props":981,"children":982},{"style":325},[983],{"type":45,"value":984}," user",{"type":40,"tag":222,"props":986,"children":987},{"style":282},[988],{"type":45,"value":989}," =",{"type":40,"tag":222,"props":991,"children":992},{"style":345},[993],{"type":45,"value":353},{"type":40,"tag":222,"props":995,"children":996},{"style":276},[997],{"type":45,"value":998}," getAuthenticatedUser",{"type":40,"tag":222,"props":1000,"children":1001},{"style":385},[1002],{"type":45,"value":1003},"()",{"type":40,"tag":222,"props":1005,"children":1006},{"style":282},[1007],{"type":45,"value":518},{"type":40,"tag":222,"props":1009,"children":1010},{"class":224,"line":242},[1011],{"type":40,"tag":222,"props":1012,"children":1013},{"style":385},[1014],{"type":45,"value":1015},"  \n",{"type":40,"tag":222,"props":1017,"children":1018},{"class":224,"line":251},[1019],{"type":40,"tag":222,"props":1020,"children":1021},{"style":228},[1022],{"type":45,"value":1023},"  \u002F\u002F Run in a transaction — both succeed or both fail\n",{"type":40,"tag":222,"props":1025,"children":1026},{"class":224,"line":261},[1027,1032,1036,1040,1045,1049,1053,1058,1063,1067,1072],{"type":40,"tag":222,"props":1028,"children":1029},{"style":345},[1030],{"type":45,"value":1031},"  await",{"type":40,"tag":222,"props":1033,"children":1034},{"style":325},[1035],{"type":45,"value":358},{"type":40,"tag":222,"props":1037,"children":1038},{"style":282},[1039],{"type":45,"value":363},{"type":40,"tag":222,"props":1041,"children":1042},{"style":276},[1043],{"type":45,"value":1044},"transaction",{"type":40,"tag":222,"props":1046,"children":1047},{"style":385},[1048],{"type":45,"value":285},{"type":40,"tag":222,"props":1050,"children":1051},{"style":265},[1052],{"type":45,"value":268},{"type":40,"tag":222,"props":1054,"children":1055},{"style":282},[1056],{"type":45,"value":1057}," (",{"type":40,"tag":222,"props":1059,"children":1060},{"style":288},[1061],{"type":45,"value":1062},"tx",{"type":40,"tag":222,"props":1064,"children":1065},{"style":282},[1066],{"type":45,"value":513},{"type":40,"tag":222,"props":1068,"children":1069},{"style":265},[1070],{"type":45,"value":1071}," =>",{"type":40,"tag":222,"props":1073,"children":1074},{"style":282},[1075],{"type":45,"value":338},{"type":40,"tag":222,"props":1077,"children":1078},{"class":224,"line":341},[1079,1084,1089,1093,1097,1102,1106,1111,1115,1120,1124,1128,1132],{"type":40,"tag":222,"props":1080,"children":1081},{"style":265},[1082],{"type":45,"value":1083},"    const",{"type":40,"tag":222,"props":1085,"children":1086},{"style":325},[1087],{"type":45,"value":1088}," channel",{"type":40,"tag":222,"props":1090,"children":1091},{"style":282},[1092],{"type":45,"value":989},{"type":40,"tag":222,"props":1094,"children":1095},{"style":345},[1096],{"type":45,"value":353},{"type":40,"tag":222,"props":1098,"children":1099},{"style":325},[1100],{"type":45,"value":1101}," tx",{"type":40,"tag":222,"props":1103,"children":1104},{"style":282},[1105],{"type":45,"value":363},{"type":40,"tag":222,"props":1107,"children":1108},{"style":325},[1109],{"type":45,"value":1110},"channels",{"type":40,"tag":222,"props":1112,"children":1113},{"style":282},[1114],{"type":45,"value":363},{"type":40,"tag":222,"props":1116,"children":1117},{"style":276},[1118],{"type":45,"value":1119},"get",{"type":40,"tag":222,"props":1121,"children":1122},{"style":385},[1123],{"type":45,"value":285},{"type":40,"tag":222,"props":1125,"children":1126},{"style":325},[1127],{"type":45,"value":291},{"type":40,"tag":222,"props":1129,"children":1130},{"style":385},[1131],{"type":45,"value":513},{"type":40,"tag":222,"props":1133,"children":1134},{"style":282},[1135],{"type":45,"value":518},{"type":40,"tag":222,"props":1137,"children":1138},{"class":224,"line":371},[1139,1144,1148,1153,1158,1163,1168,1173,1178,1182,1186,1191,1195,1199],{"type":40,"tag":222,"props":1140,"children":1141},{"style":345},[1142],{"type":45,"value":1143},"    if",{"type":40,"tag":222,"props":1145,"children":1146},{"style":385},[1147],{"type":45,"value":1057},{"type":40,"tag":222,"props":1149,"children":1150},{"style":282},[1151],{"type":45,"value":1152},"!",{"type":40,"tag":222,"props":1154,"children":1155},{"style":325},[1156],{"type":45,"value":1157},"channel",{"type":40,"tag":222,"props":1159,"children":1160},{"style":385},[1161],{"type":45,"value":1162},") ",{"type":40,"tag":222,"props":1164,"children":1165},{"style":345},[1166],{"type":45,"value":1167},"throw",{"type":40,"tag":222,"props":1169,"children":1170},{"style":282},[1171],{"type":45,"value":1172}," new",{"type":40,"tag":222,"props":1174,"children":1175},{"style":276},[1176],{"type":45,"value":1177}," Error",{"type":40,"tag":222,"props":1179,"children":1180},{"style":385},[1181],{"type":45,"value":285},{"type":40,"tag":222,"props":1183,"children":1184},{"style":282},[1185],{"type":45,"value":392},{"type":40,"tag":222,"props":1187,"children":1188},{"style":395},[1189],{"type":45,"value":1190},"Channel not found",{"type":40,"tag":222,"props":1192,"children":1193},{"style":282},[1194],{"type":45,"value":392},{"type":40,"tag":222,"props":1196,"children":1197},{"style":385},[1198],{"type":45,"value":513},{"type":40,"tag":222,"props":1200,"children":1201},{"style":282},[1202],{"type":45,"value":518},{"type":40,"tag":222,"props":1204,"children":1205},{"class":224,"line":437},[1206],{"type":40,"tag":222,"props":1207,"children":1208},{"emptyLinePlaceholder":255},[1209],{"type":45,"value":258},{"type":40,"tag":222,"props":1211,"children":1212},{"class":224,"line":488},[1213,1218,1222,1226,1231,1235,1240,1244],{"type":40,"tag":222,"props":1214,"children":1215},{"style":345},[1216],{"type":45,"value":1217},"    await",{"type":40,"tag":222,"props":1219,"children":1220},{"style":325},[1221],{"type":45,"value":1101},{"type":40,"tag":222,"props":1223,"children":1224},{"style":282},[1225],{"type":45,"value":363},{"type":40,"tag":222,"props":1227,"children":1228},{"style":325},[1229],{"type":45,"value":1230},"messages",{"type":40,"tag":222,"props":1232,"children":1233},{"style":282},[1234],{"type":45,"value":363},{"type":40,"tag":222,"props":1236,"children":1237},{"style":276},[1238],{"type":45,"value":1239},"insert",{"type":40,"tag":222,"props":1241,"children":1242},{"style":385},[1243],{"type":45,"value":285},{"type":40,"tag":222,"props":1245,"children":1246},{"style":282},[1247],{"type":45,"value":1248},"{\n",{"type":40,"tag":222,"props":1250,"children":1251},{"class":224,"line":521},[1252,1257],{"type":40,"tag":222,"props":1253,"children":1254},{"style":325},[1255],{"type":45,"value":1256},"      channelId",{"type":40,"tag":222,"props":1258,"children":1259},{"style":282},[1260],{"type":45,"value":1261},",\n",{"type":40,"tag":222,"props":1263,"children":1264},{"class":224,"line":717},[1265,1270,1274,1278,1282,1287],{"type":40,"tag":222,"props":1266,"children":1267},{"style":385},[1268],{"type":45,"value":1269},"      authorId",{"type":40,"tag":222,"props":1271,"children":1272},{"style":282},[1273],{"type":45,"value":296},{"type":40,"tag":222,"props":1275,"children":1276},{"style":325},[1277],{"type":45,"value":984},{"type":40,"tag":222,"props":1279,"children":1280},{"style":282},[1281],{"type":45,"value":363},{"type":40,"tag":222,"props":1283,"children":1284},{"style":325},[1285],{"type":45,"value":1286},"id",{"type":40,"tag":222,"props":1288,"children":1289},{"style":282},[1290],{"type":45,"value":1261},{"type":40,"tag":222,"props":1292,"children":1293},{"class":224,"line":725},[1294,1299],{"type":40,"tag":222,"props":1295,"children":1296},{"style":325},[1297],{"type":45,"value":1298},"      body",{"type":40,"tag":222,"props":1300,"children":1301},{"style":282},[1302],{"type":45,"value":1261},{"type":40,"tag":222,"props":1304,"children":1305},{"class":224,"line":742},[1306,1311,1315,1320,1324,1329,1333],{"type":40,"tag":222,"props":1307,"children":1308},{"style":385},[1309],{"type":45,"value":1310},"      createdAt",{"type":40,"tag":222,"props":1312,"children":1313},{"style":282},[1314],{"type":45,"value":296},{"type":40,"tag":222,"props":1316,"children":1317},{"style":325},[1318],{"type":45,"value":1319}," Date",{"type":40,"tag":222,"props":1321,"children":1322},{"style":282},[1323],{"type":45,"value":363},{"type":40,"tag":222,"props":1325,"children":1326},{"style":276},[1327],{"type":45,"value":1328},"now",{"type":40,"tag":222,"props":1330,"children":1331},{"style":385},[1332],{"type":45,"value":1003},{"type":40,"tag":222,"props":1334,"children":1335},{"style":282},[1336],{"type":45,"value":1261},{"type":40,"tag":222,"props":1338,"children":1339},{"class":224,"line":762},[1340,1345,1349],{"type":40,"tag":222,"props":1341,"children":1342},{"style":282},[1343],{"type":45,"value":1344},"    }",{"type":40,"tag":222,"props":1346,"children":1347},{"style":385},[1348],{"type":45,"value":513},{"type":40,"tag":222,"props":1350,"children":1351},{"style":282},[1352],{"type":45,"value":518},{"type":40,"tag":222,"props":1354,"children":1355},{"class":224,"line":783},[1356],{"type":40,"tag":222,"props":1357,"children":1358},{"style":385},[1359],{"type":45,"value":1360},"    \n",{"type":40,"tag":222,"props":1362,"children":1363},{"class":224,"line":805},[1364,1368,1372,1376,1380,1384,1389,1393,1397,1401],{"type":40,"tag":222,"props":1365,"children":1366},{"style":345},[1367],{"type":45,"value":1217},{"type":40,"tag":222,"props":1369,"children":1370},{"style":325},[1371],{"type":45,"value":1101},{"type":40,"tag":222,"props":1373,"children":1374},{"style":282},[1375],{"type":45,"value":363},{"type":40,"tag":222,"props":1377,"children":1378},{"style":325},[1379],{"type":45,"value":1110},{"type":40,"tag":222,"props":1381,"children":1382},{"style":282},[1383],{"type":45,"value":363},{"type":40,"tag":222,"props":1385,"children":1386},{"style":276},[1387],{"type":45,"value":1388},"update",{"type":40,"tag":222,"props":1390,"children":1391},{"style":385},[1392],{"type":45,"value":285},{"type":40,"tag":222,"props":1394,"children":1395},{"style":325},[1396],{"type":45,"value":291},{"type":40,"tag":222,"props":1398,"children":1399},{"style":282},[1400],{"type":45,"value":406},{"type":40,"tag":222,"props":1402,"children":1403},{"style":282},[1404],{"type":45,"value":338},{"type":40,"tag":222,"props":1406,"children":1407},{"class":224,"line":813},[1408,1413,1417,1421,1425,1429,1433],{"type":40,"tag":222,"props":1409,"children":1410},{"style":385},[1411],{"type":45,"value":1412},"      lastMessageAt",{"type":40,"tag":222,"props":1414,"children":1415},{"style":282},[1416],{"type":45,"value":296},{"type":40,"tag":222,"props":1418,"children":1419},{"style":325},[1420],{"type":45,"value":1319},{"type":40,"tag":222,"props":1422,"children":1423},{"style":282},[1424],{"type":45,"value":363},{"type":40,"tag":222,"props":1426,"children":1427},{"style":276},[1428],{"type":45,"value":1328},{"type":40,"tag":222,"props":1430,"children":1431},{"style":385},[1432],{"type":45,"value":1003},{"type":40,"tag":222,"props":1434,"children":1435},{"style":282},[1436],{"type":45,"value":1261},{"type":40,"tag":222,"props":1438,"children":1439},{"class":224,"line":822},[1440,1444,1448],{"type":40,"tag":222,"props":1441,"children":1442},{"style":282},[1443],{"type":45,"value":1344},{"type":40,"tag":222,"props":1445,"children":1446},{"style":385},[1447],{"type":45,"value":513},{"type":40,"tag":222,"props":1449,"children":1450},{"style":282},[1451],{"type":45,"value":518},{"type":40,"tag":222,"props":1453,"children":1454},{"class":224,"line":831},[1455,1460,1464],{"type":40,"tag":222,"props":1456,"children":1457},{"style":282},[1458],{"type":45,"value":1459},"  }",{"type":40,"tag":222,"props":1461,"children":1462},{"style":385},[1463],{"type":45,"value":513},{"type":40,"tag":222,"props":1465,"children":1466},{"style":282},[1467],{"type":45,"value":518},{"type":40,"tag":222,"props":1469,"children":1471},{"class":224,"line":1470},20,[1472],{"type":40,"tag":222,"props":1473,"children":1474},{"style":282},[1475],{"type":45,"value":527},{"type":40,"tag":142,"props":1477,"children":1479},{"id":1478},"_7-no-request-waterfalls",[1480],{"type":45,"value":1481},"7. No Request Waterfalls",{"type":40,"tag":41,"props":1483,"children":1484},{},[1485],{"type":45,"value":1486},"Server-side composition means loading related data in a single round trip. Don't force clients to make serial fetches.",{"type":40,"tag":41,"props":1488,"children":1489},{},[1490],{"type":45,"value":1491},"A query function can load messages AND their authors in one call. Not messages first, then N author lookups. The server has direct database access — use it.",{"type":40,"tag":210,"props":1493,"children":1495},{"className":212,"code":1494,"language":214,"meta":215,"style":215},"async function getMessagesWithAuthors(channelId: ChannelId): Promise\u003CMessageWithAuthor[]> {\n  const messages = await db.messages\n    .where(\"channelId\", \"==\", channelId)\n    .orderBy(\"createdAt\", \"desc\")\n    .limit(50);\n\n  \u002F\u002F Batch load authors to avoid N+1\n  const authorIds = [...new Set(messages.map(m => m.authorId))];\n  const authors = await db.users.getMany(authorIds);\n  const authorMap = new Map(authors.map(a => [a.id, a]));\n\n  return messages.map(msg => ({\n    ...msg,\n    author: authorMap.get(msg.authorId),\n  }));\n}\n",[1496],{"type":40,"tag":218,"props":1497,"children":1498},{"__ignoreMap":215},[1499,1560,1592,1647,1694,1721,1728,1736,1820,1879,1968,1975,2015,2031,2079,2095],{"type":40,"tag":222,"props":1500,"children":1501},{"class":224,"line":28},[1502,1506,1510,1515,1519,1523,1527,1531,1535,1539,1543,1548,1552,1556],{"type":40,"tag":222,"props":1503,"children":1504},{"style":265},[1505],{"type":45,"value":268},{"type":40,"tag":222,"props":1507,"children":1508},{"style":265},[1509],{"type":45,"value":273},{"type":40,"tag":222,"props":1511,"children":1512},{"style":276},[1513],{"type":45,"value":1514}," getMessagesWithAuthors",{"type":40,"tag":222,"props":1516,"children":1517},{"style":282},[1518],{"type":45,"value":285},{"type":40,"tag":222,"props":1520,"children":1521},{"style":288},[1522],{"type":45,"value":291},{"type":40,"tag":222,"props":1524,"children":1525},{"style":282},[1526],{"type":45,"value":296},{"type":40,"tag":222,"props":1528,"children":1529},{"style":299},[1530],{"type":45,"value":302},{"type":40,"tag":222,"props":1532,"children":1533},{"style":282},[1534],{"type":45,"value":307},{"type":40,"tag":222,"props":1536,"children":1537},{"style":299},[1538],{"type":45,"value":312},{"type":40,"tag":222,"props":1540,"children":1541},{"style":282},[1542],{"type":45,"value":317},{"type":40,"tag":222,"props":1544,"children":1545},{"style":299},[1546],{"type":45,"value":1547},"MessageWithAuthor",{"type":40,"tag":222,"props":1549,"children":1550},{"style":325},[1551],{"type":45,"value":328},{"type":40,"tag":222,"props":1553,"children":1554},{"style":282},[1555],{"type":45,"value":333},{"type":40,"tag":222,"props":1557,"children":1558},{"style":282},[1559],{"type":45,"value":338},{"type":40,"tag":222,"props":1561,"children":1562},{"class":224,"line":24},[1563,1567,1572,1576,1580,1584,1588],{"type":40,"tag":222,"props":1564,"children":1565},{"style":265},[1566],{"type":45,"value":979},{"type":40,"tag":222,"props":1568,"children":1569},{"style":325},[1570],{"type":45,"value":1571}," messages",{"type":40,"tag":222,"props":1573,"children":1574},{"style":282},[1575],{"type":45,"value":989},{"type":40,"tag":222,"props":1577,"children":1578},{"style":345},[1579],{"type":45,"value":353},{"type":40,"tag":222,"props":1581,"children":1582},{"style":325},[1583],{"type":45,"value":358},{"type":40,"tag":222,"props":1585,"children":1586},{"style":282},[1587],{"type":45,"value":363},{"type":40,"tag":222,"props":1589,"children":1590},{"style":325},[1591],{"type":45,"value":368},{"type":40,"tag":222,"props":1593,"children":1594},{"class":224,"line":242},[1595,1599,1603,1607,1611,1615,1619,1623,1627,1631,1635,1639,1643],{"type":40,"tag":222,"props":1596,"children":1597},{"style":282},[1598],{"type":45,"value":377},{"type":40,"tag":222,"props":1600,"children":1601},{"style":276},[1602],{"type":45,"value":382},{"type":40,"tag":222,"props":1604,"children":1605},{"style":385},[1606],{"type":45,"value":285},{"type":40,"tag":222,"props":1608,"children":1609},{"style":282},[1610],{"type":45,"value":392},{"type":40,"tag":222,"props":1612,"children":1613},{"style":395},[1614],{"type":45,"value":291},{"type":40,"tag":222,"props":1616,"children":1617},{"style":282},[1618],{"type":45,"value":392},{"type":40,"tag":222,"props":1620,"children":1621},{"style":282},[1622],{"type":45,"value":406},{"type":40,"tag":222,"props":1624,"children":1625},{"style":282},[1626],{"type":45,"value":411},{"type":40,"tag":222,"props":1628,"children":1629},{"style":395},[1630],{"type":45,"value":416},{"type":40,"tag":222,"props":1632,"children":1633},{"style":282},[1634],{"type":45,"value":392},{"type":40,"tag":222,"props":1636,"children":1637},{"style":282},[1638],{"type":45,"value":406},{"type":40,"tag":222,"props":1640,"children":1641},{"style":325},[1642],{"type":45,"value":429},{"type":40,"tag":222,"props":1644,"children":1645},{"style":385},[1646],{"type":45,"value":434},{"type":40,"tag":222,"props":1648,"children":1649},{"class":224,"line":251},[1650,1654,1658,1662,1666,1670,1674,1678,1682,1686,1690],{"type":40,"tag":222,"props":1651,"children":1652},{"style":282},[1653],{"type":45,"value":377},{"type":40,"tag":222,"props":1655,"children":1656},{"style":276},[1657],{"type":45,"value":447},{"type":40,"tag":222,"props":1659,"children":1660},{"style":385},[1661],{"type":45,"value":285},{"type":40,"tag":222,"props":1663,"children":1664},{"style":282},[1665],{"type":45,"value":392},{"type":40,"tag":222,"props":1667,"children":1668},{"style":395},[1669],{"type":45,"value":460},{"type":40,"tag":222,"props":1671,"children":1672},{"style":282},[1673],{"type":45,"value":392},{"type":40,"tag":222,"props":1675,"children":1676},{"style":282},[1677],{"type":45,"value":406},{"type":40,"tag":222,"props":1679,"children":1680},{"style":282},[1681],{"type":45,"value":411},{"type":40,"tag":222,"props":1683,"children":1684},{"style":395},[1685],{"type":45,"value":477},{"type":40,"tag":222,"props":1687,"children":1688},{"style":282},[1689],{"type":45,"value":392},{"type":40,"tag":222,"props":1691,"children":1692},{"style":385},[1693],{"type":45,"value":434},{"type":40,"tag":222,"props":1695,"children":1696},{"class":224,"line":261},[1697,1701,1705,1709,1713,1717],{"type":40,"tag":222,"props":1698,"children":1699},{"style":282},[1700],{"type":45,"value":377},{"type":40,"tag":222,"props":1702,"children":1703},{"style":276},[1704],{"type":45,"value":498},{"type":40,"tag":222,"props":1706,"children":1707},{"style":385},[1708],{"type":45,"value":285},{"type":40,"tag":222,"props":1710,"children":1711},{"style":505},[1712],{"type":45,"value":508},{"type":40,"tag":222,"props":1714,"children":1715},{"style":385},[1716],{"type":45,"value":513},{"type":40,"tag":222,"props":1718,"children":1719},{"style":282},[1720],{"type":45,"value":518},{"type":40,"tag":222,"props":1722,"children":1723},{"class":224,"line":341},[1724],{"type":40,"tag":222,"props":1725,"children":1726},{"emptyLinePlaceholder":255},[1727],{"type":45,"value":258},{"type":40,"tag":222,"props":1729,"children":1730},{"class":224,"line":371},[1731],{"type":40,"tag":222,"props":1732,"children":1733},{"style":228},[1734],{"type":45,"value":1735},"  \u002F\u002F Batch load authors to avoid N+1\n",{"type":40,"tag":222,"props":1737,"children":1738},{"class":224,"line":437},[1739,1743,1748,1752,1757,1762,1767,1771,1775,1779,1784,1788,1793,1797,1802,1806,1811,1816],{"type":40,"tag":222,"props":1740,"children":1741},{"style":265},[1742],{"type":45,"value":979},{"type":40,"tag":222,"props":1744,"children":1745},{"style":325},[1746],{"type":45,"value":1747}," authorIds",{"type":40,"tag":222,"props":1749,"children":1750},{"style":282},[1751],{"type":45,"value":989},{"type":40,"tag":222,"props":1753,"children":1754},{"style":385},[1755],{"type":45,"value":1756}," [",{"type":40,"tag":222,"props":1758,"children":1759},{"style":282},[1760],{"type":45,"value":1761},"...new",{"type":40,"tag":222,"props":1763,"children":1764},{"style":276},[1765],{"type":45,"value":1766}," Set",{"type":40,"tag":222,"props":1768,"children":1769},{"style":385},[1770],{"type":45,"value":285},{"type":40,"tag":222,"props":1772,"children":1773},{"style":325},[1774],{"type":45,"value":1230},{"type":40,"tag":222,"props":1776,"children":1777},{"style":282},[1778],{"type":45,"value":363},{"type":40,"tag":222,"props":1780,"children":1781},{"style":276},[1782],{"type":45,"value":1783},"map",{"type":40,"tag":222,"props":1785,"children":1786},{"style":385},[1787],{"type":45,"value":285},{"type":40,"tag":222,"props":1789,"children":1790},{"style":288},[1791],{"type":45,"value":1792},"m",{"type":40,"tag":222,"props":1794,"children":1795},{"style":265},[1796],{"type":45,"value":1071},{"type":40,"tag":222,"props":1798,"children":1799},{"style":325},[1800],{"type":45,"value":1801}," m",{"type":40,"tag":222,"props":1803,"children":1804},{"style":282},[1805],{"type":45,"value":363},{"type":40,"tag":222,"props":1807,"children":1808},{"style":325},[1809],{"type":45,"value":1810},"authorId",{"type":40,"tag":222,"props":1812,"children":1813},{"style":385},[1814],{"type":45,"value":1815},"))]",{"type":40,"tag":222,"props":1817,"children":1818},{"style":282},[1819],{"type":45,"value":518},{"type":40,"tag":222,"props":1821,"children":1822},{"class":224,"line":488},[1823,1827,1832,1836,1840,1844,1848,1853,1857,1862,1866,1871,1875],{"type":40,"tag":222,"props":1824,"children":1825},{"style":265},[1826],{"type":45,"value":979},{"type":40,"tag":222,"props":1828,"children":1829},{"style":325},[1830],{"type":45,"value":1831}," authors",{"type":40,"tag":222,"props":1833,"children":1834},{"style":282},[1835],{"type":45,"value":989},{"type":40,"tag":222,"props":1837,"children":1838},{"style":345},[1839],{"type":45,"value":353},{"type":40,"tag":222,"props":1841,"children":1842},{"style":325},[1843],{"type":45,"value":358},{"type":40,"tag":222,"props":1845,"children":1846},{"style":282},[1847],{"type":45,"value":363},{"type":40,"tag":222,"props":1849,"children":1850},{"style":325},[1851],{"type":45,"value":1852},"users",{"type":40,"tag":222,"props":1854,"children":1855},{"style":282},[1856],{"type":45,"value":363},{"type":40,"tag":222,"props":1858,"children":1859},{"style":276},[1860],{"type":45,"value":1861},"getMany",{"type":40,"tag":222,"props":1863,"children":1864},{"style":385},[1865],{"type":45,"value":285},{"type":40,"tag":222,"props":1867,"children":1868},{"style":325},[1869],{"type":45,"value":1870},"authorIds",{"type":40,"tag":222,"props":1872,"children":1873},{"style":385},[1874],{"type":45,"value":513},{"type":40,"tag":222,"props":1876,"children":1877},{"style":282},[1878],{"type":45,"value":518},{"type":40,"tag":222,"props":1880,"children":1881},{"class":224,"line":521},[1882,1886,1891,1895,1899,1904,1908,1913,1917,1921,1925,1930,1934,1938,1942,1946,1950,1954,1959,1964],{"type":40,"tag":222,"props":1883,"children":1884},{"style":265},[1885],{"type":45,"value":979},{"type":40,"tag":222,"props":1887,"children":1888},{"style":325},[1889],{"type":45,"value":1890}," authorMap",{"type":40,"tag":222,"props":1892,"children":1893},{"style":282},[1894],{"type":45,"value":989},{"type":40,"tag":222,"props":1896,"children":1897},{"style":282},[1898],{"type":45,"value":1172},{"type":40,"tag":222,"props":1900,"children":1901},{"style":276},[1902],{"type":45,"value":1903}," Map",{"type":40,"tag":222,"props":1905,"children":1906},{"style":385},[1907],{"type":45,"value":285},{"type":40,"tag":222,"props":1909,"children":1910},{"style":325},[1911],{"type":45,"value":1912},"authors",{"type":40,"tag":222,"props":1914,"children":1915},{"style":282},[1916],{"type":45,"value":363},{"type":40,"tag":222,"props":1918,"children":1919},{"style":276},[1920],{"type":45,"value":1783},{"type":40,"tag":222,"props":1922,"children":1923},{"style":385},[1924],{"type":45,"value":285},{"type":40,"tag":222,"props":1926,"children":1927},{"style":288},[1928],{"type":45,"value":1929},"a",{"type":40,"tag":222,"props":1931,"children":1932},{"style":265},[1933],{"type":45,"value":1071},{"type":40,"tag":222,"props":1935,"children":1936},{"style":385},[1937],{"type":45,"value":1756},{"type":40,"tag":222,"props":1939,"children":1940},{"style":325},[1941],{"type":45,"value":1929},{"type":40,"tag":222,"props":1943,"children":1944},{"style":282},[1945],{"type":45,"value":363},{"type":40,"tag":222,"props":1947,"children":1948},{"style":325},[1949],{"type":45,"value":1286},{"type":40,"tag":222,"props":1951,"children":1952},{"style":282},[1953],{"type":45,"value":406},{"type":40,"tag":222,"props":1955,"children":1956},{"style":325},[1957],{"type":45,"value":1958}," a",{"type":40,"tag":222,"props":1960,"children":1961},{"style":385},[1962],{"type":45,"value":1963},"]))",{"type":40,"tag":222,"props":1965,"children":1966},{"style":282},[1967],{"type":45,"value":518},{"type":40,"tag":222,"props":1969,"children":1970},{"class":224,"line":717},[1971],{"type":40,"tag":222,"props":1972,"children":1973},{"emptyLinePlaceholder":255},[1974],{"type":45,"value":258},{"type":40,"tag":222,"props":1976,"children":1977},{"class":224,"line":725},[1978,1982,1986,1990,1994,1998,2003,2007,2011],{"type":40,"tag":222,"props":1979,"children":1980},{"style":345},[1981],{"type":45,"value":348},{"type":40,"tag":222,"props":1983,"children":1984},{"style":325},[1985],{"type":45,"value":1571},{"type":40,"tag":222,"props":1987,"children":1988},{"style":282},[1989],{"type":45,"value":363},{"type":40,"tag":222,"props":1991,"children":1992},{"style":276},[1993],{"type":45,"value":1783},{"type":40,"tag":222,"props":1995,"children":1996},{"style":385},[1997],{"type":45,"value":285},{"type":40,"tag":222,"props":1999,"children":2000},{"style":288},[2001],{"type":45,"value":2002},"msg",{"type":40,"tag":222,"props":2004,"children":2005},{"style":265},[2006],{"type":45,"value":1071},{"type":40,"tag":222,"props":2008,"children":2009},{"style":385},[2010],{"type":45,"value":1057},{"type":40,"tag":222,"props":2012,"children":2013},{"style":282},[2014],{"type":45,"value":1248},{"type":40,"tag":222,"props":2016,"children":2017},{"class":224,"line":742},[2018,2023,2027],{"type":40,"tag":222,"props":2019,"children":2020},{"style":282},[2021],{"type":45,"value":2022},"    ...",{"type":40,"tag":222,"props":2024,"children":2025},{"style":325},[2026],{"type":45,"value":2002},{"type":40,"tag":222,"props":2028,"children":2029},{"style":282},[2030],{"type":45,"value":1261},{"type":40,"tag":222,"props":2032,"children":2033},{"class":224,"line":762},[2034,2039,2043,2047,2051,2055,2059,2063,2067,2071,2075],{"type":40,"tag":222,"props":2035,"children":2036},{"style":385},[2037],{"type":45,"value":2038},"    author",{"type":40,"tag":222,"props":2040,"children":2041},{"style":282},[2042],{"type":45,"value":296},{"type":40,"tag":222,"props":2044,"children":2045},{"style":325},[2046],{"type":45,"value":1890},{"type":40,"tag":222,"props":2048,"children":2049},{"style":282},[2050],{"type":45,"value":363},{"type":40,"tag":222,"props":2052,"children":2053},{"style":276},[2054],{"type":45,"value":1119},{"type":40,"tag":222,"props":2056,"children":2057},{"style":385},[2058],{"type":45,"value":285},{"type":40,"tag":222,"props":2060,"children":2061},{"style":325},[2062],{"type":45,"value":2002},{"type":40,"tag":222,"props":2064,"children":2065},{"style":282},[2066],{"type":45,"value":363},{"type":40,"tag":222,"props":2068,"children":2069},{"style":325},[2070],{"type":45,"value":1810},{"type":40,"tag":222,"props":2072,"children":2073},{"style":385},[2074],{"type":45,"value":513},{"type":40,"tag":222,"props":2076,"children":2077},{"style":282},[2078],{"type":45,"value":1261},{"type":40,"tag":222,"props":2080,"children":2081},{"class":224,"line":783},[2082,2086,2091],{"type":40,"tag":222,"props":2083,"children":2084},{"style":282},[2085],{"type":45,"value":1459},{"type":40,"tag":222,"props":2087,"children":2088},{"style":385},[2089],{"type":45,"value":2090},"))",{"type":40,"tag":222,"props":2092,"children":2093},{"style":282},[2094],{"type":45,"value":518},{"type":40,"tag":222,"props":2096,"children":2097},{"class":224,"line":805},[2098],{"type":40,"tag":222,"props":2099,"children":2100},{"style":282},[2101],{"type":45,"value":527},{"type":40,"tag":41,"props":2103,"children":2104},{},[2105],{"type":45,"value":2106},"Clients get exactly the data shape they need in one request. If using a reactive system, any author's name change triggers an update automatically.",{"type":40,"tag":142,"props":2108,"children":2110},{"id":2109},"_8-colocated-server-logic",[2111],{"type":45,"value":2112},"8. Colocated Server Logic",{"type":40,"tag":41,"props":2114,"children":2115},{},[2116,2118,2124,2126,2132,2133,2139,2140,2146],{"type":45,"value":2117},"Queries, mutations, and helper functions live together, organized by domain. Not split across ",{"type":40,"tag":218,"props":2119,"children":2121},{"className":2120},[],[2122],{"type":45,"value":2123},"routes\u002F",{"type":45,"value":2125},", ",{"type":40,"tag":218,"props":2127,"children":2129},{"className":2128},[],[2130],{"type":45,"value":2131},"controllers\u002F",{"type":45,"value":2125},{"type":40,"tag":218,"props":2134,"children":2136},{"className":2135},[],[2137],{"type":45,"value":2138},"services\u002F",{"type":45,"value":2125},{"type":40,"tag":218,"props":2141,"children":2143},{"className":2142},[],[2144],{"type":45,"value":2145},"repositories\u002F",{"type":45,"value":2147}," layers.",{"type":40,"tag":41,"props":2149,"children":2150},{},[2151],{"type":45,"value":2152},"Understanding an operation should mean reading one file, not tracing through four layers of indirection. Colocation reduces cognitive load and makes the codebase navigable for both humans and AI agents.",{"type":40,"tag":210,"props":2154,"children":2158},{"className":2155,"code":2157,"language":45},[2156],"language-text","server\u002F\n  messages.ts      # queries + mutations for messages\n  channels.ts      # queries + mutations for channels\n  users.ts         # queries + mutations for users\n  schema.ts        # the data model\n  helpers\u002F         # shared utilities (auth, validation)\nclient\u002F\n",[2159],{"type":40,"tag":218,"props":2160,"children":2161},{"__ignoreMap":215},[2162],{"type":45,"value":2157},{"type":40,"tag":142,"props":2164,"children":2166},{"id":2165},"_9-agent-friendly-dx",[2167],{"type":45,"value":2168},"9. Agent-Friendly DX",{"type":40,"tag":41,"props":2170,"children":2171},{},[2172],{"type":45,"value":2173},"Function signatures are self-documenting. Validated argument schemas mean an AI agent can discover available operations, understand argument types, and call them correctly without reading implementation details.",{"type":40,"tag":41,"props":2175,"children":2176},{},[2177],{"type":45,"value":2178},"Design for the \"pit of success\" — the correct implementation is the easy path. Wrong usage should fail at compile time or with a clear validation error, not silently produce incorrect results.",{"type":40,"tag":41,"props":2180,"children":2181},{},[2182],{"type":45,"value":2183},"Clear contracts beat clever abstractions. An explicit function with typed args is better than a magical ORM method that hides what query it generates.",{"type":40,"tag":142,"props":2185,"children":2187},{"id":2186},"_10-minimal-infrastructure-burden",[2188],{"type":45,"value":2189},"10. Minimal Infrastructure Burden",{"type":40,"tag":41,"props":2191,"children":2192},{},[2193],{"type":45,"value":2194},"The backend handles scaling, caching, connection pooling, and deployment automatically where possible.",{"type":40,"tag":41,"props":2196,"children":2197},{},[2198],{"type":45,"value":2199},"Prefer managed infrastructure that handles scaling, caching, and deployment automatically. The less operational burden you own, the more you can focus on product logic.",{"type":40,"tag":41,"props":2201,"children":2202},{},[2203],{"type":45,"value":2204},"Built-in query caching with automatic invalidation when underlying data changes. No manual cache keys. No TTLs to tune. No stale data bugs because you forgot to invalidate after a write.",{"type":40,"tag":142,"props":2206,"children":2208},{"id":2207},"_11-use-platform-primitives",[2209],{"type":45,"value":2210},"11. Use Platform Primitives",{"type":40,"tag":41,"props":2212,"children":2213},{},[2214],{"type":45,"value":2215},"Auth, file storage, scheduled jobs, vector search, and text search should be first-class features of your platform or well-integrated services. Don't bolt on five loosely-coupled external services when cohesive solutions exist.",{"type":40,"tag":41,"props":2217,"children":2218},{},[2219],{"type":45,"value":2220},"Keep external API calls (sending emails, processing payments) separate from database transactions so they don't block or degrade core performance.",{"type":40,"tag":210,"props":2222,"children":2224},{"className":212,"code":2223,"language":214,"meta":215,"style":215},"\u002F\u002F Scheduled cleanup — use your platform's native scheduler\n\u002F\u002F (cloud functions, cron jobs, built-in scheduling, etc.)\n\nasync function cleanupExpiredTokens(): Promise\u003Cvoid> {\n  const expired = await db.tokens\n    .where(\"expiresAt\", \"\u003C\", Date.now())\n    .limit(1000);\n\n  await db.transaction(async (tx) => {\n    for (const token of expired) {\n      await tx.tokens.delete(token.id);\n    }\n  });\n}\n\n\u002F\u002F Schedule: run every hour, or trigger after token creation\n",[2225],{"type":40,"tag":218,"props":2226,"children":2227},{"__ignoreMap":215},[2228,2236,2244,2251,2292,2325,2390,2418,2425,2472,2511,2566,2574,2589,2596,2603],{"type":40,"tag":222,"props":2229,"children":2230},{"class":224,"line":28},[2231],{"type":40,"tag":222,"props":2232,"children":2233},{"style":228},[2234],{"type":45,"value":2235},"\u002F\u002F Scheduled cleanup — use your platform's native scheduler\n",{"type":40,"tag":222,"props":2237,"children":2238},{"class":224,"line":24},[2239],{"type":40,"tag":222,"props":2240,"children":2241},{"style":228},[2242],{"type":45,"value":2243},"\u002F\u002F (cloud functions, cron jobs, built-in scheduling, etc.)\n",{"type":40,"tag":222,"props":2245,"children":2246},{"class":224,"line":242},[2247],{"type":40,"tag":222,"props":2248,"children":2249},{"emptyLinePlaceholder":255},[2250],{"type":45,"value":258},{"type":40,"tag":222,"props":2252,"children":2253},{"class":224,"line":251},[2254,2258,2262,2267,2272,2276,2280,2284,2288],{"type":40,"tag":222,"props":2255,"children":2256},{"style":265},[2257],{"type":45,"value":268},{"type":40,"tag":222,"props":2259,"children":2260},{"style":265},[2261],{"type":45,"value":273},{"type":40,"tag":222,"props":2263,"children":2264},{"style":276},[2265],{"type":45,"value":2266}," cleanupExpiredTokens",{"type":40,"tag":222,"props":2268,"children":2269},{"style":282},[2270],{"type":45,"value":2271},"():",{"type":40,"tag":222,"props":2273,"children":2274},{"style":299},[2275],{"type":45,"value":312},{"type":40,"tag":222,"props":2277,"children":2278},{"style":282},[2279],{"type":45,"value":317},{"type":40,"tag":222,"props":2281,"children":2282},{"style":299},[2283],{"type":45,"value":963},{"type":40,"tag":222,"props":2285,"children":2286},{"style":282},[2287],{"type":45,"value":333},{"type":40,"tag":222,"props":2289,"children":2290},{"style":282},[2291],{"type":45,"value":338},{"type":40,"tag":222,"props":2293,"children":2294},{"class":224,"line":261},[2295,2299,2304,2308,2312,2316,2320],{"type":40,"tag":222,"props":2296,"children":2297},{"style":265},[2298],{"type":45,"value":979},{"type":40,"tag":222,"props":2300,"children":2301},{"style":325},[2302],{"type":45,"value":2303}," expired",{"type":40,"tag":222,"props":2305,"children":2306},{"style":282},[2307],{"type":45,"value":989},{"type":40,"tag":222,"props":2309,"children":2310},{"style":345},[2311],{"type":45,"value":353},{"type":40,"tag":222,"props":2313,"children":2314},{"style":325},[2315],{"type":45,"value":358},{"type":40,"tag":222,"props":2317,"children":2318},{"style":282},[2319],{"type":45,"value":363},{"type":40,"tag":222,"props":2321,"children":2322},{"style":325},[2323],{"type":45,"value":2324},"tokens\n",{"type":40,"tag":222,"props":2326,"children":2327},{"class":224,"line":341},[2328,2332,2336,2340,2344,2349,2353,2357,2361,2365,2369,2373,2377,2381,2385],{"type":40,"tag":222,"props":2329,"children":2330},{"style":282},[2331],{"type":45,"value":377},{"type":40,"tag":222,"props":2333,"children":2334},{"style":276},[2335],{"type":45,"value":382},{"type":40,"tag":222,"props":2337,"children":2338},{"style":385},[2339],{"type":45,"value":285},{"type":40,"tag":222,"props":2341,"children":2342},{"style":282},[2343],{"type":45,"value":392},{"type":40,"tag":222,"props":2345,"children":2346},{"style":395},[2347],{"type":45,"value":2348},"expiresAt",{"type":40,"tag":222,"props":2350,"children":2351},{"style":282},[2352],{"type":45,"value":392},{"type":40,"tag":222,"props":2354,"children":2355},{"style":282},[2356],{"type":45,"value":406},{"type":40,"tag":222,"props":2358,"children":2359},{"style":282},[2360],{"type":45,"value":411},{"type":40,"tag":222,"props":2362,"children":2363},{"style":395},[2364],{"type":45,"value":317},{"type":40,"tag":222,"props":2366,"children":2367},{"style":282},[2368],{"type":45,"value":392},{"type":40,"tag":222,"props":2370,"children":2371},{"style":282},[2372],{"type":45,"value":406},{"type":40,"tag":222,"props":2374,"children":2375},{"style":325},[2376],{"type":45,"value":1319},{"type":40,"tag":222,"props":2378,"children":2379},{"style":282},[2380],{"type":45,"value":363},{"type":40,"tag":222,"props":2382,"children":2383},{"style":276},[2384],{"type":45,"value":1328},{"type":40,"tag":222,"props":2386,"children":2387},{"style":385},[2388],{"type":45,"value":2389},"())\n",{"type":40,"tag":222,"props":2391,"children":2392},{"class":224,"line":371},[2393,2397,2401,2405,2410,2414],{"type":40,"tag":222,"props":2394,"children":2395},{"style":282},[2396],{"type":45,"value":377},{"type":40,"tag":222,"props":2398,"children":2399},{"style":276},[2400],{"type":45,"value":498},{"type":40,"tag":222,"props":2402,"children":2403},{"style":385},[2404],{"type":45,"value":285},{"type":40,"tag":222,"props":2406,"children":2407},{"style":505},[2408],{"type":45,"value":2409},"1000",{"type":40,"tag":222,"props":2411,"children":2412},{"style":385},[2413],{"type":45,"value":513},{"type":40,"tag":222,"props":2415,"children":2416},{"style":282},[2417],{"type":45,"value":518},{"type":40,"tag":222,"props":2419,"children":2420},{"class":224,"line":437},[2421],{"type":40,"tag":222,"props":2422,"children":2423},{"emptyLinePlaceholder":255},[2424],{"type":45,"value":258},{"type":40,"tag":222,"props":2426,"children":2427},{"class":224,"line":488},[2428,2432,2436,2440,2444,2448,2452,2456,2460,2464,2468],{"type":40,"tag":222,"props":2429,"children":2430},{"style":345},[2431],{"type":45,"value":1031},{"type":40,"tag":222,"props":2433,"children":2434},{"style":325},[2435],{"type":45,"value":358},{"type":40,"tag":222,"props":2437,"children":2438},{"style":282},[2439],{"type":45,"value":363},{"type":40,"tag":222,"props":2441,"children":2442},{"style":276},[2443],{"type":45,"value":1044},{"type":40,"tag":222,"props":2445,"children":2446},{"style":385},[2447],{"type":45,"value":285},{"type":40,"tag":222,"props":2449,"children":2450},{"style":265},[2451],{"type":45,"value":268},{"type":40,"tag":222,"props":2453,"children":2454},{"style":282},[2455],{"type":45,"value":1057},{"type":40,"tag":222,"props":2457,"children":2458},{"style":288},[2459],{"type":45,"value":1062},{"type":40,"tag":222,"props":2461,"children":2462},{"style":282},[2463],{"type":45,"value":513},{"type":40,"tag":222,"props":2465,"children":2466},{"style":265},[2467],{"type":45,"value":1071},{"type":40,"tag":222,"props":2469,"children":2470},{"style":282},[2471],{"type":45,"value":338},{"type":40,"tag":222,"props":2473,"children":2474},{"class":224,"line":521},[2475,2480,2484,2489,2494,2499,2503,2507],{"type":40,"tag":222,"props":2476,"children":2477},{"style":345},[2478],{"type":45,"value":2479},"    for",{"type":40,"tag":222,"props":2481,"children":2482},{"style":385},[2483],{"type":45,"value":1057},{"type":40,"tag":222,"props":2485,"children":2486},{"style":265},[2487],{"type":45,"value":2488},"const",{"type":40,"tag":222,"props":2490,"children":2491},{"style":325},[2492],{"type":45,"value":2493}," token",{"type":40,"tag":222,"props":2495,"children":2496},{"style":282},[2497],{"type":45,"value":2498}," of",{"type":40,"tag":222,"props":2500,"children":2501},{"style":325},[2502],{"type":45,"value":2303},{"type":40,"tag":222,"props":2504,"children":2505},{"style":385},[2506],{"type":45,"value":1162},{"type":40,"tag":222,"props":2508,"children":2509},{"style":282},[2510],{"type":45,"value":1248},{"type":40,"tag":222,"props":2512,"children":2513},{"class":224,"line":717},[2514,2519,2523,2527,2532,2536,2541,2545,2550,2554,2558,2562],{"type":40,"tag":222,"props":2515,"children":2516},{"style":345},[2517],{"type":45,"value":2518},"      await",{"type":40,"tag":222,"props":2520,"children":2521},{"style":325},[2522],{"type":45,"value":1101},{"type":40,"tag":222,"props":2524,"children":2525},{"style":282},[2526],{"type":45,"value":363},{"type":40,"tag":222,"props":2528,"children":2529},{"style":325},[2530],{"type":45,"value":2531},"tokens",{"type":40,"tag":222,"props":2533,"children":2534},{"style":282},[2535],{"type":45,"value":363},{"type":40,"tag":222,"props":2537,"children":2538},{"style":276},[2539],{"type":45,"value":2540},"delete",{"type":40,"tag":222,"props":2542,"children":2543},{"style":385},[2544],{"type":45,"value":285},{"type":40,"tag":222,"props":2546,"children":2547},{"style":325},[2548],{"type":45,"value":2549},"token",{"type":40,"tag":222,"props":2551,"children":2552},{"style":282},[2553],{"type":45,"value":363},{"type":40,"tag":222,"props":2555,"children":2556},{"style":325},[2557],{"type":45,"value":1286},{"type":40,"tag":222,"props":2559,"children":2560},{"style":385},[2561],{"type":45,"value":513},{"type":40,"tag":222,"props":2563,"children":2564},{"style":282},[2565],{"type":45,"value":518},{"type":40,"tag":222,"props":2567,"children":2568},{"class":224,"line":725},[2569],{"type":40,"tag":222,"props":2570,"children":2571},{"style":282},[2572],{"type":45,"value":2573},"    }\n",{"type":40,"tag":222,"props":2575,"children":2576},{"class":224,"line":742},[2577,2581,2585],{"type":40,"tag":222,"props":2578,"children":2579},{"style":282},[2580],{"type":45,"value":1459},{"type":40,"tag":222,"props":2582,"children":2583},{"style":385},[2584],{"type":45,"value":513},{"type":40,"tag":222,"props":2586,"children":2587},{"style":282},[2588],{"type":45,"value":518},{"type":40,"tag":222,"props":2590,"children":2591},{"class":224,"line":762},[2592],{"type":40,"tag":222,"props":2593,"children":2594},{"style":282},[2595],{"type":45,"value":527},{"type":40,"tag":222,"props":2597,"children":2598},{"class":224,"line":783},[2599],{"type":40,"tag":222,"props":2600,"children":2601},{"emptyLinePlaceholder":255},[2602],{"type":45,"value":258},{"type":40,"tag":222,"props":2604,"children":2605},{"class":224,"line":805},[2606],{"type":40,"tag":222,"props":2607,"children":2608},{"style":228},[2609],{"type":45,"value":2610},"\u002F\u002F Schedule: run every hour, or trigger after token creation\n",{"type":40,"tag":142,"props":2612,"children":2614},{"id":2613},"_12-optimistic-updates",[2615],{"type":45,"value":2616},"12. Optimistic Updates",{"type":40,"tag":41,"props":2618,"children":2619},{},[2620],{"type":45,"value":2621},"Mutations can describe their expected effect so UIs update instantly, before the server confirms. Latency becomes invisible to the user.",{"type":40,"tag":41,"props":2623,"children":2624},{},[2625],{"type":45,"value":2626},"The optimistic update runs client-side against a local cache. When the server confirms (or rejects), the real result replaces the optimistic one. Handle rollback on conflict.",{"type":40,"tag":41,"props":2628,"children":2629},{},[2630],{"type":40,"tag":167,"props":2631,"children":2632},{},[2633],{"type":45,"value":2634},"Implementations: React Query optimistic updates, Apollo Client, TanStack Query, SWR mutate.",{"type":40,"tag":142,"props":2636,"children":2638},{"id":2637},"_13-stateless-by-design",[2639],{"type":45,"value":2640},"13. Stateless by Design",{"type":40,"tag":41,"props":2642,"children":2643},{},[2644],{"type":45,"value":2645},"Server functions should not rely on in-memory state between requests. Any state lives in the database or a dedicated cache layer. This enables horizontal scaling — add more server instances without coordination.",{"type":40,"tag":41,"props":2647,"children":2648},{},[2649],{"type":45,"value":2650},"Session data, user context, and temporary state belong in persistent storage, not process memory. A request should be handleable by any server instance.",{"type":40,"tag":142,"props":2652,"children":2654},{"id":2653},"_14-graceful-degradation",[2655],{"type":45,"value":2656},"14. Graceful Degradation",{"type":40,"tag":41,"props":2658,"children":2659},{},[2660],{"type":45,"value":2661},"External dependencies fail. Design for it. Use timeouts on external calls. Implement circuit breakers for flaky services. Return partial results when possible rather than failing entirely.",{"type":40,"tag":41,"props":2663,"children":2664},{},[2665],{"type":45,"value":2666},"A user search that can't reach the recommendation service should still return basic results. A dashboard that can't load analytics should still show the data it can fetch.",{"type":40,"tag":142,"props":2668,"children":2670},{"id":2669},"_15-rate-limiting",[2671],{"type":45,"value":2672},"15. Rate Limiting",{"type":40,"tag":41,"props":2674,"children":2675},{},[2676],{"type":45,"value":2677},"Protect your backend from abuse and thundering herds. Implement rate limiting at the function level, not just at an API gateway. Different operations have different limits — a login endpoint needs stricter limits than a read-only query.",{"type":40,"tag":41,"props":2679,"children":2680},{},[2681],{"type":45,"value":2682},"Rate limits should return structured errors with retry-after information, not just reject requests silently.",{"type":40,"tag":64,"props":2684,"children":2686},{"id":2685},"anti-patterns",[2687],{"type":45,"value":2688},"Anti-Patterns",{"type":40,"tag":41,"props":2690,"children":2691},{},[2692],{"type":45,"value":2693},"These are the \"AI slop\" of backend architecture — patterns that look productive but create long-term pain:",{"type":40,"tag":76,"props":2695,"children":2696},{},[2697,2707,2717,2727,2737,2755,2765,2783,2793,2803,2813,2823,2833,2843,2861,2871,2881,2898,2908,2918],{"type":40,"tag":80,"props":2698,"children":2699},{},[2700,2705],{"type":40,"tag":51,"props":2701,"children":2702},{},[2703],{"type":45,"value":2704},"REST boilerplate factories",{"type":45,"value":2706}," — GET\u002FPOST\u002FPUT\u002FDELETE scaffolds for every resource, generating hundreds of lines of routing code that all do the same thing",{"type":40,"tag":80,"props":2708,"children":2709},{},[2710,2715],{"type":40,"tag":51,"props":2711,"children":2712},{},[2713],{"type":45,"value":2714},"Row-level security as the primary auth model",{"type":45,"value":2716}," — hard to reason about, hard to test, hard to compose. Security belongs in server functions, not in database policy DSLs",{"type":40,"tag":80,"props":2718,"children":2719},{},[2720,2725],{"type":40,"tag":51,"props":2721,"children":2722},{},[2723],{"type":45,"value":2724},"Direct client-to-database access",{"type":45,"value":2726}," — every production app eventually needs server-side logic; start there instead of discovering this truth at the worst possible time",{"type":40,"tag":80,"props":2728,"children":2729},{},[2730,2735],{"type":40,"tag":51,"props":2731,"children":2732},{},[2733],{"type":45,"value":2734},"ORMs that hide queries",{"type":45,"value":2736}," — magic methods that generate N+1 disasters, obscure what SQL actually runs, and make performance debugging a nightmare",{"type":40,"tag":80,"props":2738,"children":2739},{},[2740,2745,2747,2753],{"type":40,"tag":51,"props":2741,"children":2742},{},[2743],{"type":45,"value":2744},"Polling for freshness",{"type":45,"value":2746}," — ",{"type":40,"tag":218,"props":2748,"children":2750},{"className":2749},[],[2751],{"type":45,"value":2752},"setInterval(() => refetch(), 5000)",{"type":45,"value":2754}," when real-time subscriptions exist and are simpler to implement correctly",{"type":40,"tag":80,"props":2756,"children":2757},{},[2758,2763],{"type":40,"tag":51,"props":2759,"children":2760},{},[2761],{"type":45,"value":2762},"Separate real-time infrastructure",{"type":45,"value":2764}," — bolting a WebSocket service alongside your REST API and main database, creating two sources of truth",{"type":40,"tag":80,"props":2766,"children":2767},{},[2768,2773,2775,2781],{"type":40,"tag":51,"props":2769,"children":2770},{},[2771],{"type":45,"value":2772},"Manual cache invalidation",{"type":45,"value":2774}," — scattered ",{"type":40,"tag":218,"props":2776,"children":2778},{"className":2777},[],[2779],{"type":45,"value":2780},"cache.delete()",{"type":45,"value":2782}," calls that inevitably miss an edge case, showing stale data to users",{"type":40,"tag":80,"props":2784,"children":2785},{},[2786,2791],{"type":40,"tag":51,"props":2787,"children":2788},{},[2789],{"type":45,"value":2790},"Layered architecture",{"type":45,"value":2792}," — splitting a single logical operation across routes, controllers, services, repositories, and DTOs. Four files to understand one database read",{"type":40,"tag":80,"props":2794,"children":2795},{},[2796,2801],{"type":40,"tag":51,"props":2797,"children":2798},{},[2799],{"type":45,"value":2800},"Client-side request waterfalls",{"type":45,"value":2802}," — serial fetches that should be a single server-side composed query",{"type":40,"tag":80,"props":2804,"children":2805},{},[2806,2811],{"type":40,"tag":51,"props":2807,"children":2808},{},[2809],{"type":45,"value":2810},"Migration files as source of truth",{"type":45,"value":2812}," — delta migration files that you have to replay mentally to understand the current schema. Declarative schemas beat imperative migrations",{"type":40,"tag":80,"props":2814,"children":2815},{},[2816,2821],{"type":40,"tag":51,"props":2817,"children":2818},{},[2819],{"type":45,"value":2820},"Raw SQL string concatenation",{"type":45,"value":2822}," — SQL injection waiting to happen, bypassing every type safety guarantee",{"type":40,"tag":80,"props":2824,"children":2825},{},[2826,2831],{"type":40,"tag":51,"props":2827,"children":2828},{},[2829],{"type":45,"value":2830},"Hardcoded configuration values",{"type":45,"value":2832}," — connection strings, API keys, and feature flags embedded in application code",{"type":40,"tag":80,"props":2834,"children":2835},{},[2836,2841],{"type":40,"tag":51,"props":2837,"children":2838},{},[2839],{"type":45,"value":2840},"Missing input validation",{"type":45,"value":2842}," — trusting client-sent data without validating at the function boundary",{"type":40,"tag":80,"props":2844,"children":2845},{},[2846,2851,2853,2859],{"type":40,"tag":51,"props":2847,"children":2848},{},[2849],{"type":45,"value":2850},"Inconsistent error shapes",{"type":45,"value":2852}," — some endpoints return ",{"type":40,"tag":218,"props":2854,"children":2856},{"className":2855},[],[2857],{"type":45,"value":2858},"{ error: string }",{"type":45,"value":2860},", others throw, others return HTTP 500 with an HTML page",{"type":40,"tag":80,"props":2862,"children":2863},{},[2864,2869],{"type":40,"tag":51,"props":2865,"children":2866},{},[2867],{"type":45,"value":2868},"Middleware chains 10 layers deep",{"type":45,"value":2870}," — debugging requires stepping through a dozen wrappers before reaching business logic",{"type":40,"tag":80,"props":2872,"children":2873},{},[2874,2879],{"type":40,"tag":51,"props":2875,"children":2876},{},[2877],{"type":45,"value":2878},"Mixed freshness",{"type":45,"value":2880}," — a page showing real-time chat messages next to a user list that updates every 30 seconds. Reactivity as an afterthought creates jarring UX",{"type":40,"tag":80,"props":2882,"children":2883},{},[2884,2889,2890,2896],{"type":40,"tag":51,"props":2885,"children":2886},{},[2887],{"type":45,"value":2888},"Unbounded queries",{"type":45,"value":2746},{"type":40,"tag":218,"props":2891,"children":2893},{"className":2892},[],[2894],{"type":45,"value":2895},"SELECT * FROM messages",{"type":45,"value":2897}," without limits. Every query should have a maximum result size",{"type":40,"tag":80,"props":2899,"children":2900},{},[2901,2906],{"type":40,"tag":51,"props":2902,"children":2903},{},[2904],{"type":45,"value":2905},"Synchronous external calls in hot paths",{"type":45,"value":2907}," — calling a third-party API during a user request blocks the response. Move external calls to background jobs when possible",{"type":40,"tag":80,"props":2909,"children":2910},{},[2911,2916],{"type":40,"tag":51,"props":2912,"children":2913},{},[2914],{"type":45,"value":2915},"Missing timeouts",{"type":45,"value":2917}," — database queries or HTTP calls that can hang forever, exhausting connection pools",{"type":40,"tag":80,"props":2919,"children":2920},{},[2921,2926,2927,2933],{"type":40,"tag":51,"props":2922,"children":2923},{},[2924],{"type":45,"value":2925},"Offset-based pagination at scale",{"type":45,"value":2746},{"type":40,"tag":218,"props":2928,"children":2930},{"className":2929},[],[2931],{"type":45,"value":2932},"OFFSET 10000",{"type":45,"value":2934}," means the database still reads 10,000 rows before discarding them",{"type":40,"tag":64,"props":2936,"children":2938},{"id":2937},"implementation-guidance",[2939],{"type":45,"value":2940},"Implementation Guidance",{"type":40,"tag":41,"props":2942,"children":2943},{},[2944],{"type":45,"value":2945},"When building backend features, follow these practices:",{"type":40,"tag":76,"props":2947,"children":2948},{},[2949,2959,2969,2979,2989,2999,3009,3019,3029,3039,3049,3059,3069],{"type":40,"tag":80,"props":2950,"children":2951},{},[2952,2957],{"type":40,"tag":51,"props":2953,"children":2954},{},[2955],{"type":45,"value":2956},"Validate inputs at the function boundary",{"type":45,"value":2958}," — use schema validators (Zod, TypeBox, Joi, etc.) on every query and mutation argument. Invalid data should never reach business logic.",{"type":40,"tag":80,"props":2960,"children":2961},{},[2962,2967],{"type":40,"tag":51,"props":2963,"children":2964},{},[2965],{"type":45,"value":2966},"Keep server functions focused",{"type":45,"value":2968}," — queries should be deterministic reads with no side effects. Mutations should be focused writes. Side effects (external API calls, emails) should be handled separately (background jobs, queues, etc.).",{"type":40,"tag":80,"props":2970,"children":2971},{},[2972,2977],{"type":40,"tag":51,"props":2973,"children":2974},{},[2975],{"type":45,"value":2976},"Use platform-native scheduling",{"type":45,"value":2978}," — scheduled functions are more reliable and observable than external cron jobs.",{"type":40,"tag":80,"props":2980,"children":2981},{},[2982,2987],{"type":40,"tag":51,"props":2983,"children":2984},{},[2985],{"type":45,"value":2986},"Prefer database constraints over application checks",{"type":45,"value":2988}," — unique indexes, required fields, and foreign key relationships catch bugs that application code misses.",{"type":40,"tag":80,"props":2990,"children":2991},{},[2992,2997],{"type":40,"tag":51,"props":2993,"children":2994},{},[2995],{"type":45,"value":2996},"Design for idempotency",{"type":45,"value":2998}," — writes that might be retried (network failures, user double-clicks) should produce the same result when executed twice.",{"type":40,"tag":80,"props":3000,"children":3001},{},[3002,3007],{"type":40,"tag":51,"props":3003,"children":3004},{},[3005],{"type":45,"value":3006},"Return structured errors",{"type":45,"value":3008}," — error codes and machine-readable details, not string messages. Clients (especially AI agents) need to programmatically handle errors.",{"type":40,"tag":80,"props":3010,"children":3011},{},[3012,3017],{"type":40,"tag":51,"props":3013,"children":3014},{},[3015],{"type":45,"value":3016},"Log at function boundaries",{"type":45,"value":3018}," — log inputs and outcomes at the query\u002Fmutation level, not deep inside helper functions. This makes debugging straightforward and keeps logs meaningful.",{"type":40,"tag":80,"props":3020,"children":3021},{},[3022,3027],{"type":40,"tag":51,"props":3023,"children":3024},{},[3025],{"type":45,"value":3026},"Index every query path",{"type":45,"value":3028}," — if you query by a field, index it. Full table scans are never acceptable in production.",{"type":40,"tag":80,"props":3030,"children":3031},{},[3032,3037],{"type":40,"tag":51,"props":3033,"children":3034},{},[3035],{"type":45,"value":3036},"Separate reads from writes",{"type":45,"value":3038}," — queries read, mutations write. Don't mix concerns. This separation enables reactive systems to track dependencies correctly.",{"type":40,"tag":80,"props":3040,"children":3041},{},[3042,3047],{"type":40,"tag":51,"props":3043,"children":3044},{},[3045],{"type":45,"value":3046},"Use helper functions for shared logic",{"type":45,"value":3048}," — auth checks, permission validation, and common data loading patterns should be extracted into reusable helpers, not copy-pasted across mutations.",{"type":40,"tag":80,"props":3050,"children":3051},{},[3052,3057],{"type":40,"tag":51,"props":3053,"children":3054},{},[3055],{"type":45,"value":3056},"Think in documents, not joins",{"type":45,"value":3058}," — model data for how it's read, not how it's normalized. Denormalization is often correct when reads vastly outnumber writes.",{"type":40,"tag":80,"props":3060,"children":3061},{},[3062,3067],{"type":40,"tag":51,"props":3063,"children":3064},{},[3065],{"type":45,"value":3066},"Implement cursor-based pagination",{"type":45,"value":3068}," — offset pagination breaks at scale and produces inconsistent results when data changes. Use cursors (timestamps, IDs) for stable pagination.",{"type":40,"tag":80,"props":3070,"children":3071},{},[3072,3077],{"type":40,"tag":51,"props":3073,"children":3074},{},[3075],{"type":45,"value":3076},"Plan for multi-tenancy early",{"type":45,"value":3078}," — even single-tenant apps often become multi-tenant. Include tenant isolation in your data model from day one; retrofitting it is painful.",{"type":40,"tag":41,"props":3080,"children":3081},{},[3082,3087],{"type":40,"tag":51,"props":3083,"children":3084},{},[3085],{"type":45,"value":3086},"IMPORTANT",{"type":45,"value":3088},": Match implementation complexity to the problem. A simple CRUD feature needs a schema, a few queries, and a few mutations — not an event-sourced architecture with CQRS. Conversely, a real-time collaborative feature needs careful thought about conflict resolution and consistency. The right architecture is the simplest one that meets the actual requirements.",{"type":40,"tag":41,"props":3090,"children":3091},{},[3092],{"type":45,"value":3093},"Remember: Claude is capable of building sophisticated backend systems. Don't default to boilerplate scaffolds. Think about what the backend actually needs to do, pick the right primitives, and implement it correctly the first time.",{"type":40,"tag":3095,"props":3096,"children":3097},"style",{},[3098],{"type":45,"value":3099},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":3101,"total":3284},[3102,3115,3128,3143,3160,3177,3192,3205,3224,3238,3255,3269],{"slug":8,"name":8,"fn":3103,"description":3104,"org":3105,"tags":3106,"stars":3112,"repoUrl":3113,"updatedAt":3114},"guide Convex project setup and usage","Routes general Convex requests to the right project skill. Use when the user asks which Convex skill to use or gives an underspecified Convex app task.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3107,3108,3109],{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":3110,"slug":3111,"type":16},"Database","database",34,"https:\u002F\u002Fgithub.com\u002Fget-convex\u002Fagent-skills","2026-07-12T08:00:45.091281",{"slug":3116,"name":3116,"fn":3117,"description":3118,"org":3119,"tags":3120,"stars":3112,"repoUrl":3113,"updatedAt":3127},"convex-create-component","build reusable Convex components","Builds reusable Convex components with isolated tables and app-facing APIs. Use for new components, reusable backend modules, integrations, or component boundary work.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3121,3122,3125,3126],{"name":22,"slug":23,"type":16},{"name":3123,"slug":3124,"type":16},"Architecture","architecture",{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},"2026-07-12T08:00:39.428577",{"slug":3129,"name":3129,"fn":3130,"description":3131,"org":3132,"tags":3133,"stars":3112,"repoUrl":3113,"updatedAt":3142},"convex-migration-helper","plan Convex schema and data migrations","Plans Convex schema and data migrations with widen-migrate-narrow and @convex-dev\u002Fmigrations. Use for breaking schema changes, backfills, table reshaping, or zero-downtime rollouts.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3134,3135,3136,3139],{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":3137,"slug":3138,"type":16},"Data Engineering","data-engineering",{"name":3140,"slug":3141,"type":16},"Migration","migration","2026-07-12T08:00:51.27967",{"slug":3144,"name":3144,"fn":3145,"description":3146,"org":3147,"tags":3148,"stars":3112,"repoUrl":3113,"updatedAt":3159},"convex-performance-audit","audit Convex application performance","Audits Convex performance for reads, subscriptions, write contention, and function limits. Use for slow features, insights findings, OCC conflicts, or read amplification.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3149,3150,3153,3156],{"name":9,"slug":8,"type":16},{"name":3151,"slug":3152,"type":16},"Debugging","debugging",{"name":3154,"slug":3155,"type":16},"Monitoring","monitoring",{"name":3157,"slug":3158,"type":16},"Performance","performance","2026-07-12T08:00:50.02928",{"slug":3161,"name":3161,"fn":3162,"description":3163,"org":3164,"tags":3165,"stars":3112,"repoUrl":3113,"updatedAt":3176},"convex-quickstart","initialize Convex in applications","Creates or adds Convex to an app. Use for new Convex projects, npm create convex@latest, frontend setup, env vars, or the first npx convex dev run.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3166,3169,3170,3173],{"name":3167,"slug":3168,"type":16},"CLI","cli",{"name":9,"slug":8,"type":16},{"name":3171,"slug":3172,"type":16},"Frontend","frontend",{"name":3174,"slug":3175,"type":16},"Onboarding","onboarding","2026-07-12T08:00:43.436152",{"slug":3178,"name":3178,"fn":3179,"description":3180,"org":3181,"tags":3182,"stars":3112,"repoUrl":3113,"updatedAt":3191},"convex-setup-auth","set up authentication and access control","Sets up Convex auth, identity mapping, and access control. Use for login, auth providers, users tables, protected functions, or roles in a Convex app.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3183,3186,3189,3190],{"name":3184,"slug":3185,"type":16},"Access Control","access-control",{"name":3187,"slug":3188,"type":16},"Auth","auth",{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},"2026-07-12T08:00:48.652641",{"slug":3193,"name":3193,"fn":3194,"description":3195,"org":3196,"tags":3197,"stars":24,"repoUrl":3203,"updatedAt":3204},"add","add capabilities to Convex applications","Add a capability to the CURRENT Convex + Next.js project — consults the served Convex capability catalog for always-current procedures (billing, crons, auth, agent, search, …); falls back to built-in hosting or @convex-dev component search. TRIGGER when the user runs $add, or asks to add hosting\u002Fpublishing or any backend capability to an existing Convex app.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3198,3199,3200],{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":3201,"slug":3202,"type":16},"Next.js","next-js","https:\u002F\u002Fgithub.com\u002Fget-convex\u002Fconvex-codex-plugin","2026-07-12T07:59:59.358004",{"slug":3206,"name":3206,"fn":3207,"description":3208,"org":3209,"tags":3210,"stars":24,"repoUrl":3203,"updatedAt":3223},"agent","build AI agents with Convex","Build an AI agent \u002F RAG feature on Convex (@convex-dev\u002Fagent: threads, tools, vector search). TRIGGER on an AI-agent\u002Fchatbot\u002FRAG request.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3211,3214,3217,3220],{"name":3212,"slug":3213,"type":16},"Agents","agents",{"name":3215,"slug":3216,"type":16},"Engineering","engineering",{"name":3218,"slug":3219,"type":16},"RAG","rag",{"name":3221,"slug":3222,"type":16},"Search","search","2026-07-12T08:00:01.921824",{"slug":3188,"name":3188,"fn":3225,"description":3226,"org":3227,"tags":3228,"stars":24,"repoUrl":3203,"updatedAt":3237},"add authentication to Convex applications","Add sign-in (passkeys by default, OAuth\u002Fpassword optional) to the current Convex app, wired correctly incl. auth.config.ts. TRIGGER on a login\u002Fauth request for an existing app.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3229,3230,3233,3234],{"name":3187,"slug":3188,"type":16},{"name":3231,"slug":3232,"type":16},"Authentication","authentication",{"name":9,"slug":8,"type":16},{"name":3235,"slug":3236,"type":16},"OAuth","oauth","2026-07-18T05:12:54.443056",{"slug":3239,"name":3239,"fn":3240,"description":3241,"org":3242,"tags":3243,"stars":24,"repoUrl":3203,"updatedAt":3254},"billing","integrate Stripe billing in Convex apps","Add Stripe billing to a Convex app via @convex-dev\u002Fstripe (checkout + auto-verified webhook + subscription gating). TRIGGER on a payments\u002Fbilling\u002Fsubscription request.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3244,3245,3248,3251],{"name":9,"slug":8,"type":16},{"name":3246,"slug":3247,"type":16},"Payments","payments",{"name":3249,"slug":3250,"type":16},"Stripe","stripe",{"name":3252,"slug":3253,"type":16},"Webhooks","webhooks","2026-07-12T08:00:08.123246",{"slug":3256,"name":3256,"fn":3257,"description":3258,"org":3259,"tags":3260,"stars":24,"repoUrl":3203,"updatedAt":3268},"check-updates","upgrade Convex component versions","Check the CURRENT Convex app's pinned components against the latest recommended versions and offer to upgrade them — e.g. the passkey auth component's new email-first sign-in. TRIGGER when the user runs \u002Fcheck-updates or $check-updates, asks 'are my components up to date', 'any updates', 'upgrade auth', 'upgrade my components', or wants the newest features after a quickstart. Applies each upgrade behind a build gate (verify-or-revert) with the user's consent.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3261,3264,3265],{"name":3262,"slug":3263,"type":16},"Configuration","configuration",{"name":9,"slug":8,"type":16},{"name":3266,"slug":3267,"type":16},"Maintenance","maintenance","2026-07-12T08:00:03.236862",{"slug":3270,"name":3270,"fn":3271,"description":3272,"org":3273,"tags":3274,"stars":24,"repoUrl":3203,"updatedAt":3283},"convex-authz","audit and harden Convex authorization","Audit and harden a Convex app's authorization: identity-from-arg impersonation, missing per-document ownership checks, and public queries leaking PII\u002Ffinancial data by a client-supplied id — the single largest real-defect cluster measured against generated Convex backends (44 of 214). Runs a deterministic scan for the 3 shapes, then applies the canonical requireIdentity\u002FrequireOwner pattern, then verifies with tsc. TRIGGER on 'secure my app', 'audit auth', 'add login', 'who can access this data', or an explicit 'audit my authz'. NOT always-on. SKIP when there is no convex\u002F directory.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3275,3276,3279,3280],{"name":3187,"slug":3188,"type":16},{"name":3277,"slug":3278,"type":16},"Code Analysis","code-analysis",{"name":9,"slug":8,"type":16},{"name":3281,"slug":3282,"type":16},"Security","security","2026-07-12T08:00:04.516752",26,{"items":3286,"total":28},[3287],{"slug":4,"name":4,"fn":5,"description":6,"org":3288,"tags":3289,"stars":24,"repoUrl":25,"updatedAt":26},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3290,3291,3292,3293],{"name":22,"slug":23,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":19,"slug":20,"type":16}]