s-validator
v0.0.9
Published
[](https://www.npmjs.com/package/s-validator) [](https://opensource.org/licenses/MIT) [
Table of Contents
- Why s-validator?
- Core Concepts
- Getting Started
- Advanced Example: Recursive Schemas
- Documentation
- Contributing
- License
Why s-validator?
In a sea of validation libraries, s-validator stands out by focusing on a few key principles:
- Declarative, Configuration-Object API: Schemas are just plain JavaScript objects. This makes them easy to define, read, compose, and even serialize. No complex method chaining or builder patterns are needed.
- Powerful Three-Phase Pipeline: The unique
prepare -> validate -> transformpipeline provides a clear and robust mental model for handling data. It cleanly separates type coercion, validation, and output formatting, preventing cluttered and hard-to-maintain validation logic. - Async-First by Design: All validation is asynchronous from the ground up. This means you can easily add
asyncvalidation rules (like checking a database for a unique username) without any special workarounds. - Zero Dependencies & Lightweight:
s-validatoris built to be lean and easy to integrate into any project without bringing in a large dependency tree. - Fully Type-Safe: Leverage the full power of TypeScript for excellent autocompletion, type inference, and compile-time error checking.
Core Concepts
s-validator operates on a unique three-phase validation pipeline for every schema. When you call parse() or safeParse(), your data flows through these steps in order, giving you granular control over the entire process.
graph TD
A[Input Data] --> P["<b>1. Preparation</b><br>Coerce types<br>Trim strings<br>Apply defaults"];
P --> V["<b>2. Validation</b><br>Check lengths/ranges<br>Validate formats (email, URL)<br>Run custom rules"];
V --> T["<b>3. Transformation</b><br>Change data shape<br>Format values<br>Add or remove fields"];
T --> O[Output Data];
style P fill:#f9f9f9,stroke:#333,stroke-width:2px,color:#333
style V fill:#f9f9f9,stroke:#333,stroke-width:2px,color:#333
style T fill:#f9f9f9,stroke:#333,stroke-width:2px,color:#333Preparation: The raw input is recursively traversed, and preparation functions are run. This is ideal for coercing data into the correct type before validation, such as converting a
Datestring to aDateobject or trimming a string.Validation: The prepared data is recursively validated against the rules defined in your schema (e.g.,
minLength,min,email). If any validation fails, the process stops and returns an error.Transformation: After the data has been successfully validated, it is recursively transformed into its final output shape. This is useful for formatting data, such as converting a
Dateobject back to a formatted string or adding a computed property.
Getting Started
First, install s-validator in your project:
pnpm add s-validator
# or
npm install s-validator
# or
yarn add s-validatorNext, define a schema. Let's create one for a User that has a contact field, which can be either an email or a phone number.
import { s } from "s-validator";
// Define a schema with the new configuration-object syntax
const userSchema = s.object({
// Top-level configurations
transform: {
custom: [(user) => ({ ...user, name: user.name.toLowerCase() })],
},
// Validation logic is nested under the 'validate' key
validate: {
properties: {
name: s.string({
validate: { minLength: 2 },
}),
contact: s.union({
// The union validator can also have top-level configs
// for preparations, transformations, or custom error messages.
validate: {
of: [
s.string({ validate: { email: true } }),
s.string({ validate: { pattern: /^\d{10}$/ } }),
],
},
}),
createdAt: s.date({
prepare: { coerce: true }, // Coerce string/number to Date
optional: true, // This field can be omitted
}),
},
},
});
// Use `s.infer` to extract the TypeScript type
type User = s.infer<typeof userSchema>;
// type User = {
// name: string;
// contact: string;
// createdAt?: Date | undefined
// }
async function safeValidateUser(data: unknown) {
const result = await userSchema.safeParse(data);
if (result.status === "success") {
console.log("User is valid:", result.data);
return result.data;
}
// The error object contains detailed information about what went wrong
console.error("Validation failed:", result.error.issues);
// Example error:
// [
// {
// "path": [ "name" ],
// "message": "minLength must be greater than or equal to 2, but received 1"
// }
// ]
}
// Example Usages
safeValidateUser({
name: "John Doe",
contact: "[email protected]",
});
safeValidateUser({
name: "J", // Too short
contact: "not-a-valid-contact",
});Advanced Example: Recursive Schemas
s-validator supports recursive schemas out of the box using getters. This is useful for validating nested data structures like file system trees or comments with replies.
Here's how you can define a schema for a file system entry, where a directory can contain more entries:
import { s } from "s-validator";
interface File {
type: "file";
name: string;
size: number;
}
interface Directory {
type: "directory";
name: string;
children: (File | Directory)[];
}
type FileSystemEntry = File | Directory;
const fileSchema = s.object({
validate: {
properties: {
type: s.literal("file"),
name: s.string(),
size: s.number(),
},
},
});
const directorySchema: s.ObjectSchema<any, Directory> = s.object({
validate: {
properties: {
type: s.literal("directory"),
name: s.string(),
// Use a getter to reference the 'entrySchema' which is defined later
get children() {
return s.array({ validate: { of: entrySchema } });
},
},
},
});
const entrySchema = s.switch<FileSystemEntry>({
validate: {
on: "type",
of: {
file: fileSchema,
directory: directorySchema,
},
},
});
// Example of a valid tree
const fileSystemTree = {
type: "directory",
name: "root",
children: [
{ type: "file", name: "file1.txt", size: 100 },
{
type: "directory",
name: "subdir",
children: [{ type: "file", name: "file2.txt", size: 200 }],
},
],
};
async function validateTree() {
const result = await entrySchema.safeParse(fileSystemTree);
console.log(result.status); // 'success'
}
validateTree();Documentation
For a complete guide to all features and validators, check out the full documentation.
Contributing
Contributions are welcome! Please feel free to submit a pull request or open an issue.
License
This project is licensed under the MIT License.
