Skip to content

Metamorphic, type-safe validation engine for TypeScript. Unified runtime, builder, decorator, and string rule support.

License

Notifications You must be signed in to change notification settings

jasgigli/gigli.js

Gigli.js Logo

Gigli.js

Metamorphic, type-safe validation engine for TypeScript & JavaScript

NPM Version NPM Downloads Bundle Size Build Status License Types PRs Welcome

Unified runtime, builder, decorator, and string rule support. Generate OpenAPI/JSON Schema. Use in Node.js, React, Express, NestJS, and more.


🚀 Why Gigli.js?

Gigli.js is not just another validator. It's a metamorphic engine that adapts to your coding style—builder, decorator, or string rules—without sacrificing type safety, power, or extensibility.

  • 🧩 Unified API: Mix & match builder, decorator, and string rules
  • 🦾 Type Inference: Full TypeScript support, everywhere
  • 🛠️ Extensible: Custom rules, transformers, and definitions
  • 🔍 Detailed Error Tracing: See exactly why validation failed
  • 🏗️ Schema Generation: OpenAPI & JSON Schema out of the box
  • Zero dependencies, works in Node.js, browsers, and modern runtimes

📦 Installation

npm install gigli.js

🏁 Quick Start

import { v } from 'gigli.js';

const UserSchema = v.object({
  username: v.string().min(3),
  email: v.string().email(),
});

const result = UserSchema.safeParse({ username: 'bob', email: 'bob@email.com' });
console.log(result.success); // true

🌱 Progressive Examples

1️⃣ Basic Validation

import { v } from 'gigli.js';

const UserSchema = v.object({
  username: v.string().min(3),
  email: v.string().email(),
});

UserSchema.parse({ username: 'ab', email: 'bad' }); // Throws with detailed error

2️⃣ Type Inference

type User = v.infer<typeof UserSchema>;
// User: { username: string; email: string }

3️⃣ Error Handling & Flattening

try {
  UserSchema.parse({ username: 'ab', email: 'bad' });
} catch (err) {
  console.log(err.flatten());
  /*
  {
    input: { username: 'ab', email: 'bad' },
    errors: [
      { path: ['username'], message: 'String must be at least 3 characters' },
      { path: ['email'], message: 'Invalid email address' }
    ]
  }
  */
}

4️⃣ Advanced Builder Features

const PostSchema = v.object({
  id: v.string().uuid(),
  title: v.string().min(5).max(100),
  tags: v.array(v.string().min(2)).optional(),
  author: UserSchema, // Schemas are composable!
  status: v.string().from('enum:values=draft|published|archived'),
  meta: v.union([
    v.object({ type: v.literal('text'), content: v.string() }),
    v.object({ type: v.literal('image'), url: v.string().url() })
  ])
});

5️⃣ Nested Objects, Arrays, Optionals, Enums

const BlogSchema = v.object({
  posts: v.array(PostSchema),
  owner: v.object({
    id: v.string().uuid(),
    name: v.string(),
    roles: v.array(v.string().from('enum:values=admin|editor|user')),
  }),
  settings: v.object({
    commentsEnabled: v.boolean().optional(),
    theme: v.string().default('light'),
  })
});

6️⃣ Decorator API (for OOP & NestJS fans)

import { v, ValidatedModel } from 'gigli.js';

@v.Refine((dto) => dto.password === dto.passwordConfirm, {
  message: "Passwords don't match",
  path: ['passwordConfirm'],
})
class CreateUserDto extends ValidatedModel {
  @v.Rule(v.string().email())
  email: string;

  @v.Rule('string:min=8,max=50')
  password: string;

  @v.Rule(v.string())
  passwordConfirm: string;
}

const userDto = CreateUserDto.from({
  email: 'foo@bar.com',
  password: 'secret123',
  passwordConfirm: 'secret123',
});

7️⃣ Pipeline API (for complex workflows)

