Skip to content

tRPC v12 isn’t on npm yet. The @trpc12/* packages on this site can’t be installed until it’s released. Use v11 today

tRPC

ExperimentalContracts and OpenAPI might change or go away in any release. Stability

Generating a client with Hey API

Generate a typed SDK of the REST routes from the OpenAPI document, with Hey API.

On this page

Hey API (@hey-api/openapi-ts) turns an OpenAPI document into a TypeScript SDK, with plugins for validators, TanStack Query, mocks and more. Point it at the document experimental_generateOpenApi writes, and apps that can't import the router's type get typed functions for its REST routes: another team's app, a public API's users, another repository. An app that can import the router's type is better served by the tRPC client, which needs no generation step.

The document

This router serves the blog over REST: a query with a declared NOT_FOUND error, a mutation behind a bearer token that takes a file, and a subscription. The document declares the bearer scheme that authed routes require (Security and tags):

rest.ts
/**
 * The blog of `./server.ts` over REST, documented for clients generated from
 * OpenAPI. `pnpm --filter @samples/docs generate:api` writes its document to
 * `../openapi.json`, and Hey API writes `./api/` from that.
 */
import {
  function experimental_generateOpenApi(router: object, opts: GenerateOpenApiOptions): OpenApiDocumentexperimental_generateOpenApi,
  function experimental_openapi(): ProcedureBuilder<RootDef<{}, OpenApiMeta>>experimental_openapi,
} from '@trpc12/trpc/openapi';
import { function error<const TCode extends string, TData = undefined>(opts: Omit<TrpcErrorOptions<TCode, TData>, "defined"> & ReservedCodeCheck<TCode>): TrpcError<TCode, TData>error, function initTrpc<TConfig extends RootConfig = {}>(opts?: RootOptions<TConfig["meta"] extends object ? TConfig["meta"] : {}>): TrpcRoot<TConfig["ctx"] extends object ? TConfig["ctx"] : {}, TConfig["meta"] extends object ? TConfig["meta"] : {}>initTrpc, function tracked<TData>(id: string, data: TData): TrackedEnvelope<TData>tracked } from '@trpc12/trpc/server';
import { import zz } from 'zod';

