zod
zod copied to clipboard
[Feature request] Marking a field as deprecated
Use case
I am creating an API using Zod and TRPC, and I can't guarantee that all of the clients will upgrade immediately upon deploy (e.g. long opened tabs).
I would like to deprecate fields out of the TRCP input or output schema without forcing an immediate client refresh; and I didn't spot an approach to doing this in the docs.
If I have missed any easier approach, please let me know :)
Proposal
A deprecated method which removes the key from the input, or output, types based on the setting (naming just a first-thought, not a suggestion).
input:- optional in validation
- is not in the input type
- is in the output type
output:- same as before in validation (optional or required)
- is in the input type
- is not in the output type
Why not just temporarily use optional
- For
inputtypes, it will not prevent consumers from continuing to attempt to supply this data. A two step deprecation allows all consumer code to be updated (and type checked) before updating the server code. - For
outputtypes, it ensures that no consumers are still expecting this field before the server stops sending it entirely
In both situations, deprecating for a situation-appropriate amount of time helps to avoid API mismatch.
Examples
export const appRouter = t.router({
hello: publicProcedure
.input(
z.object({
// consumers of this API route will not see `name` in the input types
// but it is still available here to be responded to for old consumers
name: z.string().deprecated('input'),
}),
)
.query((opts) => {
const name = opts.input.name;
if (name) { /* do the old version */ }
else { /* do the new version */ }
return {
greeting: "Hello",
};
}),
});
export const appRouter = t.router({
// this method is being deprecated, so new consumers will see `{}` as the output type
// it will still work for older consumers, allowing time for all clients to update to the latest build
// this specific situation may be better managed at a TRPC level as well
hello: publicProcedure
.input(
z.object({
name: z.string().deprecated('input'),
}),
)
.output(
z.object({
greeting: z.string().deprecated('output'),
}),
)
.query((opts) => {
const name = opts.input.name;
return {
greeting: "Hello",
};
}),
});
How's this comming along?
I'd settle for even just a type annotation as a starting point. Would you accept a PR?