1.0.0 โ€ข Published 5 months ago

@nayi/formulate v1.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

@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

OptionTypeDescription
convertBooleansbooleanConvert "true" / "false"
convertNumbersbooleanConvert "123" to 123
convertNullsbooleanConvert "null" / "undefined"
enableDateParsingbooleanParse ISO and short dates
enableJsonParsingbooleanConvert JSON strings to objects/arrays
treatEmptyStringAs"null" \| "undefined" \| "keep"How to handle empty strings
removeUndefinedFieldsbooleanIf 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
schemazod \| yup \| customSchema-level validator

๐Ÿ“ Directory Structure

src/
โ”œโ”€โ”€ InputNormalizer.ts       # Main class-based engine
โ”œโ”€โ”€ normalize.ts             # Functional API
โ”œโ”€โ”€ utils.ts                 # Helpers
โ”œโ”€โ”€ types.ts                 # Interfaces and types

Documentation


๐Ÿ“„ 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.