import { 
const db: {
    post: {
        find: (id: string) => Post | undefined;
        list: () => Post[];
        create: (input: {
            title: string;
            author?: string | null;
        }) => Post;
    };
}
db
,
const PostSchema: z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>
PostSchema
, type Context } from './server.ts';
const
const t: TrpcRoot<Context, {
    route?: Route | undefined;
}, ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>
t
=
initTrpc<{
    ctx: Context;
}>(opts?: RootOptions<{}>): TrpcRoot<Context, {}, RootDef<Context, {}>>
initTrpc
<{ ctx: Contextctx: Context }>().
TrpcRoot<Context, {}, RootDef<Context, {}>>.concat<RootDef<{}, OpenApiMeta>>(extension: ProcedureBuilder<RootDef<{}, OpenApiMeta>>): TrpcRoot<Context, {
    route?: Route | undefined;
}, ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>
concat
(function experimental_openapi(): ProcedureBuilder<RootDef<{}, OpenApiMeta>>experimental_openapi());
const
const authed: ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<"UNAUTHORIZED", undefined>;
    shorts: never;
}>>
authed
=
const t: TrpcRoot<Context, {
    route?: Route | undefined;
}, ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>
t
.TrpcRoot<Context, { route?: Route | undefined; }, ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>.procedure: ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>procedure
.
ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>.use<{
    user: User;
}, TrpcError<"UNAUTHORIZED", undefined>, never, Unset, {}>(fn: MiddlewareFunction<{
    user: User | null;
}, undefined, {
    route?: Route | undefined;
}, {
    user: User;
}, TrpcError<"UNAUTHORIZED", undefined>, never, Unset>): ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<"UNAUTHORIZED", undefined>;
    shorts: never;
}>> (+1 overload)
use
((
opts: MiddlewareOpts<{
    user: User | null;
}, undefined, {
    route?: Route | undefined;
}>
opts
) =>
opts: MiddlewareOpts<{
    user: User | null;
}, undefined, {
    route?: Route | undefined;
}>
opts
.
MiddlewareOpts<{ user: User | null; }, undefined, { route?: Route | undefined; }>.ctx: {
    user: User | null;
}
ctx
.user: User | nulluser
?
opts: MiddlewareOpts<{
    user: User | null;
}, undefined, {
    route?: Route | undefined;
}>
opts
.
MiddlewareOpts<{ user: User | null; }, undefined, { route?: Route | undefined; }>.next: NextFn
<{
    user: User;
}>(opts: {
    ctx: {
        user: User;
    };
}) => Promise<MiddlewareResult<{
    user: User;
}, Unset>> (+2 overloads)
next
({
ctx: {
    user: User;
}
ctx
: { user: Useruser:
opts: MiddlewareOpts<{
    user: User | null;
}, undefined, {
    route?: Route | undefined;
}>
opts
.
MiddlewareOpts<{ user: User | null; }, undefined, { route?: Route | undefined; }>.ctx: {
    user: User | null;
}
ctx
.user: Useruser } })
: error<"UNAUTHORIZED", undefined>(opts: Omit<TrpcErrorOptions<"UNAUTHORIZED", undefined>, "defined">): TrpcError<"UNAUTHORIZED", undefined>error({ code: "UNAUTHORIZED"code: 'UNAUTHORIZED' }), ) .
ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { ctx: Context; ctxAdded: { user: User; }; inputOut: Unset; errors: TrpcError<...>; shorts: never; }>>.meta<{
    readonly route: {
        readonly spec: (op: OperationSpec) => {
            security: {
                bearer: never[];
            }[];
        };
    };
}>(meta: {
    readonly route: {
        readonly spec: (op: OperationSpec) => {
            security: {
                bearer: never[];
            }[];
        };
    };
} & UndeclaredMeta<{
    route?: Route | undefined;
}, {
    readonly route: {
        readonly spec: (op: OperationSpec) => {
            security: {
                bearer: never[];
            }[];
        };
    };
}>): ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        ...;
    };
    inputOut: Unset;
    errors: TrpcError<...>;
    shorts: never;
}>>
meta
({
route: {
    readonly spec: (op: OperationSpec) => {
        security: {
            bearer: never[];
        }[];
    };
}
route
: {
spec: (op: OperationSpec) => {
    security: {
        bearer: never[];
    }[];
}
spec
: (op: OperationSpecop) => ({ ...op: OperationSpecop,
security: {
    bearer: never[];
}[]
security
: [{ bearer: never[]bearer: [] }] }) } });
export const
const restRouter: {
    post: {
        byId: Procedure<{
            type: "query";
            ctx: Context;
            meta: {
                route?: Route | undefined;
            };
            input: {
                id: string;
            };
            output: {
                id: string;
                title: string;
                author: string | null;
                createdAt: Date;
            };
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }> | TrpcError<"NOT_FOUND", {
                id: string;
            }> | SchemaValidationError<z.ZodObject<{
                id: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: Context;
            meta: {
                route?: Route | undefined;
            };
            input: {
                title: string;
                cover?: z.core.File | undefined;
            };
            output: {
                id: string;
                title: string;
                author: string | null;
                createdAt: Date;
            };
            errors: TrpcError<...> | SchemaValidationError<...>;
            services: never;
        }>;
        onAdd: Procedure<...>;
    };
}
restRouter
= {
post: {
    byId: Procedure<{
        type: "query";
        ctx: Context;
        meta: {
            route?: Route | undefined;
        };
        input: {
            id: string;
        };
        output: {
            id: string;
            title: string;
            author: string | null;
            createdAt: Date;
        };
        errors: TrpcError<"NOT_FOUND", {
            id: string;
        }> | TrpcError<"NOT_FOUND", {
            id: string;
        }> | SchemaValidationError<z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>;
        services: never;
    }>;
    create: Procedure<{
        type: "mutation";
        ctx: Context;
        meta: {
            route?: Route | undefined;
        };
        input: {
            title: string;
            cover?: z.core.File | undefined;
        };
        output: {
            id: string;
            title: string;
            author: string | null;
            createdAt: Date;
        };
        errors: TrpcError<...> | SchemaValidationError<...>;
        services: never;
    }>;
    onAdd: Procedure<...>;
}
post
: {
byId: Procedure<{
    type: "query";
    ctx: Context;
    meta: {
        route?: Route | undefined;
    };
    input: {
        id: string;
    };
    output: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | TrpcError<"NOT_FOUND", {
        id: string;
    }> | SchemaValidationError<z.ZodObject<{
        id: z.ZodString;
    }, z.core.$strip>>;
    services: never;
}>
byId
:
const t: TrpcRoot<Context, {
    route?: Route | undefined;
}, ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>
t
.TrpcRoot<Context, { route?: Route | undefined; }, ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>.procedure: ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>procedure
.
ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>.meta<{
    readonly route: {
        readonly method: "GET";
        readonly path: "/posts/{id}";
    };
}>(meta: {
    readonly route: {
        readonly method: "GET";
        readonly path: "/posts/{id}";
    };
} & UndeclaredMeta<{
    route?: Route | undefined;
}, {
    readonly route: {
        readonly method: "GET";
        readonly path: "/posts/{id}";
    };
}>): ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>
meta
({
route: {
    readonly method: "GET";
    readonly path: "/posts/{id}";
}
route
: { method: "GET"method: 'GET', path: "/posts/{id}"path: '/posts/{id}' } })
.
ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>.errors<{
    readonly NOT_FOUND: {
        readonly data: z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>;
    };
}>(map: {
    readonly NOT_FOUND: {
        readonly data: z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>;
    };
} & ReservedKeyCheck): ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
}>>
errors
({
type NOT_FOUND: {
    readonly data: z.ZodObject<{
        id: z.ZodString;
    }, z.core.$strip>;
}
NOT_FOUND
: {
data: z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>
data
: import zz.
function object<{
    id: z.ZodString;
}>(shape?: {
    id: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodStringid: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }) } })
.
ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; }; errors: TrpcError<...>; }>>.input<z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>> (+1 overload)
input
(import zz.
function object<{
    id: z.ZodString;
}>(shape?: {
    id: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodStringid: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }))
