Skip to content

Support input validation on the direct server-action RPC path #245

@vivek7405

Description

@vivek7405

Problem

The primary way actions are called (importing them from a client component, which becomes the RPC stub) has no built-in input validation seam. route.validate is only honored inside the expose() REST path (actions.js:423); invokeAction (the /__webjs/action RPC path the docs push as 'the API') deserializes args and calls fn(...args) with no validation hook. Every action hand-writes argument coercion and returns an ad-hoc single-string error, and the validator cannot be declared once and shared by both call paths.

Design / approach

Let a validate option (a plain function or zod schema the app owns) attach to an action and run on both the RPC and expose paths, returning a structured field-error shape. The framework only invokes the validator and shapes the result; it ships no validation library.

Web-standards fit: Validation is the app's own small function; the framework just calls it on both call paths, adding no dependency or DSL.

Prior art: Next server actions wrapping zod safeParse returning fieldErrors; Remix action zod field errors; tRPC per-procedure input validator across transports; Fastify uniform body validation.

Acceptance criteria

  • A validate/schema option attached to a server action runs on both the RPC import path and the expose REST path
  • Validation failure produces a structured field-error result an app can render, not just a single string
  • The validator runs server-side before the action body regardless of how the action was invoked
  • packages/server/test/actions covers RPC-path validation rejection and pass-through
  • AGENTS.md and recipes document declaring action input validation once
  • Tests cover the new behaviour at the applicable layer(s)
  • Docs / AGENTS.md / CONVENTIONS.md updated if the public surface changed

Filed from the production-readiness audit (webjs vs Next.js / Remix / Rails / Turbo / Lit). Theme: data-forms. Priority: P1. Kept to webjs identity: no-build, progressive enhancement, web-components-first, AI-first, batteries-included, close to web standards.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions