orpc-contract-first

oRPC Contract-First Development

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "orpc-contract-first" with this command: npx skills add langgenius/dify/langgenius-dify-orpc-contract-first

oRPC Contract-First Development

Intent

  • Keep contract as single source of truth in web/contract/* .

  • Default query usage: call-site useQuery(consoleQuery|marketplaceQuery.xxx.queryOptions(...)) when endpoint behavior maps 1:1 to the contract.

  • Keep abstractions minimal and preserve TypeScript inference.

Minimal Structure

web/contract/ ├── base.ts ├── router.ts ├── marketplace.ts └── console/ ├── billing.ts └── ...other domains web/service/client.ts

Core Workflow

  • Define contract in web/contract/console/{domain}.ts or web/contract/marketplace.ts

  • Use base.route({...}).output(type<...>()) as baseline.

  • Add .input(type<...>()) only when request has params/query/body .

  • For GET without input, omit .input(...) (do not use .input(type<unknown>()) ).

  • Register contract in web/contract/router.ts

  • Import directly from domain files and nest by API prefix.

  • Consume from UI call sites via oRPC query utils.

import { useQuery } from '@tanstack/react-query' import { consoleQuery } from '@/service/client'

const invoiceQuery = useQuery(consoleQuery.billing.invoices.queryOptions({ staleTime: 5 * 60 * 1000, throwOnError: true, select: invoice => invoice.url, }))

Query Usage Decision Rule

  • Default: call site directly uses *.queryOptions(...) .

  • If 3+ call sites share the same extra options (for example retry: false ), extract a small queryOptions helper, not a use-* passthrough hook.

  • Create web/service/use-{domain}.ts only for orchestration:

  • Combine multiple queries/mutations.

  • Share domain-level derived state or invalidation helpers.

const invoicesBaseQueryOptions = () => consoleQuery.billing.invoices.queryOptions({ retry: false })

const invoiceQuery = useQuery({ ...invoicesBaseQueryOptions(), throwOnError: true, })

Mutation Usage Decision Rule

  • Default: call mutation helpers from consoleQuery / marketplaceQuery , for example useMutation(consoleQuery.billing.bindPartnerStack.mutationOptions(...)) .

  • If mutation flow is heavily custom, use oRPC clients as mutationFn (for example consoleClient.xxx / marketplaceClient.xxx ), instead of generic handwritten non-oRPC mutation logic.

Key API Guide (.key vs .queryKey vs .mutationKey )

  • .key(...) :

  • Use for partial matching operations (recommended for invalidation/refetch/cancel patterns).

  • Example: queryClient.invalidateQueries({ queryKey: consoleQuery.billing.key() })

  • .queryKey(...) :

  • Use for a specific query's full key (exact query identity / direct cache addressing).

  • .mutationKey(...) :

  • Use for a specific mutation's full key.

  • Typical use cases: mutation defaults registration, mutation-status filtering (useIsMutating , queryClient.isMutating ), or explicit devtools grouping.

Anti-Patterns

  • Do not wrap useQuery with options?: Partial<UseQueryOptions> .

  • Do not split local queryKey/queryFn when oRPC queryOptions already exists and fits the use case.

  • Do not create thin use-* passthrough hooks for a single endpoint.

  • Reason: these patterns can degrade inference (data may become unknown , especially around throwOnError /select ) and add unnecessary indirection.

Contract Rules

  • Input structure: Always use { params, query?, body? } format

  • No-input GET: Omit .input(...) ; do not use .input(type<unknown>())

  • Path params: Use {paramName} in path, match in params object

  • Router nesting: Group by API prefix (e.g., /billing/* -> billing: {} )

  • No barrel files: Import directly from specific files

  • Types: Import from @/types/ , use type<T>() helper

  • Mutations: Prefer mutationOptions ; use explicit mutationKey mainly for defaults/filtering/devtools

Type Export

export type ConsoleInputs = InferContractRouterInputs<typeof consoleRouterContract>

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

frontend-code-review

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

backend-code-review

No summary provided by upstream source.

Repository SourceNeeds Review
General

component-refactoring

No summary provided by upstream source.

Repository SourceNeeds Review
General

frontend-testing

No summary provided by upstream source.

Repository SourceNeeds Review