Aviary
Validation

Forms

forms.ts — a validation schema per validated endpoint.

For every route whose @Body()/@Query() is a validated DTO (or a defineContract schema), the codegen emits a schema into forms.ts, plus a formSchemas map keyed by route name.

src/generated/forms.ts (zod)
import { z } from 'zod';

export const LoginBodySchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});
export type LoginBody = z.infer<typeof LoginBodySchema>;

export const formSchemas = {
  'auth.login': LoginBodySchema,
} as const;

Using it with react-hook-form

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { LoginBodySchema, type LoginBody } from '../generated/forms';
import { api } from '../lib/api';
import { useMutation } from '@tanstack/react-query';

function LoginForm() {
  const form = useForm<LoginBody>({ resolver: zodResolver(LoginBodySchema) });
  const login = useMutation(api.auth.login().mutationOptions());

  return (
    <form onSubmit={form.handleSubmit((body) => login.mutate({ body }))}>
      <input {...form.register('email')} />
      <input type="password" {...form.register('password')} />
    </form>
  );
}

The same schema validates on the client and matches what the server enforces — one source of truth, derived from your DTO.

Switch the validation lib in config and forms.ts is rendered with that library instead. See Pluggable validation.

On this page