.
ProcedureBuilder<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; }; errors: TrpcError<...>; }>, ZodObject<...>>>.output<z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>, opts?: {
    validate?: true;
}): ProcedureBuilder<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
    };
    errors: TrpcError<...>;
}>, z.ZodObject<...>>, {
    ...;
}>> (+1 overload)
output
(
const PostSchema: z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>
PostSchema
)
.
ProcedureBuilder<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; }; errors: TrpcError<...>; }>, ZodObject<...>>, { ...; }>>.query<Post | TrpcError<"NOT_FOUND", {
    id: string;
}>>(resolver: (opts: ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>) => Post | ... 1 more ... | TrpcError<...>): Procedure<...>
query
(
(
opts: ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>
opts
) =>
const db: {
    post: {
        find: (id: string) => Post | undefined;
        list: () => Post[];
        create: (input: {
            title: string;
            author?: string | null;
        }) => Post;
    };
}
db
.
post: {
    find: (id: string) => Post | undefined;
    list: () => Post[];
    create: (input: {
        title: string;
        author?: string | null;
    }) => Post;
}
post
.find: (id: string) => Post | undefinedfind(
opts: ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>
opts
.
ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; }; errors: TrpcError<...>; }>, ZodObject<...>>, { ...; }>>.input: {
    id: string;
}
input
.id: stringid) ??
error<"NOT_FOUND", {
    id: string;
}>(opts: Omit<TrpcErrorOptions<"NOT_FOUND", {
    id: string;
}>, "defined">): TrpcError<"NOT_FOUND", {
    id: string;
}>
error
({ code: "NOT_FOUND"code: 'NOT_FOUND',
data?: {
    id: string;
} | undefined
data
: { id: stringid:
opts: ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>
opts
.
ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; }; errors: TrpcError<...>; }>, ZodObject<...>>, { ...; }>>.input: {
    id: string;
}
input
.id: stringid } }),
),
create: Procedure<{
    type: "mutation";
    ctx: Context;
    meta: {
        route?: Route | undefined;
    };
    input: {
        title: string;
        cover?: z.core.File | undefined;
    };
    output: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    errors: TrpcError<...> | SchemaValidationError<...>;
    services: never;
}>
create
:
const authed: ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<"UNAUTHORIZED", undefined>;
    shorts: never;
}>>
authed
.
ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { ctx: Context; ctxAdded: { user: User; }; inputOut: Unset; errors: TrpcError<...>; shorts: never; }>>.meta<{
    readonly route: {
        readonly method: "POST";
        readonly path: "/posts";
        readonly successStatus: 201;
    };
}>(meta: {
    readonly route: {
        readonly method: "POST";
        readonly path: "/posts";
        readonly successStatus: 201;
    };
} & UndeclaredMeta<{
    route?: Route | undefined;
}, {
    readonly route: {
        readonly method: "POST";
        readonly path: "/posts";
        readonly successStatus: 201;
    };
}>): ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<...>;
    shorts: never;
}>>
meta
({
route: {
    readonly method: "POST";
    readonly path: "/posts";
    readonly successStatus: 201;
}
route
: { method: "POST"method: 'POST', path: "/posts"path: '/posts', successStatus: 201successStatus: 201 } })
.
ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { ctx: Context; ctxAdded: { user: User; }; inputOut: Unset; errors: TrpcError<...>; shorts: never; }>>.input<z.ZodObject<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}, z.core.$strip>>(schema: z.ZodObject<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}, z.core.$strip>): ProcedureBuilder<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<"UNAUTHORIZED", undefined>;
    shorts: never;
}>, z.ZodObject<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}, z.core.$strip>>> (+1 overload)
input
(import zz.
function object<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}>(shape?: {
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}, z.core.$strip>
object
({ title: z.ZodStringtitle: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string()._ZodString<$ZodStringInternals<string>>.min(minLength: number, params?: string | z.core.$ZodCheckMinLengthParams): z.ZodStringmin(1), cover: z.ZodOptional<z.ZodFile>cover: import zz.function file(params?: string | z.core.$ZodFileParams): z.ZodFilefile().ZodType<any, any, $ZodFileInternals>.optional(): z.ZodOptional<z.ZodFile>optional() }))
.
ProcedureBuilder<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { ctx: Context; ctxAdded: { user: User; }; inputOut: Unset; errors: TrpcError<...>; shorts: never; }>, ZodObject<...>>>.output<z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>, opts?: {
    validate?: true;
}): ProcedureBuilder<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<...>;
    shorts: never;
}>, z.ZodObject<...>>, {
    ...;
}>> (+1 overload)
output
(
const PostSchema: z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>
PostSchema
)
.
ProcedureBuilder<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { ctx: Context; ctxAdded: { user: User; }; inputOut: Unset; errors: TrpcError<...>; shorts: never; }>, ZodObject<...>>, { ...; }>>.mutation<Post>(resolver: (opts: ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<"UNAUTHORIZED", undefined>;
    shorts: never;
}>, z.ZodObject<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}, z.core.$strip>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>) => Post | Returns<...>): Procedure<...>
mutation
((
opts: ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<"UNAUTHORIZED", undefined>;
    shorts: never;
}>, z.ZodObject<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}, z.core.$strip>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>
opts
) =>
const db: {
    post: {
        find: (id: string) => Post | undefined;
        list: () => Post[];
        create: (input: {
            title: string;
            author?: string | null;
        }) => Post;
    };
}
db
.
post: {
    find: (id: string) => Post | undefined;
    list: () => Post[];
    create: (input: {
        title: string;
        author?: string | null;
    }) => Post;
}
post
.
create: (input: {
    title: string;
    author?: string | null;
}) => Post
create
({ title: stringtitle:
opts: ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<"UNAUTHORIZED", undefined>;
    shorts: never;
}>, z.ZodObject<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}, z.core.$strip>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>
opts
.
ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { ctx: Context; ctxAdded: { user: User; }; inputOut: Unset; errors: TrpcError<...>; shorts: never; }>, ZodObject<...>>, { ...; }>>.input: {
    title: string;
    cover?: z.core.File | undefined;
}
input
.title: stringtitle, author?: string | null | undefinedauthor:
opts: ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    ctx: Context;
    ctxAdded: {
        user: User;
    };
    inputOut: Unset;
    errors: TrpcError<"UNAUTHORIZED", undefined>;
    shorts: never;
}>, z.ZodObject<{
    title: z.ZodString;
    cover: z.ZodOptional<z.ZodFile>;
}, z.core.$strip>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>
opts
.
ResolverOpts<With<InputDef<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { ctx: Context; ctxAdded: { user: User; }; inputOut: Unset; errors: TrpcError<...>; shorts: never; }>, ZodObject<...>>, { ...; }>>.ctx: {
    user: User;
}
ctx
.user: Useruser.User.name: stringname }),
),
onAdd: Procedure<{
    type: "subscription";
    ctx: Context;
    meta: {
        route?: Route | undefined;
    };
    input: undefined;
    output: TrackedEnvelope<{
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    }>;
    errors: never;
    services: never;
}>
onAdd
:
const t: TrpcRoot<Context, {
    route?: Route | undefined;
}, ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>
t
.TrpcRoot<Context, { route?: Route | undefined; }, ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>.procedure: ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>procedure
.
ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>.meta<{
    readonly route: {
        readonly path: "/posts/events";
    };
}>(meta: {
    readonly route: {
        readonly path: "/posts/events";
    };
} & UndeclaredMeta<{
    route?: Route | undefined;
}, {
    readonly route: {
        readonly path: "/posts/events";
    };
}>): ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>
meta
({
route: {
    readonly path: "/posts/events";
}
route
: { path: "/posts/events"path: '/posts/events' } })
.
ProcedureBuilder<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>>.output<z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>, opts?: {
    validate?: true;
}): ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        ...;
    };
    requires: never;
}>> (+1 overload)
output
(
const PostSchema: z.ZodObject<{
    id: z.ZodString;
    title: z.ZodString;
    author: z.ZodNullable<z.ZodString>;
    createdAt: z.ZodDate;
}, z.core.$strip>
PostSchema
)
.
ProcedureBuilder<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, { outputIn: { id: string; title: string; author: string | null; createdAt: Date; }; outputOut: { ...; }; requires: never; }>>.subscription<AsyncGenerator<TrackedEnvelope<Post>, void, unknown>>(resolver: (opts: SubscriptionResolverOpts<With<ExtendedDef<RootDef<Context, {}>, RootDef<{}, OpenApiMeta>>, {
    outputIn: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    outputOut: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    requires: never;
}>>) => Yields<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
} | TrackedEnvelope<...>, AnyTrpcError> | AsyncGenerator<...>): Procedure<...>
subscription
(async function* () {
for (const const post: Postpost of
const db: {
    post: {
        find: (id: string) => Post | undefined;
        list: () => Post[];
        create: (input: {
            title: string;
            author?: string | null;
        }) => Post;
    };
}
db
.
post: {
    find: (id: string) => Post | undefined;
    list: () => Post[];
    create: (input: {
        title: string;
        author?: string | null;
    }) => Post;
}
post
.list: () => Post[]list()) yield tracked<Post>(id: string, data: Post): TrackedEnvelope<Post>tracked(const post: Postpost.Post.id: stringid, const post: Postpost);
}), }, }; export const const document: OpenApiDocumentdocument = function experimental_generateOpenApi(router: object, opts: GenerateOpenApiOptions): OpenApiDocumentexperimental_generateOpenApi(
const restRouter: {
    post: {
        byId: Procedure<{
            type: "query";
            ctx: Context;
            meta: {
                route?: Route | undefined;
            };
            input: {
                id: string;
            };
            output: {
                id: string;
                title: string;
                author: string | null;
                createdAt: Date;
            };
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }> | TrpcError<"NOT_FOUND", {
                id: string;
            }> | SchemaValidationError<z.ZodObject<{
                id: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: Context;
            meta: {
                route?: Route | undefined;
            };
            input: {
                title: string;
                cover?: z.core.File | undefined;
            };
            output: {
                id: string;
                title: string;
                author: string | null;
                createdAt: Date;
            };
            errors: TrpcError<...> | SchemaValidationError<...>;
            services: never;
        }>;
        onAdd: Procedure<...>;
    };
}
restRouter
, {
GenerateOpenApiOptions.info: OpenApiInfoinfo: { OpenApiInfo.title: stringtitle: 'Blog', OpenApiInfo.version: stringversion: '1.0.0' }, GenerateOpenApiOptions.servers?: readonly OpenApiServer[] | undefinedservers: [{ OpenApiServer.url: stringurl: 'http://localhost:3000' }],
GenerateOpenApiOptions.components?: {
    readonly schemas?: Readonly<Record<string, JsonSchema>>;
    readonly securitySchemes?: Readonly<Record<string, OpenApiSecurityScheme>>;
} | undefined
components
: {
securitySchemes?: Readonly<Record<string, OpenApiSecurityScheme>> | undefinedsecuritySchemes: {
bearer: {
    type: "http";
    scheme: string;
}
bearer
: { type: "http"type: 'http', scheme: stringscheme: 'bearer' } },
}, });

A script writes it where Hey API reads it:

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
('openapi.json', var JSON: JSONJSON.JSON.stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string (+1 overload)stringify(const document: OpenApiDocumentdocument, null, 2));

Generating the SDK

// @filename: src/openapi-ts.config.ts
import { function defineConfig(config: LazyOrAsync<ReadonlyArray<UserConfig>>): Promise<ReadonlyArray<UserConfig>> (+1 overload)defineConfig } from '@hey-api/openapi-ts';

export default function defineConfig(config: LazyOrAsync<UserConfig>): Promise<UserConfig> (+1 overload)defineConfig({
  input: MaybeArray<UserInput | ApiRegistryShorthands | AnyString | JsonSchema>input: './openapi.json',
  output: MaybeArray<string | UserOutput>output: 'src/api',
  
plugins?: readonly (keyof PluginConfigMap | (Plugin.Name<"@hey-api/client-fetch"> & Plugin.Hooks & UserIndexExportOption & {
    baseUrl?: string | number | boolean;
    bundle?: boolean;
    runtimeConfigPath?: string;
    strictBaseUrl?: boolean;
} & {
    throwOnError?: boolean;
} & {
    name: "@hey-api/client-fetch";
}) | (Plugin.Name<"@hey-api/typescript"> & Plugin.Hooks & UserCommentsOption & UserIndexExportOption & HeyApiTypeScriptResolvers & {
    ...;
} & {
    ...;
}) | ... 26 more ... | (Plugin.Name<...> & ... 5 more ... & {
    ...;
}))[] | undefined
plugins
: [
'@hey-api/client-fetch', '@hey-api/typescript', { name: "@hey-api/sdk"name: '@hey-api/sdk',
transformer?: boolean | PluginTransformerNames | {
    response?: PluginTransformerNames | boolean;
} | undefined
transformer
: true },
{ name: "@hey-api/transformers"name: '@hey-api/transformers', dates?: boolean | "date" | "temporal" | undefineddates: true }, ], });
npx @hey-api/openapi-ts

