Runtime Type Mismatch during Router Schema Rollback
0 reputation · 16 Sept 2025, 23:27 UTC
0 reputation · 16 Sept 2025, 23:27 UTC
In tRPC v11, server-to-client type inference occurs at build time. When a router's input or output schema is modified and deployed, clients compiled against the newer inferred types expect a specific data structure. If the server is subsequently rolled back to a previous schema version, a runtime drift occurs between the deployed client and the reverted server.
Because tRPC does not implement native client-side version negotiation or runtime schema validation for the response body, the client may attempt to process a response that no longer aligns with its compiled TypeScript definitions.
errorFormatter handles serialization but does not resolve structural mismatches in successful responses.What is the recommended strategy for ensuring backward compatibility during a schema rollback to prevent client-side crashes? How can a server detect that a client is using an outdated inferred type before executing a procedure?
34775 reputation · 17 Sept 2025, 11:24 UTC
To prevent client-side crashes during a tRPC schema rollback, you must adopt additive-only schema evolution and implement runtime-aware versioning. Since tRPC relies on TypeScript's build-time type inference, it cannot natively detect when a client is expecting a structure that the server no longer provides.
The most effective way to handle rollbacks is to ensure the server always remains compatible with the N-1 client version. Follow these steps:
.optional() for new fields. This ensures that if a server rollback removes a field, the newer client-side logic handles undefined gracefully rather than crashing on a missing property.Since tRPC does not provide native version negotiation, you must implement it manually using middleware. This allows the server to detect the client's schema version before executing procedure logic.
You can pass a version identifier or a schema hash from the client via headers. The server then uses a middleware to intercept the request:
const versionMiddleware = trpc.middleware(({ next, ctx }) => {
const clientVersion = ctx.req.headers.('x-client-schema-version');
const minSupportedVersion = '1.2.0';
if (clientVersion && clientVersion < minSupportedVersion) {
throw new TRPCPCError({
code: 'BAD_REQUEST',
n message: 'Your client version is outdated. Please refresh the application.',
});
}
return next();
});
This approach assumes you have a CI/CD pipeline capable of injecting a version string into the client build metadata. Note that this does not fix the type mismatch; it simply allows the server to fail gracefully with a controlled error message rather than allowing the client to crash when attempting to parse malformed response data.
Use comments to ask for clarification. Post a solution as an answer.
5,930 reputation · 17 Sept 2025, 01:33 UTC
When a router's output schema changes, attaching a runtime validator (e.g., Zod) via the output option causes tRPC to check the server's response before it reaches the client. If the rolled‑back server returns data that no longer matches the newer client‑side TypeScript types, the validator fails and tRPC throws a TRPCError with code BAD_REQUEST. This prevents the client from attempting to parse mismatched JSON and turning a silent type drift into a clear, handleable error.