T
TanStack2w ago
correct-apricot

`createServerFn` Zod validation works, but TS doesn’t catch invalid params at callsite?

I'm using createServerFn from @tanstack/react-start with Zod validation to define sendEmailFn. Everything works at runtime (thanks to .validator(...)), but TypeScript doesn't enforce the parameter shape when calling sendEmailFn()—I can omit fields or add invalid ones, and TS doesn’t complain. Only Zod throws at runtime. Why isn't TypeScript checking the input types at compile-time? Example code
import { createServerFn } from '@tanstack/react-start';
import { Resend } from 'resend';
import { z } from 'zod';

const resend = new Resend(process.env.RESEND_API_KEY);

const SendEmailFnParams = z.object({
to: z.email(),
subject: z.string(),
});

export const sendEmailFn = createServerFn({ method: 'POST' })
.validator((sendEmailParams) => SendEmailFnParams.parse(sendEmailParams))
.handler(async ({ data }) => {
const response = await resend.emails.send({
from: 'Test <test@test.com>',
to: data.to,
replyTo: process.env.EMAIL_ADDRESS_SUPPORT,
subject: data.subject,
text: 'This is a test email',
});

return response;
});
import { createServerFn } from '@tanstack/react-start';
import { Resend } from 'resend';
import { z } from 'zod';

const resend = new Resend(process.env.RESEND_API_KEY);

const SendEmailFnParams = z.object({
to: z.email(),
subject: z.string(),
});

export const sendEmailFn = createServerFn({ method: 'POST' })
.validator((sendEmailParams) => SendEmailFnParams.parse(sendEmailParams))
.handler(async ({ data }) => {
const response = await resend.emails.send({
from: 'Test <test@test.com>',
to: data.to,
replyTo: process.env.EMAIL_ADDRESS_SUPPORT,
subject: data.subject,
text: 'This is a test email',
});

return response;
});
I can call it with whatever params I want, what am I missing 🤔
await sendEmailFn({
data: {
to: guestEmail,
subject: 'Test email subject', // I can even remove subject field
no-tsc-error: 123
},
});
await sendEmailFn({
data: {
to: guestEmail,
subject: 'Test email subject', // I can even remove subject field
no-tsc-error: 123
},
});
5 Replies
dependent-tan
dependent-tan2w ago
You should just pass the schema i believe. .validator(OrganizationSwitchSchema)
flat-fuchsia
flat-fuchsia2w ago
use validator(SendEmailFnParams)
correct-apricot
correct-apricotOP2w ago
sorry not following where should I use this?
flat-fuchsia
flat-fuchsia2w ago
export const sendEmailFn = createServerFn({ method: 'POST' })
.validator(SendEmailFnParams)
.handler(async ({ data }) => {
const response = await resend.emails.send({
from: 'Test <test@test.com>',
to: data.to,
replyTo: process.env.EMAIL_ADDRESS_SUPPORT,
subject: data.subject,
text: 'This is a test email',
});

return response;
});
export const sendEmailFn = createServerFn({ method: 'POST' })
.validator(SendEmailFnParams)
.handler(async ({ data }) => {
const response = await resend.emails.send({
from: 'Test <test@test.com>',
to: data.to,
replyTo: process.env.EMAIL_ADDRESS_SUPPORT,
subject: data.subject,
text: 'This is a test email',
});

return response;
});
the way you used the schema was loosing the type information
correct-apricot
correct-apricotOP2w ago
oh I see, thanks a lot!

Did you find this page helpful?