Hey API 0.99 needs TypeScript 5 or 6: it uses TypeScript's JavaScript API, which TypeScript 7 doesn't have. A project on TypeScript 7, which experimental_generateOpenApiFromTypes needs, can run it with TypeScript 6 alongside: pnpm dlx --package=@hey-api/openapi-ts --package=typescript@6 openapi-ts.

What the document gives Hey API:

  • Each operationId is the procedure's path, so post.byId is postById(). route.operationId renames one.
  • Each declared error is a response whose code is a constant. The default response leaves out the codes the operation documents, so error.defined && error.code === 'NOT_FOUND' narrows to the declared error and its data (The document).
  • Operations with security send the credentials from the client's auth option.
  • A body with a file is multipart/form-data, so the SDK sends a FormData (Inputs).
  • Dates are date-time strings, which transformers with dates: true turns back into Dates.

Calling the API

import { const client: Clientclient } from './api/client.gen';
import { const postById: <ThrowOnError extends boolean = false>(options: Options<PostByIdData, ThrowOnError>) => RequestResult<PostByIdResponses, PostByIdErrors, ThrowOnError>postById, const postCreate: <ThrowOnError extends boolean = false>(options: Options<PostCreateData, ThrowOnError>) => RequestResult<PostCreateResponses, PostCreateErrors, ThrowOnError>postCreate } from './api';