const OrderPipeline = v.pipeline()
  .transform((data) => ({ ...data, orderId: data.id.toLowerCase() }))
  .validate(v.object({ orderId: v.string().min(1) }))
  .dispatch('paymentMethod', {
    'credit_card': v.object({ card: v.string().creditCard() }),
    'paypal': v.object({ email: v.string().email() }),
  })
  .refine((order) => order.total > 0, { message: 'Order total must be positive' })
  .effect({
    onSuccess: (data) => console.log('Order Validated', data.orderId),
    onFailure: (trace) => console.error('Order Failed', trace),
  });

const result = OrderPipeline.safeParse(orderData);

8️⃣ Custom Rules, Transformers, and Definitions

v.registerRule('isEven', (value) => typeof value === 'number' && value % 2 === 0);
v.registerTransformer('trim', (value) => typeof value === 'string' ? value.trim() : value);
v.define('slug', 'string:min=3|regex:^[a-z0-9-]+$');

const SlugSchema = v.string().from('slug').transform('trim');

🧑‍💻 CLI Usage

npx gigli codegen --schema ./src/schemas.ts --target openapi
npx gigli codegen --schema ./src/schemas.ts --target jsonschema
npx gigli analyze --schema ./src/schemas.ts
npx gigli --help

🏆 Feature Comparison

Feature Zod Yup class-validator Gigli.js
Type Inference
Chainable Schema Builder
Decorator API
Portable String Rules
Unified Runtime (Mix & Match)
Validation Pipelines & Dispatch
Detailed Error Tracing
Auto OpenAPI/JSON Schema Gen
Extensible (Rules/Transformers) ⚠️ ⚠️ ⚠️

🌍 Use It Everywhere

  • Node.js, Deno, Bun, Cloudflare Workers
  • React, Vue, Svelte, Solid
  • Express, NestJS, tRPC, REST, GraphQL
  • Works in browsers and modern runtimes

🔗 Documentation & Resources


🤝 Contributing

We are building the future of data validation, and we'd love your help! Please read our CONTRIBUTING.md to get started. Whether it's a bug report, a new feature, or a documentation improvement, all contributions are welcome!


🪪 License

Gigli.js is open-source software licensed under the MIT License.


🏷️ Keywords

validation, validator, typescript, schema, zod, yup, class-validator, openapi, jsonschema, decorators, cli, nodejs, react, express, nestjs, type-safe, builder, portable, runtime, inference, extensible, pipeline, unified, metamorphic

Usage

ESM (Node.js with "type": "module" or .mjs files)

import { v } from 'gigli.js';

const UserSchema = v.object({
  username: v.string().min(3),
  email: v.string().email(),
});

(async () => {
  const result = await UserSchema.safeParse({ username: 'bob', email: 'bob@email.com' });
  console.log('safeParse:', result); // { success: true, data: ..., error: null }
  try {
    const parsed = await UserSchema.parse({ username: 'bob', email: 'bob@email.com' });
    console.log('parse:', parsed); // { username: 'bob', email: 'bob@email.com' }
  } catch (err) {
    console.error('parse error:', err);
  }
})();

CommonJS (default Node.js or .js files)

const { v } = require('gigli.js');

const UserSchema = v.object({
  username: v.string().min(3),
  email: v.string().email(),
});

(async () => {
  const result = await UserSchema.safeParse({ username: 'bob', email: 'bob@email.com' });
  console.log('safeParse:', result); // { success: true, data: ..., error: null }
  try {
    const parsed = await UserSchema.parse({ username: 'bob', email: 'bob@email.com' });
    console.log('parse:', parsed); // { username: 'bob', email: 'bob@email.com' }
  } catch (err) {
    console.error('parse error:', err);
  }
})();

TypeScript

TypeScript types are included automatically. You can use the same import as ESM:

import { v } from 'gigli.js';
// ...rest of your code

API

  • schema.safeParse(data) — Returns { success, data, error }. Does not throw.
  • schema.parse(data) — Returns parsed data or throws on error.

Both methods are async and must be awaited.


Connect with the Author

About

Metamorphic, type-safe validation engine for TypeScript. Unified runtime, builder, decorator, and string rule support.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published