> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kit-rs.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Requests

> Validate and parse incoming request data with the #[request] attribute

Kit provides a simple pattern for handling incoming request data with automatic validation. The `#[request]` attribute works with both JSON and form-urlencoded data, making it suitable for REST APIs and HTML forms alike.

## Overview

Requests combine three concerns into a single, declarative struct:

1. **Data parsing** - Automatically parse JSON or form-urlencoded data
2. **Validation** - Validate fields using the `validator` crate
3. **Authorization** - Optionally check if the request is authorized

## The `#[handler]` Attribute

All controller methods in Kit use the `#[handler]` attribute. This enables automatic extraction and validation of request data:

```rust theme={null}
use kit::{handler, json_response, Request, Response};

// Simple handler with Request
#[handler]
pub async fn index(req: Request) -> Response {
    json_response!({ "message": "Hello" })
}
```

When combined with validated request types, validation happens automatically:

```rust theme={null}
use kit::{handler, json_response, Response};
use crate::requests::CreateUserRequest;

// Request handler - automatically validates incoming data
#[handler]
pub async fn store(form: CreateUserRequest) -> Response {
    // `form` is already validated - this code only runs if validation passes
    json_response!({
        "email": form.email,
        "name": form.name
    })
}
```

## Defining a Request

The `#[request]` attribute automatically adds `Deserialize` and `Validate` derives:

```rust theme={null}
use kit::request;

#[request]
pub struct CreateUserRequest {
    #[validate(email(message = "Please provide a valid email address"))]
    pub email: String,

    #[validate(length(min = 8, message = "Password must be at least 8 characters"))]
    pub password: String,

    #[validate(length(min = 1, max = 100, message = "Name is required"))]
    pub name: String,
}
```

## Validation Rules

Kit uses the `validator` crate for validation. Here are common validation rules:

### String Validations

```rust theme={null}
#[request]
pub struct ExampleRequest {
    // Required (non-empty)
    #[validate(length(min = 1, message = "This field is required"))]
    pub name: String,

    // Email format
    #[validate(email(message = "Invalid email address"))]
    pub email: String,

    // URL format
    #[validate(url(message = "Invalid URL"))]
    pub website: String,

    // Length constraints
    #[validate(length(min = 8, max = 100))]
    pub password: String,

    // Regex pattern
    #[validate(regex(path = "PHONE_REGEX", message = "Invalid phone number"))]
    pub phone: String,
}
```

### Numeric Validations

```rust theme={null}
#[request]
pub struct ProductRequest {
    // Range validation
    #[validate(range(min = 0, max = 10000, message = "Price must be between 0 and 10000"))]
    pub price: f64,

    // Minimum value
    #[validate(range(min = 1))]
    pub quantity: i32,

    // Maximum value
    #[validate(range(max = 100))]
    pub discount_percent: i32,
}
```

### Nested and Collection Validations

```rust theme={null}
use serde::Deserialize;

#[derive(Deserialize, Validate)]
pub struct Address {
    #[validate(length(min = 1))]
    pub street: String,

    #[validate(length(min = 1))]
    pub city: String,
}

#[request]
pub struct OrderRequest {
    // Nested struct validation
    #[validate(nested)]
    pub shipping_address: Address,

    // Collection length
    #[validate(length(min = 1, message = "At least one item required"))]
    pub items: Vec<String>,
}
```

### Common Validation Attributes

| Attribute          | Description               | Example                                            |
| ------------------ | ------------------------- | -------------------------------------------------- |
| `email`            | Valid email format        | `#[validate(email)]`                               |
| `url`              | Valid URL format          | `#[validate(url)]`                                 |
| `length`           | String/collection length  | `#[validate(length(min = 1, max = 100))]`          |
| `range`            | Numeric range             | `#[validate(range(min = 0, max = 100))]`           |
| `regex`            | Regex pattern match       | `#[validate(regex(path = "PATTERN"))]`             |
| `contains`         | String contains substring | `#[validate(contains(pattern = "@"))]`             |
| `does_not_contain` | String doesn't contain    | `#[validate(does_not_contain(pattern = "admin"))]` |
| `nested`           | Validate nested struct    | `#[validate(nested)]`                              |

## Validation Error Response

When validation fails, Kit automatically returns a 422 response with Laravel/Inertia-compatible error format:

```json theme={null}
HTTP 422 Unprocessable Entity

{
    "message": "The given data was invalid.",
    "errors": {
        "email": ["Please provide a valid email address"],
        "password": ["Password must be at least 8 characters"]
    }
}
```

This format integrates seamlessly with Inertia.js form handling on the frontend.

## Complete Example

Here's a complete example of a user registration endpoint:

**Define the request:**

```rust theme={null}
// src/requests/create_user.rs
use kit::request;

#[request]
pub struct CreateUserRequest {
    #[validate(email(message = "Please provide a valid email address"))]
    pub email: String,

    #[validate(length(min = 8, message = "Password must be at least 8 characters"))]
    pub password: String,

    #[validate(length(min = 2, max = 50, message = "Name must be between 2 and 50 characters"))]
    pub name: String,
}
```

**Create the controller:**

```rust theme={null}
// src/controllers/user.rs
use kit::{handler, json_response, Request, Response, ResponseExt};
use crate::requests::CreateUserRequest;

#[handler]
pub async fn index(_req: Request) -> Response {
    json_response!({ "users": [] })
}

#[handler]
pub async fn store(form: CreateUserRequest) -> Response {
    // Validation passed - create the user
    // In a real app, you'd save to database here

    json_response!({
        "user": {
            "email": form.email,
            "name": form.name
        },
        "message": "User created successfully"
    })
    .status(201)
}
```

**Register the routes:**