const client: Clientclient.setConfig: (config: Config<ClientOptions>) => Config<ClientOptions>setConfig({
  Config<ClientOptions>.baseUrl?: string | undefinedbaseUrl: 'https://api.example.com',
  Config.auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthTokenauth: () => var localStorage: StoragelocalStorage.Storage.getItem(key: string): string | nullgetItem('token') ?? var undefinedundefined,
});

const 
const result: ({
    data: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    error: undefined;
} | {
    data: undefined;
    error: {
        defined: true;
        code: "BAD_REQUEST";
        message: string;
        data: {
            issues: Array<{
                message: string;
                path?: Array<string | number>;
            }>;
        };
    } | {
        defined: true;
        code: "NOT_FOUND";
        message: string;
        data: {
            id: string;
        };
    } | TrpcUntypedError | {
        defined: true;
        code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
        message: string;
        data?: unknown;
    };
}) & {
    ...;
}
result
= await
postById<false>(options: Options<PostByIdData, false, unknown>): Promise<({
    data: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    error: undefined;
} | {
    data: undefined;
    error: {
        defined: true;
        code: "BAD_REQUEST";
        message: string;
        data: {
            issues: Array<{
                message: string;
                path?: Array<string | number>;
            }>;
        };
    } | {
        defined: true;
        code: "NOT_FOUND";
        message: string;
        data: {
            id: string;
        };
    } | TrpcUntypedError | {
        ...;
    };
}) & {
    ...;
}>
postById
({
path: {
    id: string;
}
path
: { id: stringid: '1' } });
if (
const result: ({
    data: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    error: undefined;
} | {
    data: undefined;
    error: {
        defined: true;
        code: "BAD_REQUEST";
        message: string;
        data: {
            issues: Array<{
                message: string;
                path?: Array<string | number>;
            }>;
        };
    } | {
        defined: true;
        code: "NOT_FOUND";
        message: string;
        data: {
            id: string;
        };
    } | TrpcUntypedError | {
        defined: true;
        code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
        message: string;
        data?: unknown;
    };
}) & {
    ...;
}
result
.
error: {
    defined: true;
    code: "BAD_REQUEST";
    message: string;
    data: {
        issues: Array<{
            message: string;
            path?: Array<string | number>;
        }>;
    };
} | {
    defined: true;
    code: "NOT_FOUND";
    message: string;
    data: {
        id: string;
    };
} | TrpcUntypedError | {
    defined: true;
    code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
    message: string;
    data?: unknown;
} | undefined
error
) {
const
const error: {
    defined: true;
    code: "BAD_REQUEST";
    message: string;
    data: {
        issues: Array<{
            message: string;
            path?: Array<string | number>;
        }>;
    };
} | {
    defined: true;
    code: "NOT_FOUND";
    message: string;
    data: {
        id: string;
    };
} | TrpcUntypedError | {
    defined: true;
    code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
    message: string;
    data?: unknown;
}
error
=
const result: {
    data: undefined;
    error: {
        defined: true;
        code: "BAD_REQUEST";
        message: string;
        data: {
            issues: Array<{
                message: string;
                path?: Array<string | number>;
            }>;
        };
    } | {
        defined: true;
        code: "NOT_FOUND";
        message: string;
        data: {
            id: string;
        };
    } | TrpcUntypedError | {
        defined: true;
        code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
        message: string;
        data?: unknown;
    };
} & {
    ...;
}
result
.
error: {
    defined: true;
    code: "BAD_REQUEST";
    message: string;
    data: {
        issues: Array<{
            message: string;
            path?: Array<string | number>;
        }>;
    };
} | {
    defined: true;
    code: "NOT_FOUND";
    message: string;
    data: {
        id: string;
    };
} | TrpcUntypedError | {
    defined: true;
    code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
    message: string;
    data?: unknown;
}
error
;
if (
const error: {
    defined: true;
    code: "BAD_REQUEST";
    message: string;
    data: {
        issues: Array<{
            message: string;
            path?: Array<string | number>;
        }>;
    };
} | {
    defined: true;
    code: "NOT_FOUND";
    message: string;
    data: {
        id: string;
    };
} | TrpcUntypedError | {
    defined: true;
    code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
    message: string;
    data?: unknown;
}
error
.defined: booleandefined &&
const error: {
    defined: true;
    code: "BAD_REQUEST";
    message: string;
    data: {
        issues: Array<{
            message: string;
            path?: Array<string | number>;
        }>;
    };
} | {
    defined: true;
    code: "NOT_FOUND";
    message: string;
    data: {
        id: string;
    };
} | {
    defined: true;
    code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
    message: string;
    data?: unknown;
}
error
.code: "BAD_REQUEST" | "NOT_FOUND" | "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT"code === 'NOT_FOUND') {
var console: Consoleconsole.Console.log(...data: any[]): voidlog(`No post ${
const error: {
    defined: true;
    code: "NOT_FOUND";
    message: string;
    data: {
        id: string;
    };
}
error
.
data: {
    id: string;
}
data
.id: stringid}`);
} else { var console: Consoleconsole.Console.log(...data: any[]): voidlog(
const error: {
    defined: true;
    code: "BAD_REQUEST";
    message: string;
    data: {
        issues: Array<{
            message: string;
            path?: Array<string | number>;
        }>;
    };
} | TrpcUntypedError | {
    defined: true;
    code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
    message: string;
    data?: unknown;
}
error
.message: stringmessage);
} } else { var console: Consoleconsole.Console.log(...data: any[]): voidlog(
const result: {
    data: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    error: undefined;
} & {
    ...;
}
result
.
data: {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}
data
.title: stringtitle,
const result: {
    data: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    error: undefined;
} & {
    ...;
}
result
.
data: {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}
data
.createdAt: DatecreatedAt.Date.getFullYear(): numbergetFullYear());
} const const cover: Filecover = new var File: new (fileBits: BlobPart[], fileName: string, options?: FilePropertyBag) => FileFile(['…'], 'cover.png', { BlobPropertyBag.type?: string | undefinedtype: 'image/png' }); await
postCreate<false>(options: Options<PostCreateData, false, unknown>): Promise<({
    data: {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    };
    error: undefined;
} | {
    data: undefined;
    error: TrpcUntypedError | {
        defined: true;
        code: "BAD_REQUEST";
        message: string;
        data: {
            issues: Array<{
                message: string;
                path?: Array<string | number>;
            }>;
        };
    } | {
        defined: true;
        code: "PARSE_ERROR" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
        message: string;
        data?: unknown;
    };
}) & {
    ...;
}>
postCreate
({
body: {
    title: string;
    cover?: Blob | File;
}
body
: { title: stringtitle: 'Hello', cover?: Blob | File | undefinedcover } });

