
Research
Malicious fezbox npm Package Steals Browser Passwords from Cookies via Innovative QR Code Steganographic Technique
A malicious package uses a QR code as steganography in an innovative technique.
@pothos/plugin-validation
Advanced tools
A plugin for adding validation to field arguments, input object fields, and input types using modern validation libraries like Zod, Valibot, and ArkType.
This plugin provides a library-agnostic approach to validation by supporting any validation library that implements the standard schema interface, making it flexible and future-proof.
To use the validation plugin, you'll need to install the validation plugin and a compatible validation library:
npm install --save @pothos/plugin-validation zod
# OR
npm install --save @pothos/plugin-validation valibot
# OR
npm install --save @pothos/plugin-validation arktype
import ValidationPlugin from '@pothos/plugin-validation';
import { z } from 'zod'; // or your preferred validation library
const builder = new SchemaBuilder({
plugins: [ValidationPlugin],
});
builder.queryType({
fields: (t) => ({
simple: t.boolean({
args: {
// Validate individual arguments
email: t.arg.string({
validate: z.string().email(),
}),
},
resolve: () => true,
}),
}),
});
The validation plugin supports validating inputs and arguments in several different ways:
t.arg.string({ validate: schema })
or t.arg.string().validate(schema)
- Validate individual argumentst.field({ args, validate: schema, ... })
or t.field({ args: t.validate(args), ... })
- Validate all arguments togetherbuilder.inputType({ validate: schema, ... })
or builder.inputType({ ... }).validate(schema)
- Validate entire input objectst.string({ validate: schema })
or t.string().validate(schema)
- Validate individual input type fieldsValidate each field argument independently using either the object syntax or chaining API:
builder.queryType({
fields: (t) => ({
user: t.string({
args: {
email: t.arg.string({
validate: z.string().email(),
}),
name: t.arg.string()
.validate(z.string().min(2).max(50)),
},
resolve: (_, args) => `User: ${args.name}`,
}),
}),
});
When using the chaining API, you can transform data as part of the validation process:
builder.queryType({
fields: (t) => ({
processData: t.string({
args: {
// Convert comma-separated string to array
tags: t.arg.string()
.validate(z.string().transform(str => str.split(',').map(s => s.trim()))),
},
resolve: (_, args) => {
return `Processed ${args.tags.length} tags`;
},
}),
}),
});
You can validate all arguments of a field together by passing a validation schema to the t.field
builder.queryType({
fields: (t) => ({
contact: t.boolean({
args: {
email: t.arg.string(),
phone: t.arg.string(),
},
// Ensure at least one contact method is provided
validate: z
.object({
email: z.string().optional(),
phone: z.string().optional(),
})
.refine(
(args) => !!args.phone || !!args.email,
{ message: 'Must provide either phone or email' }
),
resolve: () => true,
}),
}),
});
To transform all arguments together, you will need to use t.validate(args):
builder.queryType({
fields: (t) => ({
user: t.string({
args: t.validate({
email: t.arg.string(),
phone: t.arg.string(),
},
z.object({
email: z.string().optional(),
phone: z.string().optional(),
})
.refine(
(args) => !!args.phone || !!args.email,
{ message: 'Must provide either phone or email' }
)
.transform((args) => ({
filter: {
email: args.email ? args.email.toLowerCase() : undefined,
phone: args.phone ? args.phone.replace(/\D/g, '') : undefined,
},
}))
),
resolve: (_, args) => {
// args has transformed shape:
// { filter: { email?: string, phone?: string } }
return `User filter: ${JSON.stringify(args.filter)}`;
},
}),
}),
});
Validate entire input objects with complex validation logic using either object syntax or chaining:
// Object syntax
const UserInput = builder.inputType('UserInput', {
fields: (t) => ({
name: t.string(),
age: t.int(),
}),
validate: z
.object({
name: z.string(),
age: z.number(),
})
.refine((user) => user.name !== 'admin', {
message: 'Username "admin" is not allowed',
})
});
Transform entire input types:
const UserInput = builder.inputType('RawUserInput', {
fields: (t) => ({
fullName: t.string(),
birthYear: t.string(),
}),
}).validate(
z.object({
fullName: z.string(),
birthYear: z.string(),
}).transform(data => ({
firstName: data.fullName.split(' ')[0],
lastName: data.fullName.split(' ').slice(1).join(' '),
age: new Date().getFullYear() - parseInt(data.birthYear),
}))
);
builder.queryType({
fields: (t) => ({
createUser: t.string({
args: {
userData: t.arg({ type: UserInput }),
},
resolve: (_, args) => {
// args.userData has transformed shape:
// { firstName: string, lastName: string, age: number }
return `Created user: ${args.userData.firstName} ${args.userData.lastName}`;
},
}),
}),
});
Validate individual fields within input types:
const UserInput = builder.inputType('UserInput', {
fields: (t) => ({
name: t.string({
validate: z.string().min(2).refine(
(name) => name[0].toUpperCase() === name[0],
{ message: 'Name must be capitalized' }
),
})
}),
});
Transform field values during validation:
const UserInput = builder.inputType('UserInput', {
fields: (t) => ({
birthDate: t.string()
.validate(z.string().regex(/^\d{4}-\d{2}-\d{2}$/))
.validate(z.string().transform(str => new Date(str))),
}),
});
This plugin works with multiple validation libraries, giving you the flexibility to choose the one that best fits your needs:
The validationError
option allows you to customize how validation errors are handled and formatted. This is useful for:
const builder = new SchemaBuilder({
plugins: [ValidationPlugin],
validation: {
validationError: (validationResult, args, context) => {
// validationResult contains the standard-schema validation result
return new Error(`Validation failed: ${validationResult.issues.map(i => i.message).join(', ')}`);
},
},
});
Your error handler can return:
Understanding when and how validations are executed:
t.validate()
runs lastWhen there are multiple validations for the same field or type, they are executed in order, so that any transforms are applied before passing to the next schema. Validations for separate fields or arguments are executed in parallel, and their results are merged into a single set of issues.
FAQs
A Pothos plugin for adding schema based 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 1 open source maintainer 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
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.
Application Security
/Research
/Security News
Socket detected multiple compromised CrowdStrike npm packages, continuing the "Shai-Hulud" supply chain attack that has now impacted nearly 500 packages.