1.0.0 โข Published 5 months ago
@nayi/formulate v1.0.0
@nayi/formulate
formulate is a framework-agnostic, TypeScript-first input normalization and validation library.
It helps you convert raw user inputs (usually strings from req.query, req.body, or forms) into well-typed, validated, and sanitized JavaScript objects.
โจ Features
- ๐ Converts strings into
boolean,number,Date,null, etc. - ๐ฆ Supports nested objects and arrays
- ๐งฉ Custom field transformers and type parsers
- ๐ฆ Built-in validation with error collection or strict mode
- ๐ก๏ธ Schema fallback and default value injection
- ๐ Whitelist/blacklist support
- ๐ก Dual API: OOP and functional
- โ๏ธ Ready for Express, React, Next.js, Vue, NestJS, and any JS/TS stack
๐ฆ Installation
npm install @nayi/formulate zod yup๐ง Usage
๐น Functional API
import { normalize } from '@nayi/formulate';
const { result, errors } = normalize(req.query, {
convertBooleans: true,
convertNumbers: true,
validationMode: 'collect',
validators: {
age: (val) => val >= 18,
},
});๐น OOP API
import { InputNormalizer } from '@nayi/formulate';
const normalizer = new InputNormalizer({
defaultValues: { role: 'user' },
fieldTransformers: {
email: (val) => val.trim().toLowerCase(),
},
});
const { result } = normalizer.normalize({
email: ' JOHN@EXAMPLE.COM ',
});๐น Express Middleware
import express from 'express';
import { createNormalizerMiddleware } from '@nayi/formulate';
const app = express();
app.use(express.json());
app.post(
'/submit',
createNormalizerMiddleware({
source: 'body',
options: {
validators: {
password: (val) => val.length >= 8,
},
validationMode: 'collect',
},
}),
(req, res) => {
if (req.normalized?.errors) {
return res.status(400).json({ errors: req.normalized.errors });
}
res.send(req.normalized.result);
}
);๐งช Schema Validation Support
โ Zod
import { z } from 'zod';
const schema = z.object({
username: z.string().min(3),
age: z.number().min(18),
});
const { result, errors } = normalize(input, {
schema: {
type: 'zod',
validator: schema,
},
validationMode: 'collect',
});โ Yup
import * as yup from 'yup';
const schema = yup.object().shape({
email: yup.string().email().required(),
});
const { result, errors } = normalize(input, {
schema: {
type: 'yup',
validator: schema,
},
validationMode: 'collect',
});โ Custom Schema
const customSchema = {
validate: (input) => {
const errors = {};
if (input.role !== 'admin') errors.role = 'Must be admin';
return { valid: Object.keys(errors).length === 0, errors };
},
};
const { result, errors } = normalize(input, {
schema: {
type: 'custom',
validator: customSchema,
},
validationMode: 'collect',
});โ๏ธ Options
| Option | Type | Description |
|---|---|---|
convertBooleans | boolean | Convert "true" / "false" |
convertNumbers | boolean | Convert "123" to 123 |
convertNulls | boolean | Convert "null" / "undefined" |
enableDateParsing | boolean | Parse ISO and short dates |
enableJsonParsing | boolean | Convert JSON strings to objects/arrays |
treatEmptyStringAs | "null" \| "undefined" \| "keep" | How to handle empty strings |
removeUndefinedFields | boolean | If true, removes undefined keys |
fieldTransformers | { [key]: (val) => any } | Field-specific mutation |
fieldParsers | { type: (val) => any } | Per-type custom parser |
validators | { [key]: (val) => boolean } | Field validation logic |
defaultValues | { [key]: any } | Fallback values if null/undefined |
schemaFallbacks | { [key]: (val) => any } | Apply fallback if schema fails |
validationMode | "none" \| "strict" \| "collect" | Error behavior |
schema | zod \| yup \| custom | Schema-level validator |
๐ Directory Structure
src/
โโโ InputNormalizer.ts # Main class-based engine
โโโ normalize.ts # Functional API
โโโ utils.ts # Helpers
โโโ types.ts # Interfaces and typesDocumentation
- ๐ค Localization Guide
- ๐ Plugin System
- ๐งช Schema Validation
- ๐ Express Integration
- ๐ง React Integration
- ๐ Full Docs Index
๐ License
MIT ยฉ 2025 Patrick NAYITURIKI Pull requests are welcome!
๐ค Contribution
See CONTRIBUTING.md for how to propose changes and collaborate.
๐ฆ Releases & Changelog
See CHANGELOG.md for a full history of updates.
Feel free to submit bug reports, PRs, or feature ideas.
1.0.0
5 months ago