Subscriptions

A subscription is a stream of server-sent events. Its function returns { stream }, and Hey API reconnects after a dropped connection with Last-Event-ID, so tracked() events resume. Hey API yields the data of every event, though: tRPC ends a stream with a done event, whose data is empty, and sends a failure as an error event with the error as data. onSseEvent runs just before each item is yielded, so it tells them apart:

import { const postOnAdd: <ThrowOnError extends boolean = false>(options?: Options<PostOnAddData, ThrowOnError, PostOnAddResponse>) => Promise<ServerSentEventsResult<PostOnAddResponses>>postOnAdd } from './api';

let let event: string | undefinedevent: string | undefined;
const const events: ServerSentEventsResult<PostOnAddResponses>events = await 
postOnAdd<false>(options?: Options<PostOnAddData, false, {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}> | undefined): Promise<ServerSentEventsResult<PostOnAddResponses>>
postOnAdd
({
onSseEvent?: ((event: StreamEvent<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}>) => void) | undefined
onSseEvent
: (
e: StreamEvent<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}>
e
) => {
let event: string | undefinedevent =
e: StreamEvent<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}>
e
.StreamEvent<TData = unknown>.event?: string | undefinedevent;
}, }); for await (const
const post: {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}
post
of const events: ServerSentEventsResult<PostOnAddResponses>events.
stream: AsyncGenerator<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}, void, unknown>
stream
) {
if (let event: string | undefinedevent === 'done') break; if (let event: string | undefinedevent === 'error') throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
('Subscription failed', { ErrorOptions.cause?: unknowncause:
const post: {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}
post
});
var console: Consoleconsole.Console.log(...data: any[]): voidlog(
const post: {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}
post
.title: stringtitle);
}