Contract Implementation
Implementing a contract means adding business logic to each procedure defined in that contract. It ensures every implementation stays consistent by verifying that each handler matches the procedure’s expected shape.
Implementer
The implement function turns a contract into an implementer. Use it to build procedures, routers, and create middleware with full type safety.
import { function implement<TContract extends RouterContract, TInitialContext extends Context = DefaultInitialContext>(contract: TContract, config?: ProcedureConfig): Implementer<TContract, TInitialContext & object>Turns a contract into an implementer, used to implement the contract's
procedures, routers, and middleware with full type safety.implement } from '@orpc/server'
const const implementer: Implementer<{
planet: {
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
};
}, {
...;
} & object>
implementer = implement<{
planet: {
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
};
}, DefaultInitialContext>(contract: {
planet: {
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
};
}, config?: ProcedureConfig): Implementer<...>
Turns a contract into an implementer, used to implement the contract's
procedures, routers, and middleware with full type safety.implement(const contract: {
planet: {
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
};
}
contract)
.$context<{
something?: string;
}>(): Implementer<{
planet: {
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<...>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
};
}, {
something?: string;
} & object>
$context<{ something?: string | undefinedsomething?: string }>() // <- define initial context
const implementer: Implementer<{
planet: {
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
};
}, {
...;
} & object>
implementer.planet: Public<SharedRouterImplementer<{
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
}, {
...;
} & object>> & {
...;
}
planet.- create
- find
- lazy
- list
- middleware
- router
- use
list: ProcedureImplementer<{
...;
} & object, object, ZodObject<...>, ZodArray<...>, object>
list
//
//
//
//
Initial Context
Use .$context to declare the initial context required for a procedure to execute.
Learn more in the Context Documentation.
Implementing Procedures
Define a .handler for a procedure contract to provide its business logic.
const const listPlanet: ImplementedProcedure<DefaultInitialContext & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>
listPlanet = const implementer: Implementer<{
planet: {
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
};
}, DefaultInitialContext & object>
implementer.planet: Public<SharedRouterImplementer<{
list: ProcedureContractBuilderWithInputOutput<ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ProcedureContractBuilderWithInputOutput<ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>, object>;
create: ProcedureContractBuilderWithInputOutput<...>;
}, DefaultInitialContext & object>> & {
...;
}
planet.list: ProcedureImplementer<DefaultInitialContext & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>
list
.ProcedureImplementer<DefaultInitialContext & object, object, ZodObject<{ limit: ZodOptional<ZodNumber>; cursor: ZodDefault<ZodNumber>; }, $strip>, ZodArray<...>, object>['use']<object, DefaultInitialContext & object, object>(middleware: Middleware<DefaultInitialContext & object, object, {
cursor: number;
limit?: number | undefined;
}, {
id: number;
name: string;
description?: string | undefined;
}[], object>): ProcedureImplementer<DefaultInitialContext & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<...>;
}, $strip>>, object>
use(const requireAuth: DecoratedMiddleware<DefaultInitialContext & object, object, unknown, any, object>requireAuth) // <- Apply authentication middleware
.ProcedureImplementer<DefaultInitialContext & object, object, ZodObject<{ limit: ZodOptional<ZodNumber>; cursor: ZodDefault<ZodNumber>; }, $strip>, ZodArray<...>, object>['handler'](handler: ProcedureHandler<DefaultInitialContext & object, {
cursor: number;
limit?: number | undefined;
}, {
id: number;
name: string;
description?: string | undefined;
}[] | AnyORPCError, object>): ImplementedProcedure<DefaultInitialContext & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<...>;
}, $strip>>, object>
handler(({ input: {
cursor: number;
limit?: number | undefined;
}
input }) => {
// Your logic for listing planets
return []
})
Implementing Routers
Create the root router with .router to assemble your API. This enables full type-checking and runtime contract enforcement.
const router = implementer.router({
planet: {
list: listPlanet,
find: findPlanet,
create: createPlanet,
},
})
Extending Router
Like a normal router, an implementer router can also be extended with shared behavior. For example, you can apply authentication middleware to every procedure:
const router = implementer.use(requireAuth).router({
planet: {
list: listPlanet,
find: findPlanet,
create: createPlanet,
},
})
Creating Middleware
The implementer can also create middleware. Middleware created this way can infer the contract’s typesafe errors. If not all contracts define the same errors, use the in operator to check that an error exists before using it.
const ratelimit = implementer.middleware(async ({ next, errors }) => {
if ('TOO_MANY_REQUESTS' in errors) {
// Apply rate limiting only when TOO_MANY_REQUESTS is defined by the contract.
if (isRatelimitReached) {
throw errors.TOO_MANY_REQUESTS()
}
}
return next()
})
Reusability
Each implementer call creates a new instance, which avoids reference issues and makes contracts easy to reuse and extend.
const pub = implementer // Base setup for procedures that publish
const authed = implementer.use(requireAuth) // Extends 'pub' with authentication
const listPlanets = pub.planet.list.handler(({ input }) => {
// Your logic for listing planets without authentication
return []
})
const createPlanet = authed.planet.create.handler(({ input }) => {
// Your logic for creating planets with authentication
return { }
})
This pattern helps prevent duplication while maintaining flexibility.