Skip to content
oRPC
Esc
navigateopen⌘Jpreview
On this page

Request Validation Plugin

Request Validation Plugin validates requests against your contract before they are sent to the server. This is useful when your application relies on server-side validation.

Setup

import { RequestValidationLinkPlugin } from '@orpc/contract/plugins'

const link = new RPCLink({
  plugins: [
    new RequestValidationLinkPlugin(contract),
  ],
})

Forward Validated Input

By default, the plugin does not reuse validated input for the rest of the request. Some schemas transform input in ways that can cause server-side validation to fail. If your schemas do not do that, set forwardValidatedInput to true.

const link = new RPCLink({
  plugins: [
    new RequestValidationLinkPlugin(contract, {
      forwardValidatedInput: true,
    }),
  ],
})

Custom Validation Errors

If you have already customized validation errors on the server, you can use interceptors to catch and map the validation errors thrown by this plugin so they match your server-side errors.

import { ORPCError } from '@orpc/client'
import { ValidationError } from '@orpc/contract'

const link = new RPCLink({
  plugins: [
    new RequestValidationLinkPlugin(contract),
  ],
  interceptors: [
    async ({ next }) => {
      try {
        return await next()
      }
      catch (error) {
        if (
          error instanceof ORPCError
          && error.code === 'BAD_REQUEST'
          && error.cause instanceof ValidationError
        ) {
          throw new CustomInputValidationError(error.cause.issues)
        }

        throw error
      }
    }
  ]
})

Form Validation

You can pair this plugin with Form Data Helpers to avoid heavier form validation libraries and keep your contract as the single source of truth on both the client and server.

import { getIssueMessage, parseFormData } from '@orpc/openapi/helpers'

export function ContactForm() {
  const [error, setError] = useState()

  const handleSubmit = async (form: FormData) => {
    try {
      const output = await client.someProcedure(parseFormData(form))
      console.log(output)
    }
    catch (error) {
      setError(error)
    }
  }

  return (
    <form action={handleSubmit}>
      <input name="user[name]" type="text" />
      <span>{getIssueMessage(error, 'user[name]')}</span>

      <input name="user[emails][]" type="email" />
      <span>{getIssueMessage(error, 'user[emails][]')}</span>

      <button type="submit">Submit</button>
    </form>
  )
}

Learn More

For implementation details, see the source code.

Last updated on August 6, 2026

Was this page helpful?