oRPC
background

Contract-First

Guide to implementing a contract-first approach with oRPC

Introduction

The contract-first approach is a powerful methodology for API development. It offers several key advantages:

  • Enables parallel development of client and server components
  • Facilitates clear API definition before implementation
  • Simplifies publishing TypeScript clients to npm registry
  • Reduces type conflicts between client and server

Installation

npm i @orpc/contract @orpc/server @orpc/client

Define Your Contract

Start by defining your API contract:

import { , type , type  } from '@orpc/contract'
import {  } from '@orpc/zod'
import {  } from 'zod'
 
export const  = .({
    : 
    .(
      .({
        : .(),
      }),
    )
    .(
      .({
        : .(),
      }),
    ),
 
  : .('/posts').({
    : 
      .({
        : '/{id}',
        : 'GET',
      })
      .(
        .({
          : .(),
        }),
      )
      .(
        .({
          : .(),
          : .(),
          : .(),
        }),
      ),
 
    : 
      .(
        .({
          : .(),
          : .(),
          : .().('image/*'),
        }),
      )
      .(
        .({
          : .(),
          : .(),
          : .(),
        }),
      ),
  }),
})
 
export type  = <typeof >
export type  = <typeof >

Implement Your Server

With your contract defined, implement the server logic:

import { ,  } from '@orpc/server'
import {  } from 'examples/contract'
 
export const  /** public access */ = .() // Ensure every implement must be match contract
export const  /** require authed */ = 
  .((, , ) => /** put auth logic here */ .({}))
  .()
 
export const  = .({
  : ..((, , ) => {
    return {
      : `Hello, ${.}!`,
    }
  }),
 
  : {
    : ..
      .((, , ) => {
        return {
          : 'example',
          : 'example',
          : 'example',
        }
      }),
 
    : ...((, , ) => {
      return {
        : 'example',
        : .,
        : .,
      }
    }),
  },
})

That's it! The contract definition and implementation are now completely separated, allowing for clean, maintainable code.

Client Usage

Create a fully typed client using just the contract definition:

import { ,  } from '@orpc/client'
import type {  } from 'examples/contract'
 
const  = <typeof  /* or server router */>({
  : 'http://localhost:3000/prefix',
  // fetch: optional override for the default fetch function
  // headers: provide additional headers
})
 
//  File upload out of the box
const  = await ..({
  : 'My Amazing Title',
  : 'This is a detailed description of my content',
  : (.('thumb') as HTMLInputElement).[0]!,
})
 
..
  • create
  • find
// typesafe and completion out of box

The client is type-safe and can be used in any JavaScript environment.

Benefits of Contract-First Development

  1. Type Safety: Ensures consistency between client and server
  2. Clear API Documentation: The contract serves as living documentation
  3. Parallel Development: Teams can work independently on client and server
  4. Easy Testing: Mock implementations can be created from the contract
  5. Version Control: API changes are tracked through contract changes

By following this approach, you create a robust foundation for your API development process.

On this page