HomeAbout UsServicesProductCase StudyBlogsContact Us
Try AI Dashboard
  1. Home
  2. /
  3. Blogs
  4. /
  5. Boosting Frappe: Smarter DTO validations

Scalix Insights3 min read

Boosting Frappe: Smarter DTO validations

By Prafful Suthar

Adding payload validation on frappe API

As we build more complex APIs in frappe and integrate with third party frontend we get usecases around validating and reject a request before it ever reaches the logical layer, for this usecase other frameworks like Node/Nest have DTO validations like class-validator and class-transformer we have implemented something similar thats directly compatible with frappe.

API Request, Payload & DTO validation

Schema validation for incoming API request can be done by using utility functions and decorators provided in frappe_utils

Example

Lets consider we have to make an API that accepts following persons information.

{
    "name": "Jon",
    "age": 18,
    "email": "jon@castlecraft.com",
    "addresses": [
        {
            "city": "Mumbai",
            "state": "MH",
            "zip_code": "400068"
        }
    ]
}

We create a whitelisted endpoint in frappe to accept this request

@frappe.whitelist(methods=["POST"])
def create_person():
    # your logic
    return True

There are few validation that we would we want to make sure before the API call reaches the business logic such as - All required fields are provided - Fields with email are valid emails - Fields length are as expected - Nested fields are not null - etc

For defining our schema we can use python classes with marshmallow

Note: If your not using frappe_utils as a part of your frappe site you would have to install marshmallow or add it to your respective project dependency.

Create a schema for your anticipated request

from marshmallow import Schema, fields


class AddressSchema(Schema):
    street = fields.Str(required=True, validate=lambda s: len(s) > 0)
    city = fields.Str(required=True, validate=lambda s: len(s) > 0)
    zip_code = fields.Str(required=True, validate=lambda s: len(s) > 0)


class PersonSchema(Schema):
    name = fields.Str(
        required=True,
    )
    age = fields.Int(required=True)
    email = fields.Email(required=True)
    addresses = fields.List(fields.Nested(AddressSchema), required=True)

We can now append this schema validation onto our whitelisted function

import frappe

from frappe_utils.request_validator.request_validator import request_validator

@frappe.whitelist(methods=["POST"])
@request_validator(PersonSchema)
def create_person(*args, **kwargs):
    # body = kwargs.get(RequestAttributes.body.value)

    # your logic
    return True

Above will make sure to validate incoming request with validation as per the schema definitions.

Note: Request body is always available as a part of kwargs and could be fetched as shown above.

Custom Validation

Lets consider a few cases - Custom error message mapping (default we give string message) - Have addition attributes as a part of kwargs

you can use provided config and overwrite options to achieve the same

Schema

Complete docs on writing custom validation

# Schema with custom validation and allowed unknown fields
class PersonSchemaCustom(Schema):
    name = fields.Str(required=True, validate=lambda s: len(s) > 0)
    age = fields.Int(required=True, validate=lambda n: 18 <= n <= 150)
    email = fields.Email(required=True)
    addresses = fields.List(fields.Nested(AddressSchema), required=True)

    # Custom validation for any field
    @validates_schema
    def validate_addresses(self, data, **kwargs):
        addresses = data.get("addresses")
        if not addresses or len(addresses) == 0:
            raise ValidationError("Addresses must not be empty")

    # To allow fields that are not defined in schema
    class Meta:
        unknown = True

Python Options

def my_custom_error_function(e):
    return "CUSTOM ERROR MESSAGE!"

@frappe.whitelist(methods=["POST"])
@request_validator(
    PersonSchemaCustom,
    {
        "validation_error_parser": my_custom_error_function,
        "request_attributes": [
            RequestAttributes.body,
            RequestAttributes.user,
            RequestAttributes.request,
            RequestAttributes.query,
        ],
    },
)
def create_person(*args, **kwargs):
    body = kwargs.get(RequestAttributes.body.value)
    user = kwargs.get(RequestAttributes.user.value)
    query = kwargs.get(RequestAttributes.query.value)

    # Full Request
    request = kwargs.get(RequestAttributes.request.value)

    return body, user, query

Overwrite options for request_validator

ValueDefaultsDetailsvalidation_error_parserReturns readable error stringAccepts a callable function that takes 1 argument of type ValidateError from Marshmallow, function provided to this option will be called when there is error in validation of request and whatever the function returns will be appended as the error for API response.

Current: {"status_code": 400, "error": "Field: name, Errors: Missing data for required field.\n.."}

Overwritten: {"status_code": 400, "error": "Your Custom Error"}request_attributes[RequestAttributes.body]Provide options for any attributes that you would need and it would be appended to kwargs parameter.

Default body is available as in kwargs, you can append

RequestAttributes.user For getting session user

RequestAttributes.request For complete request

RequestAttributes.query For request query

About the author

Prafful Suthar writes for Scalix on enterprise platforms, high-throughput systems, and ERP delivery. Scalix builds and migrates mission-critical software for regulated and scale-heavy organizations from Mumbai and remote-first teams. See our case studies for problem–solution–result write-ups, or contact us about your roadmap.

Related case studies

  • Reliance — SAP-class MDM and Frappe at conglomerate scale
  • Citixsys / iVend — retail ERP and secure distribution
  • SprintMoney — fintech integrations and platform performance
Footer Logo

About

  • Company
  • Blogs
  • Contact Us

Services

  • Custom Development
  • System Optimization
  • Infrastructure & Operation
  • Strategic Technology
  • Quality Assurance

Got a question?

Phone icon

Call us

+91 - 9987938039
Email icon

Email Us

sales@scalix.in
Location icon

Location

113 - Tanvi Diamoda Gold, Tanvi Complex,
S.V. Road, Dahisar (E),
Mumbai, Maharashtra 400068.
Footer Logo

Got a question?

Phone icon

Call us

+91 - 9987938039
Email icon

Email Us

sales@scalix.in
Location icon

Location

113 - Tanvi Diamoda Gold, Tanvi Complex,
S.V. Road, Dahisar (E),
Mumbai, Maharashtra 400068.

About

  • Company
  • Blogs
  • Contact Us

Services

  • Custom Development
  • System Optimization
  • Infrastructure & Operation
  • Strategic Technology
  • Quality Assurance

© Copyright 2026. All Rights Reserved.