Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
@pothos/plugin-validation
Advanced tools
A plugin for adding validation for field arguments based on zod. This plugin does not expose zod directly, but most of the options map closely to the validations available in zod.
To use the validation plugin you will need to install both zod
package and the validation plugin:
yarn add zod @pothos/plugin-validation
import ValidationPlugin from '@pothos/plugin-validation';
const builder = new SchemaBuilder({
plugins: [ValidationPlugin],
validationOptions: {
// optionally customize how errors are formatted
validationError: (zodError, args, context, info) => {
// the default behavior is to just throw the zod error directly
return zodError;
},
},
});
builder.queryType({
fields: (t) => ({
simple: t.boolean({
nullable: true,
args: {
// Validate individual args
email: t.arg.string({
validate: {
email: true,
},
}),
phone: t.arg.string(),
},
// Validate all args together
validate: (args) => !!args.phone || !!args.email,
resolve: () => true,
}),
}),
});
validationError
: (optional) A function that will be called when validation fails. The function
will be passed the the zod validation error, as well as the args, context and info objects. It can
throw an error, or return an error message or custom Error instance.
builder.queryType({
fields: (t) => ({
withMessage: t.boolean({
nullable: true,
args: {
email: t.arg.string({
validate: {
email: [true, { message: 'invalid email address' }],
},
}),
phone: t.arg.string(),
},
validate: [
(args) => !!args.phone || !!args.email,
{ message: 'Must provide either phone number or email address' },
],
resolve: () => true,
}),
}),
});
builder.queryType({
fields: (t) => ({
list: t.boolean({
nullable: true,
args: {
list: t.arg.stringList({
validate: {
items: {
email: true,
},
maxLength: 3,
},
}),
},
resolve: () => true,
}),
}),
});
If you just want to use a zod schema defined somewhere else, rather than using the validation
options you can use the schema
option:
builder.queryType({
fields: (t) => ({
list: t.boolean({
nullable: true,
args: {
max5: t.arg.int({
validate: {
schema: zod.number().int().max(5),
},
}),
},
resolve: () => true,
}),
}),
});
validate
: Refinement<T>
| Refinement<T>[]
| ValidationOptions
.validate
: Refinement<T>
| Refinement<T>[]
| ValidationOptions
.validate
: Refinement<T>
| Refinement<T>[]
| ValidationOptions
.Refinement
A Refinement
is a function that will be passed to the zod
refine
method. It receives the args
object, input object, or value of the specific field the refinement is defined on. It should return
a boolean
or Promise<boolean>
.
Refinement
s can either be just a function: (val) => isValid(val)
, or an array with the function,
and an options object like: [(val) => isValid(val), { message: 'field should be valid' }]
.
The options object may have a message
property, and if the type being validated is an object, it
can also include a path
property with an array of strings indicating the path of the field in the
object being validated. See the zod docs on refine
for more details.
ValidationOptions
The validation options available depend on the type being validated. Each property of
ValidationOptions
can either be a value specific to the constraint, or an array with the value,
and the options passed to the underlying zod method. This options object can be used to set a custom
error message:
{
validate: {
max: [10, { message: 'should not be more than 10' }],
int: true,
}
}
type
?: 'number'
refine
?: Refinement<number> | Refinement<number>[]
min
?: Constraint<number>
max
?: Constraint<number>
positive
?: Constraint<boolean>
nonnegative
?: Constraint<boolean>
negative
?: Constraint<boolean>
nonpositive
?: Constraint<boolean>
int
?: Constraint<boolean>
schema
?: ZodSchema<number>
type
?: 'bigint'
refine
?: Refinement<bigint> | Refinement<bigint>[]
schema
?: ZodSchema<bigint>
type
?: 'boolean'
refine
?: Refinement<boolean> | Refinement<boolean>[]
schema
?: ZodSchema<boolean>
type
?: 'boolean'
refine
?: Refinement<boolean> | Refinement<boolean>[]
schema
?: ZodSchema<Date>
type
?: 'string'
;refine
?: Refinement<string> | Refinement<string>[]
minLength
?: Constraint<number>
maxLength
?: Constraint<number>
length
?: Constraint<number>
url
?: Constraint<boolean>
uuid
?: Constraint<boolean>
email
?: Constraint<boolean>
regex
?: Constraint<RegExp>
schema
?: ZodSchema<string>
type
?: 'object'
;refine
?: Refinement<T> | Refinement<T>[]
schema
?: ZodSchema<Ts>
type
?: 'array'
;refine
?: Refinement<T[]> | Refinement<T[]>[]
minLength
?: Constraint<number>
maxLength
?: Constraint<number>
length
?: Constraint<number>
items
?: ValidationOptions<T> | Refinement<T>
schema
?: ZodSchema<T[]>
Each arg on an object field, and each field on an input type with validation will build its own zod
validator. These validators will be a union of all potential types that can apply the validations
defined for that field. For example, if you define an optional field with a maxLength
validator,
it will create a zod schema that looks something like:
zod.union([zod.null(), zod.undefined(), zod.array().maxLength(5), zod.string().maxLength(5)]);
If you set and email
validation instead the schema might look like:
zod.union([zod.null(), zod.undefined(), zod.string().email()]);
At runtime, we don't know anything about the types being used by your schema, we can't infer the
expected js type from the type definition, so the best we can do is limit the valid types based on
what validations they support. The type
validation allows explicitly validating the type
of a
field to be one of the base types supported by zod:
// field
{
validate: {
type: 'string',
maxLength: 5
}
// generated
zod.union([zod.null(), zod.undefined(), zod.string().maxLength(5)]);
There are a few exceptions the above:
args and input fields that are InputObject
s always use zod.object()
rather than creating a
union of potential types.
args and input fields that are list types always use zod.array()
.
If you only include a refine
validation (or just pass a function directly to validate) we will
just use zod
s unknown validator instead:
// field
{
validate: (val) => isValid(val),
}
// generated
zod.union([zod.null(), zod.undefined(), zod.unknown().refine((val) => isValid(val))]);
If the validation options include a schema
that schema will be used as an intersection wit the
generated validator:
// field
{
validate: {
int: true,
schema: zod.number().max(10),
}
// generated
zod.union([zod.null(), zod.undefined(), zod.intersection(zod.number().max(10), zod.number().int())]);
The easiest way to share validators is the use the to define schemas for your fields in an external
file using the normal zod APIs, and then attaching those to your fields using the schema
option.
// shared
import { ValidationOptions } from '@pothos/plugin-validation';
const numberValidation = zod.number().max(5);
// server
builder.queryType({
fields: (t) => ({
example: t.boolean({
args: {
num: t.arg.int({
validate: {
schema: numberValidation,
}
}),
},
resolve: () => true,
}),
});
});
// client
numberValidator.parse(3) // pass
numberValidator.parse('3') // fail
You can also use the createZodSchema
helper from the plugin directly to create zod Schemas from an
options object:
// shared
import { ValidationOptions } from '@pothos/plugin-validation';
const numberValidation: ValidationOptions<number> = {
max: 5,
};
// server
builder.queryType({
fields: (t) => ({
example: t.boolean({
args: {
num: t.arg.int({
validate: numberValidation,
}),
},
resolve: () => true,
}),
});
});
// client
import { createZodSchema } from '@pothos/plugin-validation';
const validator = createZodSchema(numberValidator);
validator.parse(3) // pass
validator.parse('3') // fail
FAQs
A Pothos plugin for adding argument validation
We found that @pothos/plugin-validation demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.