# How to Use Custom Validators with Property Inflate/Deflate in Neomodel

> Learn to use custom validators with neomodel property inflate and deflate. Leverage the @validator decorator for seamless error handling and efficient data transformation in your Neo4j application.

- Repository: [Neo4j Contrib/neomodel](https://github.com/neo4j-contrib/neomodel)
- Tags: how-to-guide
- Published: 2026-03-08

---

**Use the `@validator` decorator from `neomodel.properties` to wrap custom `inflate` and `deflate` methods, ensuring exceptions are automatically converted to `InflateError` or `DeflateError` for consistent error handling.**

In **neomodel**, every property class defines two core methods for data conversion: `inflate` (Neo4j → Python) and `deflate` (Python → Neo4j). When implementing custom validators with property inflate deflate logic, you must decorate these methods to ensure proper error handling and integration with neomodel's exception hierarchy.

## Understanding Property Inflate and Deflate in Neomodel

Every property in neomodel inherits from the base `Property` class defined in [`neomodel/properties.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/properties.py). The two conversion hooks work as follows:

- **`inflate(self, value, rethrow=False)`**: Converts a raw value from the Neo4j driver into a Python-native type.
- **`deflate(self, value, rethrow=False)`**: Converts a Python value into a format suitable for Neo4j storage.

These methods are wrapped by the `validator` decorator (lines 18-45 in [`neomodel/properties.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/properties.py)), which validates the method name, catches any exception raised by your implementation, and re-raises it as a dedicated `InflateError` or `DeflateError`.

## The Validator Decorator Architecture

The `@validator` decorator is the critical component for implementing custom validators with property inflate deflate workflows. Located in [`neomodel/properties.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/properties.py), it performs three essential functions:

1. **Method name validation**: Ensures the decorated function is named either `inflate` or `deflate`.
2. **Exception translation**: Catches any exception and wraps it in `InflateError` or `DeflateError` (or `NeomodelException` for unknown methods).
3. **Rethrow control**: Respects the `rethrow=False` parameter used by `ArrayProperty` to suppress conversion-specific errors and allow container-level handling.

The decorator uses a `match` statement (lines 31-35) to map exceptions to the correct error type, ensuring consistent error handling across all property types.

## Implementing Custom Validators with Inflate and Deflate

### Creating a Custom Property Class

To create a property with custom validation logic, subclass `Property` (or `NormalizedProperty`) and decorate your `inflate` and `deflate` methods with `@validator`:

```python
from neomodel import StructuredNode
from neomodel.properties import Property, validator

class UpperCaseString(Property):
    """Stores strings as uppercase in Neo4j but presents them as lowercase in Python."""
    
    @validator
    def inflate(self, value):
        # Neo4j → Python: ensure string and convert to lowercase

        if not isinstance(value, str):
            raise ValueError("Expected a string from database")
        return value.lower()
    
    @validator
    def deflate(self, value):
        # Python → Neo4j: ensure string and convert to uppercase

        if not isinstance(value, str):
            raise ValueError("Expected a string value")
        return value.upper()

```

Usage in a node class:

```python
class Person(StructuredNode):
    name = UpperCaseString()

# Writing: "Bob" is stored as "BOB" in Neo4j

person = Person(name="Bob").save()

# Reading: retrieved as "bob" in Python

retrieved = Person.nodes.get(uuid=person.uuid)
assert retrieved.name == "bob"

```

### Adding Validators to Existing Properties

You can inject custom validation into existing property classes without subclassing by applying the decorator at runtime:

```python
from neomodel.properties import StringProperty, validator

def non_empty_inflate(self, value):
    if value == "":
        raise ValueError("Empty strings are not allowed")
    return value

# Wrap the custom logic with the validator decorator

StringProperty.inflate = validator(non_empty_inflate)

class Article(StructuredNode):
    title = StringProperty(required=True)

```

Now, attempting to inflate an empty string from Neo4j raises an **`InflateError`** rather than a raw `ValueError`, maintaining consistency with neomodel's error hierarchy.

### Handling Array Properties with rethrow=False

When working with container properties like `ArrayProperty`, use `rethrow=False` to allow the container to handle individual item failures:

```python
from neomodel.properties import ArrayProperty, validator

class ValidatedArrayProperty(ArrayProperty):
    @validator
    def inflate(self, value, rethrow=False):
        # Process array items, allowing container to handle individual failures

        return super().inflate(value, rethrow=False)

```

The `rethrow=False` parameter suppresses the conversion-specific `InflateError` or `DeflateError`, allowing `ArrayProperty` to catch exceptions and handle them appropriately for individual array elements.

## Error Handling and Exception Types

When using custom validators with property inflate deflate methods, the `@validator` decorator ensures all exceptions are mapped to specific neomodel exception types:

- **`InflateError`**: Raised when `inflate` fails to convert a Neo4j value to Python.
- **`DeflateError`**: Raised when `deflate` fails to convert a Python value to Neo4j.
- **`NeomodelException`**: Generic fallback for unknown method names (should not occur in practice).

These exceptions are defined in the neomodel exception hierarchy and provide consistent error handling across your application, regardless of which specific property implementation raises the error.

## Summary

- **Use the `@validator` decorator** from `neomodel.properties` when implementing custom `inflate` and `deflate` methods to ensure proper error handling.
- **Implement custom validators** by subclassing `Property` and decorating conversion methods, or apply the decorator to existing properties at runtime.
- **Handle exceptions consistently** through `InflateError` and `DeflateError` types, which the validator automatically raises when conversion fails.
- **Use `rethrow=False`** when implementing container properties like arrays to allow fine-grained error handling at the item level.

## Frequently Asked Questions

### What is the difference between inflate and deflate in neomodel?

**Inflate** converts values from Neo4j driver types to Python-native types when reading from the database, while **deflate** converts Python values back to Neo4j-compatible types when writing to the database. Both methods are defined in the base `Property` class in [`neomodel/properties.py`](https://github.com/neo4j-contrib/neomodel/blob/main/neomodel/properties.py) and are wrapped by the `@validator` decorator to ensure consistent error handling.

### How do I create a custom validator for a neomodel property?

Create a subclass of `Property` (or `NormalizedProperty`), implement `inflate` and/or `deflate` methods with your validation logic, and decorate each method with `@validator` imported from `neomodel.properties`. The decorator ensures any raised exceptions are converted to `InflateError` or `DeflateError`. Alternatively, you can apply the decorator to existing property classes at runtime to inject validation without subclassing.

### What exceptions are raised when custom validators fail?

The `@validator` decorator catches any exception raised inside your `inflate` or `deflate` implementation and re-raises it as an **`InflateError`** (for inflate failures) or **`DeflateError`** (for deflate failures). These are specific neomodel exception types defined in the library's exception hierarchy, providing consistent error handling regardless of which property type fails.

### Can I add validation to existing neomodel properties without subclassing?

Yes. You can monkey-patch existing property classes by assigning a decorated method to the class. Define your custom inflate or deflate function, wrap it with `validator()` from `neomodel.properties`, and assign it to the property class (e.g., `StringProperty.inflate = validator(my_custom_inflate)`). This approach allows you to inject validation logic into built-in property types without creating subclasses.