Lifecycle hooks
onStart, onSuccess, onError and onFinish, for logging and metrics.
@trpc12/trpc/middleware has lifecycle helpers, each an ordinary middleware:
import { function onError(fn: (error: AnyTrpcError, opts: AnyOpts) => MaybePromise<void>): MiddlewareFunction<unknown, unknown, unknown, {}, never>onError, function onFinish(fn: (result: {
readonly ok: true;
readonly data: unknown;
} | {
readonly ok: false;
readonly error: AnyTrpcError;
}, opts: AnyOpts) => MaybePromise<void>): MiddlewareFunction<unknown, unknown, unknown, {}, never>
onFinish } from '@trpc12/trpc/middleware';
const const observed: ProcedureBuilder<With<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {};
inputOut: Unset;
errors: never;
shorts: never;
}>, {
ctx: Context;
ctxAdded: {};
inputOut: Unset;
errors: never;
shorts: never;
}>>
observed = const t: TrpcRoot<Context, {}, RootDef<Context, {}>>t.TrpcRoot<Context, {}, RootDef<Context, {}>>.procedure: ProcedureBuilder<RootDef<Context, {}>>procedure
.ProcedureBuilder<RootDef<Context, {}>>.use<{}, never, never, Unset, object>(fn: MiddlewareFunction<{
user: User | null;
} & object, undefined, {}, {}, never, never, Unset>): ProcedureBuilder<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {};
inputOut: Unset;
errors: never;
shorts: never;
}>> (+1 overload)
use(function onFinish(fn: (result: {
readonly ok: true;
readonly data: unknown;
} | {
readonly ok: false;
readonly error: AnyTrpcError;
}, opts: AnyOpts) => MaybePromise<void>): MiddlewareFunction<unknown, unknown, unknown, {}, never>
onFinish((result: {
readonly ok: true;
readonly data: unknown;
} | {
readonly ok: false;
readonly error: AnyTrpcError;
}
result, opts: AnyOptsopts) => const metrics: {
count(path: string, ok: boolean): void;
}
metrics.function count(path: string, ok: boolean): voidcount(opts: AnyOptsopts.MiddlewareOpts<unknown, unknown, unknown>.path: stringpath, result: {
readonly ok: true;
readonly data: unknown;
} | {
readonly ok: false;
readonly error: AnyTrpcError;
}
result.ok: booleanok)))
.ProcedureBuilder<With<RootDef<Context, {}>, { ctx: Context; ctxAdded: {}; inputOut: Unset; errors: never; shorts: never; }>>.use<{}, never, never, Unset, object>(fn: MiddlewareFunction<{
user: User | null;
} & object, undefined, {}, {}, never, never, Unset>): ProcedureBuilder<With<With<RootDef<Context, {}>, {
ctx: Context;
ctxAdded: {};
inputOut: Unset;
errors: never;
shorts: never;
}>, {
ctx: Context;
ctxAdded: {};
inputOut: Unset;
errors: never;
shorts: never;
}>> (+1 overload)
use(function onError(fn: (error: AnyTrpcError, opts: AnyOpts) => MaybePromise<void>): MiddlewareFunction<unknown, unknown, unknown, {}, never>onError((err: AnyTrpcErrorerr, opts: AnyOptsopts) => const log: {
warn(path: string, code: string): void;
}
log.function warn(path: string, code: string): voidwarn(opts: AnyOptsopts.MiddlewareOpts<unknown, unknown, unknown>.path: stringpath, err: AnyTrpcErrorerr.TrpcError<string, any>.code: stringcode)));onStart runs before the rest of the chain, onSuccess with the result when the rest succeeds (for a subscription, once the stream is set up), onError with the error when it fails, and onFinish with the outcome either way. Put them on a base procedure to cover every procedure built from it (Middleware).
For spans rather than logs, put a tracer in the handler's layer: every call already runs in a trpc.<path> span (Tracing).