```rust theme={null}
// src/routes.rs
use kit::{get, post, routes};
use crate::controllers;

routes! {
    get!("/users", controllers::user::index).name("users.index"),
    post!("/users", controllers::user::store).name("users.store"),
}
```

## Authorization

You can override the `authorize` method to add authorization checks:

```rust theme={null}
use kit::request;

#[request]
pub struct DeleteUserRequest {
    pub user_id: i64,
}

impl DeleteUserRequest {
    fn authorize(req: &kit::Request) -> bool {
        // Check if user has admin role
        // Return false to reject with 403 Forbidden
        req.header("X-Admin-Token").is_some()
    }
}
```

If `authorize` returns `false`, the request is rejected with a 403 Forbidden response:

```json theme={null}
HTTP 403 Forbidden

{
    "message": "This action is unauthorized."
}
```

## Request Content Types

Requests automatically detect and parse the content type:

* `application/json` - Parsed as JSON
* `application/x-www-form-urlencoded` - Parsed as form data

The parsing is handled automatically based on the `Content-Type` header.

## Using Request with Validated Data

If you need access to both the validated data and the original request (for headers, params, etc.), you can still access request information in your controller:

```rust theme={null}
use kit::{handler, json_response, Response, App};
use crate::requests::CreateUserRequest;
use crate::services::UserService;

#[handler]
pub async fn store(form: CreateUserRequest) -> Response {
    // Access services via dependency injection
    let user_service = App::resolve::<UserService>();

    // Use the validated form data
    let user = user_service.create_user(&form.email, &form.name);

    json_response!({ "user": user })
}
```

## File Organization

The standard structure for requests:

```
src/
├── requests/
│   ├── mod.rs                 # Re-exports all requests
│   ├── create_user.rs         # CreateUserRequest
│   ├── update_user.rs         # UpdateUserRequest
│   └── create_post.rs         # CreatePostRequest
├── controllers/
│   └── user.rs                # Uses CreateUserRequest
└── routes.rs
```

**src/requests/mod.rs:**

```rust theme={null}
pub mod create_user;
pub mod update_user;

pub use create_user::CreateUserRequest;
pub use update_user::UpdateUserRequest;
```

## End-to-End Type Safety with Inertia

Requests can also derive `InertiaProps` to generate TypeScript types, enabling end-to-end type safety from your Rust backend to your React frontend.

### Generating TypeScript Types for Requests

Add `InertiaProps` derive alongside `#[request]`:

```rust theme={null}
use kit::{request, InertiaProps};

#[request]
#[derive(InertiaProps)]
pub struct CreateTodoRequest {
    #[validate(length(min = 1, message = "Title is required"))]
    pub title: String,

    #[validate(length(max = 500))]
    pub description: Option<String>,
}
```

Run type generation:

```bash theme={null}
kit generate-types
```

This generates TypeScript types in `frontend/src/types/inertia-props.ts`:

```typescript theme={null}
export interface CreateTodoRequest {
  title: string
  description: string | null
}
```

### Type-Safe Forms with Inertia

Use Inertia's `<Form>` component for the cleanest form handling:

```tsx theme={null}
import { Form, usePage } from '@inertiajs/react'

export default function CreateTodo() {
  const { errors } = usePage().props

  return (
    <Form action="/todos" method="post">
      <input
        type="text"
        name="title"
        placeholder="Todo title"
      />
      {errors?.title && <span className="error">{errors.title}</span>}

      <textarea
        name="description"
        placeholder="Description (optional)"
      />

      <button type="submit">Create Todo</button>
    </Form>
  )
}
```

For more control, combine `<Form>` with the `useForm` hook and your generated types:

```tsx theme={null}
import { Form, useForm } from '@inertiajs/react'
import type { CreateTodoRequest } from '../types/inertia-props'

export default function CreateTodo() {
  const { data, setData, errors, processing } = useForm<CreateTodoRequest>({
    title: '',
    description: null,
  })

  return (
    <Form action="/todos" method="post">
      {({ processing }) => (
        <>
          <input
            type="text"
            name="title"
            value={data.title}
            onChange={(e) => setData('title', e.target.value)}
            placeholder="Todo title"
          />
          {errors.title && <span className="error">{errors.title}</span>}

          <textarea
            name="description"
            value={data.description || ''}
            onChange={(e) => setData('description', e.target.value || null)}
            placeholder="Description (optional)"
          />

          <button type="submit" disabled={processing}>
            Create Todo
          </button>
        </>
      )}
    </Form>
  )
}
```

### Benefits of End-to-End Type Safety

1. **Compile-time checks**: TypeScript catches field name typos and type mismatches
2. **IDE autocomplete**: Full IntelliSense for form fields in your editor
3. **Validation alignment**: Your TypeScript types match your Rust validation rules
4. **Refactoring safety**: Rename a field in Rust, TypeScript errors show where to update

### Workflow

1. Define request with validation in Rust
2. Add `#[derive(InertiaProps)]` to the struct
3. Run `kit generate-types` to generate TypeScript
4. Use the generated type with `useForm<RequestType>`
5. Get full type safety and validation error handling

<Note>
  For more information on TypeScript type generation, see [TypeScript Types](/frontend/typescript-types).
</Note>

## Summary

| Feature           | Description                                       |
| ----------------- | ------------------------------------------------- |
| Define request    | `#[request]` attribute                            |
| Handler attribute | `#[handler]` on all controller methods            |
| Validation        | Use `#[validate(...)]` attributes                 |
| Error format      | Laravel/Inertia-compatible 422 JSON               |
| Authorization     | Override `authorize()` method                     |
| Auto content-type | Detects JSON vs form-urlencoded                   |
| TypeScript types  | Add `#[derive(InertiaProps)]` for type generation |
| Type-safe forms   | Use generated types with `useForm<T>`             |
