Hidden page. It waits for review: the sidebar, search and search engines leave it out, and listed pages don't link to it.
A contract from OpenAPI
Generate a typed contract module from any OpenAPI 3.0 or 3.1 document.
On this page
experimental_generateContractFromOpenApi(document, { prefix?, exportName?, patterns? }) turns an OpenAPI 3.0 or 3.1 document into the source of a @trpc12/trpc/contract module. Schemas are Effect Schemas, translated by Effect's SchemaRepresentation; each operation becomes a procedure with its route meta (under .concat(experimental_openapi())), so the contract works with experimental_openApiLink and experimental_validationLink against any REST API:
import { function experimental_generateContractFromOpenApi(document: OpenApiSource, opts?: GenerateContractOptions): stringexperimental_generateContractFromOpenApi } from '@trpc12/trpc/openapi';
const const document: anydocument = await (
await function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)fetch('https://petstore.example/openapi.json')
).Body.json(): Promise<any>json();
await function writeFile(file: PathLike | FileHandle, data: string | NodeJS.ArrayBufferView | Iterable<string | NodeJS.ArrayBufferView> | AsyncIterable<string | NodeJS.ArrayBufferView>, options?: (ObjectEncodingOptions & {
mode?: Mode | undefined;
flag?: OpenMode | undefined;
flush?: boolean | undefined;
} & EventEmitter<T extends EventMap<T> = any>.Abortable) | BufferEncoding | null): Promise<void>
writeFile(
'src/petstore.ts',
function experimental_generateContractFromOpenApi(document: OpenApiSource, opts?: GenerateContractOptions): stringexperimental_generateContractFromOpenApi(const document: anydocument, {
GenerateContractOptions.exportName?: string | undefinedexportName: 'petstore',
}),
);// src/petstore.ts (generated)
export const const Pet: Schema.Struct<{
readonly id: Schema.Number;
readonly name: Schema.String;
}>
Pet = import SchemaSchema.function Struct<{
readonly id: Schema.Number;
readonly name: Schema.String;
}>(fields: {
readonly id: Schema.Number;
readonly name: Schema.String;
}): Schema.Struct<{
readonly id: Schema.Number;
readonly name: Schema.String;
}>
Struct({
id: Schema.Numberid: import SchemaSchema.const Number: Schema.NumberNumber.BottomWithoutNew<number, number, never, never, Number, Number, number, number, readonly [], number, "readonly", "required", "no-default", "readonly", "required">.check(checks_0: Check<number>, ...checks: Check<number>[]): Schema.Numbercheck(import SchemaSchema.function isInt(annotations?: Schema.Annotations.Filter): Filter<number>isInt()),
name: Schema.Stringname: import SchemaSchema.const String: Schema.StringString,
});
const const c: ContractBuilder<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>>
c = const trpcContract: {
create<TConfig extends ContractConfig = {}>(): ContractBuilder<ContractRootDef<TConfig["meta"] extends object ? TConfig["meta"] : {}>>;
}
trpcContract.create<{}>(): ContractBuilder<ContractRootDef<{}>>create().ContractBuilder<ContractRootDef<{}>>.concat<RootDef<{}, OpenApiMeta>>(extension: ProcedureBuilder<RootDef<{}, OpenApiMeta>>): ContractBuilder<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>>
concat(function experimental_openapi(): ProcedureBuilder<RootDef<{}, OpenApiMeta>>experimental_openapi());
export const const petstore: {
pets: {
list: ContractProcedure<"query", ContractWith<ContractWith<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>, {
inputIn: {
readonly limit?: number | undefined;
};
inputOut: {
readonly limit?: number | undefined;
};
errors: SchemaValidationError<Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>>;
requires: never;
}>, {
outputIn: readonly {
readonly id: number;
readonly name: string;
}[];
outputOut: readonly {
readonly id: number;
readonly name: string;
}[];
requires: never;
}>>;
};
}
petstore = {
pets: {
list: ContractProcedure<"query", ContractWith<ContractWith<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>, {
inputIn: {
readonly limit?: number | undefined;
};
inputOut: {
readonly limit?: number | undefined;
};
errors: SchemaValidationError<Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>>;
requires: never;
}>, {
outputIn: readonly {
readonly id: number;
readonly name: string;
}[];
outputOut: readonly {
readonly id: number;
readonly name: string;
}[];
requires: never;
}>>;
}
pets: {
list: ContractProcedure<"query", ContractWith<ContractWith<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>, {
inputIn: {
readonly limit?: number | undefined;
};
inputOut: {
readonly limit?: number | undefined;
};
errors: SchemaValidationError<Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>>;
requires: never;
}>, {
outputIn: readonly {
readonly id: number;
readonly name: string;
}[];
outputOut: readonly {
readonly id: number;
readonly name: string;
}[];
requires: never;
}>>
list: const c: ContractBuilder<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>>
c
.ContractBuilder<ContractWith<ContractRootDef<{}>, { meta: { route?: Route | undefined; }; }>>.input<Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>>(schema: Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>): ContractBuilder<ContractWith<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>, {
inputIn: {
readonly limit?: number | undefined;
};
inputOut: {
readonly limit?: number | undefined;
};
errors: SchemaValidationError<Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>>;
requires: never;
}>>
input(const PetsListInput: Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>
PetsListInput)
.ContractBuilder<ContractWith<ContractWith<ContractRootDef<{}>, { meta: { route?: Route | undefined; }; }>, { inputIn: { readonly limit?: number | undefined; }; inputOut: { ...; }; errors: SchemaValidationError<...>; requires: never; }>>.output<Schema.$Array<Schema.Struct<{
readonly id: Schema.Number;
readonly name: Schema.String;
}>>>(schema: Schema.$Array<Schema.Struct<{
readonly id: Schema.Number;
readonly name: Schema.String;
}>>): ContractBuilder<ContractWith<ContractWith<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>, {
inputIn: {
readonly limit?: number | undefined;
};
inputOut: {
readonly limit?: number | undefined;
};
errors: SchemaValidationError<Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>>;
requires: never;
}>, {
...;
}>>
output(const PetsListOutput: Schema.$Array<Schema.Struct<{
readonly id: Schema.Number;
readonly name: Schema.String;
}>>
PetsListOutput)
.ContractBuilder<ContractWith<ContractWith<ContractWith<ContractRootDef<{}>, { meta: { route?: Route | undefined; }; }>, { inputIn: { readonly limit?: number | undefined; }; inputOut: { ...; }; errors: SchemaValidationError<...>; requires: never; }>, { ...; }>>.meta<{
readonly route: {
readonly method: "GET";
readonly path: "/pets";
};
}>(meta: {
readonly route: {
readonly method: "GET";
readonly path: "/pets";
};
} & UndeclaredMeta<{
route?: Route | undefined;
}, {
readonly route: {
readonly method: "GET";
readonly path: "/pets";
};
}>): ContractBuilder<ContractWith<ContractWith<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>, {
inputIn: {
readonly limit?: number | undefined;
};
inputOut: {
readonly limit?: number | undefined;
};
errors: SchemaValidationError<Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>>;
requires: never;
}>, {
...;
}>>
meta({ route: {
readonly method: "GET";
readonly path: "/pets";
}
route: { method: "GET"method: 'GET', path: "/pets"path: '/pets' } })
.ContractBuilder<ContractWith<ContractWith<ContractWith<ContractRootDef<{}>, { meta: { route?: Route | undefined; }; }>, { inputIn: { readonly limit?: number | undefined; }; inputOut: { ...; }; errors: SchemaValidationError<...>; requires: never; }>, { ...; }>>.query(): ContractProcedure<"query", ContractWith<ContractWith<ContractWith<ContractRootDef<{}>, {
meta: {
route?: Route | undefined;
};
}>, {
inputIn: {
readonly limit?: number | undefined;
};
inputOut: {
readonly limit?: number | undefined;
};
errors: SchemaValidationError<Schema.Struct<{
readonly limit: Schema.optionalKey<Schema.Number>;
}>>;
requires: never;
}>, {
outputIn: readonly {
readonly id: number;
readonly name: string;
}[];
outputOut: readonly {
readonly id: number;
readonly name: string;
}[];
requires: never;
}>>
query(),
},
};- Procedure paths come from
operationIds, with dots nesting (pets.list); without one, from the method and path (deletePetsPetId). GETis a query, atext/event-streamresponse a subscription, anything else a mutation. The first 2xx status becomessuccessStatus.- Path and query parameters merge with the JSON body into one input, like
inputStructure: 'compact'. - Objects that don't mention
additionalPropertiesbecome closed structs (extra keys are still ignored when decoding), except whereallOfmerges them. - Error responses in @trpc12/trpc's own shape (
codeanddata) become.errors(), so a document fromexperimental_generateOpenApiround-trips with typed errors. Other APIs' errors stay unexpected errors. patterns are ignored unlesspatterns: 'apply'; only use that with documents you trust.
Credits
Generating contracts from OpenAPI documents follows oRPC, which does it with a Hey API plugin.