Extend validators with Medusa.js 1.8.x and up

Extend validators with Medusa.js 1.8.x and up

Are you building a commerce store with Medusa.js and want to add custom validators to your products?

·

3 min read

With Medusa.js version >=1.8.x, it's now possible to extend and add new properties to validators that are triggered on some endpoints.

In this guide, I'll walk you through the steps required to extend core validators with Medusa.js. Let's get started!

Movie gif. Nicholas Cage as Peter in Vampire's Kiss leaning forward from an office chair with a cigarette hanging out of his mouth, pointing out excitedly.

Extending Core Validators

In that new version of Medusa.js, you can add file loaders that will be executed when the server is launched. These loaders allow you to fully customize Medusa.js to your needs.

Create an utility function

The first step to extending core validators is to create a function that allows you to override the validator in question. Here's an example of what this function might look like:

// src/utils/register-extended-validator.ts
import { ValidatorOptions } from 'class-validator'
import { ClassConstructor } from '@medusajs/medusa'

const extendedValidators: any = []
let isInitialized = false

export async function registerExtendedValidator<
    T extends ClassConstructor<any>,
>(classValidator: T): Promise<void> {
    extendedValidators.push(classValidator)

    if (isInitialized) {
        return
    }

    isInitialized = true

    const module = await import('@medusajs/medusa/dist/utils/validator')
    const originalValidator = module.validator
    module.validator = <T extends ClassConstructor<any> = any, V = any>(
        typedClass: T,
        plain: V,
        config?: ValidatorOptions,
    ): Promise<any> => {
        for (const extendedValidator of extendedValidators) {
            if (extendedValidator.name === typedClass.name) {
                typedClass = extendedValidator
                break
            }
        }
        return originalValidator(typedClass, plain, config)
    }
}

This code accepts a validator as a parameter.
This validator will be used to find a match in existing internal validators. Once the validator has been found it will be overridden ✅

Create an extended validator

In my case I decided to extend the product creation validator to add a new property store_id.

// We alias the core validator so we can have the EXACT same name as the core validator.
import { AdminPostProductsReq as MedusaAdminPostProductsReq } from '@medusajs/medusa'
import { IsEnum, IsString } from 'class-validator'

export class AdminPostProductsReq extends MedusaAdminPostProductsReq {
    @IsString()
    store_id: string
}

Call the Function in a Loader

Now you need to call this function inside a loader with a custom validator.

A loader is a file that exports a function that Medusa.js will execute when the server is launched.

Here's an example of what the loader might look like:

// src/loaders/index.ts
export default async function () {
    registerExtendedValidator(AdminPostProductsReq)
}

This loader calls the registerExtendedValidator function and passes in the new AdminPostProductsReq class to register an extended validator for the create product endpoint.

This means that my endpoint will now wait for store_id to be part of the new DTO.

Conclusion

Zak Wow GIF by Bank Cler

That's it! You've now learned how to extend core validators with Medusa.js 1.8.x.
By using this function inside a loader and registering an extended version of a specific validator, you can add custom validation logic to your Medusa.js commerce store.

I hope you found this guide helpful.
If you have any questions or comments, feel free to leave them below.

A special thanks to Adrien De Peretti for his help with this.