> ## Documentation Index
> Fetch the complete documentation index at: https://www.orionjs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Schema Validation

> How schema validation works in Orionjs

Schema validation is a core feature of Orionjs that ensures data conforms to the structure and rules defined in your schemas. This is particularly important for ensuring data integrity in your application.

## Automatic Validation

When using a schema with MongoDB collections or GraphQL resolvers, validation happens automatically:

* **MongoDB**: Documents are validated before being inserted or updated
* **GraphQL**: Input data is validated before being processed by your resolvers

This automatic validation ensures that your data always adheres to your schema rules.

## Manual Validation

You can also manually validate any object against a schema using the `validate` function:

```typescript theme={null}
import {validate} from '@orion-js/schema'
import {UserSchema} from './schemas/UserSchema'

try {
  await validate(UserSchema, {
    name: 'John Doe',
    email: 'john@example.com'
  })
  // Validation passed
} catch (error) {
  // error is a ValidationError
  console.error(error.message)
  console.error(error.details) // Contains detailed information about validation failures
}
```

The `validate` function will:

1. Check that all required fields are present
2. Verify types match the schema definition
3. Apply any validation rules defined in the schema
4. Throw a `ValidationError` if validation fails

## ValidationError

When validation fails, a `ValidationError` is thrown with:

```typescript theme={null}
{
  message: 'Validation Error', // Human-readable error message
  details: [
    {
      message: 'is required', // Human-readable field error message
      path: 'email' // Path to the field that failed validation
    }
  ]
}
```

For nested fields, the path will include the full path to the field:

```typescript theme={null}
{
  details: [
    {
      message: 'must be at least 5 characters',
      path: 'address.street'
    }
  ]
}
```

## Custom Validation

You can create custom validation rules using the `validate` option in property definitions:

```typescript theme={null}
import {schemaWithName, InferSchemaType} from '@orion-js/schema'

export const ProductSchema = schemaWithName('ProductSchema', {
  price: {
    type: Number,
    validate: (value) => {
      if (value < 0) {
        return 'price-must-be-positive'
      }
      if (value > 1000000) {
        return 'price-too-high'
      }
    }
  },
  
  slug: {
    type: String,
    validate: async (value) => {
      // You can also perform async validation
      const exists = await checkIfSlugExists(value)
      if (exists) {
        return 'slug-already-exists'
      }
    }
  }
})

export type ProductType = InferSchemaType<typeof ProductSchema>
```

The validate function:

* Receives the field value as its first parameter
* Should return an error message string if validation fails
* Should return nothing (undefined) if validation passes
* Can be async (returns a Promise)

## Advanced Validation

For more complex validation that depends on multiple fields, you can use the options object:

```typescript theme={null}
billingInfo: {
  type: String,
  optional: true,
  validate: (value, {doc}) => {
    if (doc.type === 'subscription' && !value) {
      return 'billing-info-required-for-subscriptions'
    }
  }
}
```

## Cleaning Data

Before validation, Orionjs "cleans" the data by:

1. Removing fields not defined in the schema
2. Applying any `clean` functions defined on fields
3. Setting default values for missing fields

You can manually clean data using the `clean` function:

```typescript theme={null}
import {clean} from '@orion-js/schema'
import {UserSchema} from './schemas/UserSchema'

const cleanedData = await clean(UserSchema, {
  name: 'John Doe ',  // Note the trailing space
  email: 'JOHN@EXAMPLE.COM',
  extraField: 'This will be removed'
})

// Result:
// {
//   name: 'John Doe',  // If a clean function trims the string
//   email: 'john@example.com',  // If a clean function lowercases the email
// }
```

## Validation in GraphQL Resolvers

When using schemas with GraphQL resolvers, validation happens automatically for parameters:

```typescript theme={null}
import {createMutation, Resolvers} from '@orion-js/graphql'
import {schemaWithName, InferSchemaType} from '@orion-js/schema'

export const CreateUserParams = schemaWithName('CreateUserParams', {
  name: {type: String, min: 2},
  email: {type: String}
})

export type CreateUserParamsType = InferSchemaType<typeof CreateUserParams>

@Resolvers()
class UserResolvers {
  @Mutation()
  createUser = createMutation({
    params: CreateUserParams,
    returns: Boolean,
    resolve: async (params: CreateUserParamsType) => {
      // Params are already validated
      // If validation failed, GraphQL would return an error
      // ...implementation
      return true
    }
  })
}
```

This ensures that your API is always receiving valid data before it reaches your business logic.

## Validation in Schema Definition

When defining a property in a schema, you can specify validation constraints:

```typescript theme={null}
import {schemaWithName, InferSchemaType} from '@orion-js/schema'

export const ProductSchema = schemaWithName('ProductSchema', {
  name: {
    type: String,
    min: 3,
    max: 100,
    validate: value => {
      if (value.includes('forbidden')) {
        return 'name-contains-forbidden-word'
      }
    }
  },
  
  price: {
    type: Number,
    min: 0
  },
  
  description: {
    type: String,
    optional: true
  }
})

export type ProductType = InferSchemaType<typeof ProductSchema>
```

The validation will run automatically when:

1. A document is validated against the schema (e.g., in MongoDB operations)
2. A GraphQL input is processed
3. You manually call `clean` or `validate` on your schema
