{"id":15013082,"url":"https://github.com/chnapy/tabbouleh","last_synced_at":"2026-01-20T01:17:23.861Z","repository":{"id":34193563,"uuid":"170194365","full_name":"Chnapy/tabbouleh","owner":"Chnapy","description":"JSON Schema generator from TypeScript classes, in runtime","archived":false,"fork":false,"pushed_at":"2023-01-06T02:05:53.000Z","size":1212,"stargazers_count":2,"open_issues_count":14,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-01T07:37:11.542Z","etag":null,"topics":["decorators","json","json-schemas","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Chnapy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-11T20:09:37.000Z","updated_at":"2020-06-15T09:11:57.000Z","dependencies_parsed_at":"2023-01-15T05:12:14.577Z","dependency_job_id":null,"html_url":"https://github.com/Chnapy/tabbouleh","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chnapy%2Ftabbouleh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chnapy%2Ftabbouleh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chnapy%2Ftabbouleh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chnapy%2Ftabbouleh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Chnapy","download_url":"https://codeload.github.com/Chnapy/tabbouleh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247378141,"owners_count":20929297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["decorators","json","json-schemas","typescript"],"created_at":"2024-09-24T19:43:42.985Z","updated_at":"2026-01-20T01:17:23.726Z","avatar_url":"https://github.com/Chnapy.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tabbouleh - ![License](https://img.shields.io/github/license/Chnapy/tabbouleh) [![NPM version](http://img.shields.io/npm/v/tabbouleh.svg?style=flat)](https://www.npmjs.org/package/tabbouleh) ![npm bundle size](https://img.shields.io/bundlephobia/min/tabbouleh) ![contribute](https://img.shields.io/badge/can%20I%20contribute-yes-brightgreen)\n\n\nA TypeScript library which generate JSON Schema (draft 7) from data class definition, in runtime.\n\n[![Travis](https://img.shields.io/travis/Chnapy/tabbouleh.svg)](https://travis-ci.org/Chnapy/tabbouleh)\n[![Coverage Status](https://coveralls.io/repos/github/Chnapy/tabbouleh/badge.svg?branch=master)](https://coveralls.io/github/Chnapy/tabbouleh?branch=master)\n[![Dev Dependencies](https://david-dm.org/Chnapy/tabbouleh/dev-status.svg)](https://david-dm.org/Chnapy/tabbouleh?type=dev)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Chnapy_tabbouleh\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=Chnapy_tabbouleh)\n[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Chnapy_tabbouleh\u0026metric=sqale_rating)](https://sonarcloud.io/dashboard?id=Chnapy_tabbouleh)\n\n  - **Class-based** - Structure your data definitions with classes, in which you put your JSON Schema properties. No need to create other types, classes or variables. \n\n  - **Decorators** - Define JSON Schema of data fields with decorators, for readability \u0026 understandability.\n\n  - **Field type inference** - Type of the field JSON Schema can be inferred from its TypeScript type.\n\n  - **Non-opinionated** - No link to any other libraries. Choose the validator you want, the form generator you want, they just have to work with JSON Schema format which is quite generic.\n\n  - **Back \u0026 Front** - Tabbouleh works in the same way with Node or in a Browser environment, it doesn't matter !\n---\n\n- [Install](#install)\n- [Get started](#get-started)\n  - [Define a data structure](#define-a-data-structure)\n  - [Generate its JSON Schema](#generate-its-json-schema)\n- [Dependencies](#dependencies)\n- [Motivation](#motivation)\n- [Note on draft used](#note-on-draft-used)\n- [Use cases](#use-cases)\n  - [Data validation](#data-validation)\n  - [Form generation](#form-generation)\n- [API](#api)\n  - [`@JSONSchema`](#jsonschema)\n  - [`@JSONProperty`](#jsonproperty)\n  - [`@JSONString`](#jsonstring)\n  - [`@JSONNumber` \u0026 `@JSONInteger`](#jsonnumber--jsoninteger)\n  - [`@JSONBoolean`](#jsonboolean)\n  - [`@JSONObject`](#jsonobject)\n  - [`@JSONArray`](#jsonarray)\n- [How referencing works](#how-referencing-works)\n  - [Wrap the class !](#wrap-the-class-)\n- [Examples](#examples)\n- [Credits](#credits)\n\n---\n\n## Install\n\n```bash\nnpm install tabbouleh --save\n```\n\nFor enable TypeScript decorators, your `tsconfig.json` needs the following flags:\n```json\n\"experimentalDecorators\": true,\n\"emitDecoratorMetadata\": true\n```\n\n## Get started\n\nLet's imagine a login case.\n\n### Define a data structure\n\nThe user will login with:\n\n  - its **email** - It must follow the email format.\n  - its **password** - It must have at least 6 chars.\n\nAll these fields are required.\n\n```typescript\nimport { JSONSchema, JSONString } from 'tabbouleh';\n\n@JSONSchema\u003cLoginData\u003e({\n  required: ['email', 'password']\n})\nexport class LoginData {\n\n  @JSONString({\n    format: 'email'\n  })\n  email: string;\n\n  @JSONString({\n    minLength: 6\n  })\n  password: string;\n\n}\n```\n\n### Generate its JSON Schema\n\nFrom our data structure, we generate its JSON Schema.\n\n```typescript\nimport Tabbouleh from 'tabbouleh';\nimport { JSONSchema7 } from 'json-schema';\n\nconst schema: JSONSchema7 = Tabbouleh.generateJSONSchema(LoginData);\n```\n\nAnd our schema looks like...\n\n```JSON\n{\n  \"type\": \"object\",\n  \"required\": [\n    \"email\",\n    \"password\"\n  ],\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\"\n    },\n    \"password\": {\n      \"type\": \"string\",\n      \"minLength\": 6\n    }\n  }\n}\n```\n\n## Dependencies\n\nTabbouleh has 2 dependencies:\n- `reflect-metadata`, for decorators.\n- `json-schema`, for schema types. Essentially `JSONSchema7` which is the type of schemas generated by Tabbouleh.\n\n## Motivation\n\nTo understand my motivation behind Tabbouleh we have to simulate an user data input process.\nLike a **login**.\n\nLet's list the steps:\n\n  - Define the data structure (like with a type or class). We have an username and a password.\n\n  - [front-end] Generate a form with an input for each of the data fields, which one need to have some rules (required, minLength, ...).\n\n  - [front-end] On form submit, validate the data, check that it follows all the rules.\n  \n  - [front-end] Then send the data to the back-end.\n  \n  - [back-end] On data receipt, validate the data, again (no trust with front).\n\n### The problem\n\nIf you ever developed this kind of process you may know the inconsistency of the binding between the data and each of these steps.\n\nWhen we create the form, there is no concrete link between inputs and the data structure. \nWe have to create an input for each data field, give it its rules in a HTML way.\n\nThen on form submit, the data must be generated from inputs values (from FormData object), and validated with the data rules, for each field.\n\nAgain, when the back-end has the data, it validates it with the same rules.\n\nThe definition of these rules and there validation may be programmatically done, in each of these steps with lot of redundancy, fat \u0026 ugly code, poor maintainability, and too much time.\n\n### My solution\n\nI wanted a way to define all these rules easily, elegantly, without a ton of code. When I define my data structure, I define its validation rules in the same place. And from my data structure, I get my related JSON Schema.\nIt's simple, it's how Tabbouleh works.\n\nThen I use the generated JSON Schema for generate my form, and validate the data submitted. Easily.\n\nThe JSON Schema format is normalized and handled by many data validators and form generators.\n\nBut careful, **Tabbouleh will not validate your data, or generate your form.** It'll just do the first step of these: generate the JSON Schema, which can be used for these purposes.\nCheck the [use cases](#use-cases) for more.\n\n## Note on draft used\n\nTabbouleh actually uses the **draft 7** of JSON Schema specification.\n\nFor more:\n\n  - http://json-schema.org/specification.html\n  - https://tools.ietf.org/html/draft-handrews-json-schema-validation-01\n\n## Use cases\n\n### Data validation\n\nYou can see the use of Tabbouleh with [AJV](https://github.com/epoberezkin/ajv) in this dedicated repo: [tabbouleh-sample-ajv](https://github.com/Chnapy/tabbouleh-sample-ajv).\n\n### Form generation\n\nYou can see the use of Tabbouleh with [react-jsonschema-form](https://github.com/mozilla-services/react-jsonschema-form) in this dedicated repo: [tabbouleh-sample-rjsf](https://github.com/Chnapy/tabbouleh-sample-rjsf).\n\nAn alternative of react-jsonschema-form: [uniforms](https://github.com/vazco/uniforms).\n\n## API\n\nSchema definitions are made in your data class, with decorators.\n\n### `@JSONSchema`\n\nThe only decorator for the class head. It defines the root schema properties.\n\n[More infos on which fields you can use.](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-10) [10]\n\n```typescript\n@JSONSchema\u003cLoginData\u003e({\n  $id: \"https://example.com/login.json\",\n  $schema: \"http://json-schema.org/draft-07/schema#\",\n  title: \"Login data\",\n  description: \"Data required form user login\",\n  required: ['email', 'password']\n})\nexport class LoginData {\n\n  @JSONString\n  email: string;\n\n  @JSONString\n  password: string;\n\n}\n```\n\n```typescript\n@JSONSchema\nexport class LoginData {\n\n  @JSONString\n  email: string;\n\n  @JSONString\n  password: string;\n\n}\n```\n\n### `@JSONProperty`\n\nField decorator which doesn't define the schema `type`. \nIf not defined it will be inferred from the field type.\n\nDepending on the `type` given, see the corresponding decorator to know which fields are allowed. \nAlso, [more infos on which fields you can use.](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1) [6.1]\n\n```typescript\n@JSONSchema\nexport class LoginData {\n\n  @JSONProperty\n  email: string;\n\n  @JSONProperty\u003cJSONEntityString\u003e({\n    type: 'string',\n    minLength: 6\n  })\n  password: string;\n\n}\n```\n\n### `@JSONString`\n\nField decorator for **string** type.\n\n[More infos on which fields you can use.](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.3) [6.3]\n\n```typescript\n@JSONSchema\nexport class LoginData {\n\n  @JSONString({\n    format: 'email',\n    maxLength: 64\n  })\n  email: string;\n\n  @JSONString\n  password: string;\n\n}\n```\n\n### `@JSONNumber` \u0026 `@JSONInteger`\n\nField decorators for **number** and **integer** types. They share the same fields.\n\n[More infos on which fields you can use.](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.2) [6.2]\n\n```typescript\n@JSONSchema\nexport class UserData {\n\n  @JSONInteger({\n    minimum: 0\n  })\n  age: number;\n\n  @JSONNumber\n  percentCompleted: number;\n\n}\n```\n\n### `@JSONBoolean`\n\nField decorator for **boolean** type.\n\n```typescript\n@JSONSchema\nexport class UserData {\n\n  @JSONBoolean\n  active: boolean;\n\n}\n```\n\n### `@JSONObject`\n\nField decorator for **object** type.\n\n[More infos on which fields you can use.](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.5) [6.5]\n\n```typescript\n@JSONSchema\nexport class UserData {\n\n  @JSONObject({\n    properties: {\n      street: {\n        type: 'string'\n      },\n      city: {\n        type: 'string'\n      }\n    }\n  })\n  address: object;\n\n}\n```\n\n#### Class reference\n\nWith this decorator you can reference an other schema class.\n\n[Wrap your class !](#wrap-the-class-)\n\n```typescript\n@JSONSchema\nexport class UserData {\n\n  @JSONObject(() =\u003e UserAddress)\n  address: UserAddress;\n\n}\n```\n\n```typescript\n@JSONSchema\nclass UserAddress {\n\n  @JSONString\n  street: string;\n\n  @JSONString\n  city: string;\n\n}\n```\n\n### `@JSONArray`\n\nField decorator for **array** type.\n\n[More infos on which fields you can use.](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.4) [6.4]\n\n```typescript\n@JSONSchema\nclass UserData {\n\n  @JSONArray({\n    items: {\n      type: 'integer'\n    }\n  })\n  childrenAges: number[];\n\n}\n```\n\n#### Class reference\n\nAs with `@JSONObject` you can reference an other schema class.\n\n```typescript\n@JSONSchema\nclass UserData {\n\n  @JSONArray(() =\u003e UserData)\n  children: UserData[];\n\n}\n```\n\nYou may want to add some properties in addition of the reference, for that put the reference in the `items` property.\n\n```typescript\n@JSONSchema\nclass UserData {\n\n  @JSONArray({\n    items: () =\u003e UserData,\n    maxItems: 4,\n    minItems: 0\n  })\n  children: UserData[];\n\n}\n```\n\n## How referencing works\n\nLet's take one of the previous example.\n\n```typescript\n@JSONSchema\nexport class UserData {\n\n  @JSONObject(() =\u003e UserAddress)\n  address: UserAddress;\n\n}\n```\n\n```typescript\n@JSONSchema\nclass UserAddress {\n\n  @JSONString\n  street: string;\n\n  @JSONString\n  city: string;\n\n}\n```\n\nThe JSON result will be:\n\n```JSON\n{\n  \"type\": \"object\",\n  \n  \"definitions\": {\n    \"_UserAddress_\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"street\": {\n          \"type\": \"string\"\n        },\n        \"city\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  },\n  \n  \"properties\": {\n    \n    \"address\": {\n      \"$ref\": \"#/definitions/_UserAddress_\"\n    }\n    \n  }\n}\n```\n\nYou can see that:\n  - `UserAdress` schema was put in the `definitions` of the root schema,\n  - `address` field is now referencing `UserAddress` by using the `$ref` field.\n  \nA class reference is **always** translated as a schema reference with the use of the `$ref`. \nBecause of multiple same references optimization, and because of circular reference handling.\n\n\n\n### Wrap the class !\n\nYou have to wrap the target in a function, like `() =\u003e MyClass`. \n\nIt is required because of the case of circular referencing which may cause an `undefined` value instead of the referenced class.\n\n## Examples\n\nYou can find all examples used in [/examples](examples).\n\n## Credits\n\nThis library was created with [typescript-library-starter](https://github.com/alexjoverm/typescript-library-starter).\n\n\u003c!--## Credits--\u003e\n\n\u003c!--Made with :heart: by [@alexjoverm](https://twitter.com/alexjoverm) and all these wonderful contributors ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):--\u003e\n\n\u003c!--\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!--\u003c!-- prettier-ignore --\u003e\n\u003c!--\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\n\u003c!--This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind are welcome!--\u003e\n\n### So, why tabbouleh ?\n\n[Hummus](https://www.npmjs.com/package/hummus) was already taken :shipit:\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchnapy%2Ftabbouleh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchnapy%2Ftabbouleh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchnapy%2Ftabbouleh/lists"}