fluent-json-schema

repository·main·Indexed 19 days ago

https://github.com/fastify/fluent-json-schema

A framework-agnostic, chainable API for generating JSON Schema (draft-07) objects in Node.js and browser environments. It provides a fluent interface to define properties, types, and validation rules via ObjectSchema, ArraySchema, StringSchema, and others, exporting the final plain JavaScript object using .valueOf() for use with validators like AJV.

Tokens
10.1K
Snippets
55
Records
58
Agent score
68%

What's inside fluent-json-schema

  1. How logical composition works in fluent-json-schema

    main

    The library allows building complex validation logic using standard JSON Schema logical operators. These methods return a BaseSchema and accept arrays of other schemas:

    • anyOf(schemas): Logical OR. Valid if any schema matches.
    • allOf(schemas): Logical AND. Valid if all schemas match.
    • oneOf(schemas): Logical XOR. Valid if exactly one schema matches.
    • not(schema): Logical NOT. Valid if the instance fails the provided schema.
    • ifThenElse(if, then, else): Conditional logic. If the if schema matches, the instance must then match the then schema; otherwise, it must match the else schema.
    S.anyOf([S.string(), S.number()])
  2. Validate schemas using AJV

    main

    Note that fluent-json-schema generates the schema but does not perform validation itself. To validate data against your generated schema, use a library like AJV.

    1. Install AJV: npm i ajv
    2. Compile the schema using ajv.compile(schema.valueOf()).
    3. Use the returned validator function to check your data.
    const ajv = new Ajv({ allErrors: true })
    const validate = ajv.compile(schema.valueOf())
    let user = {}
    let valid = validate(user)
    console.log({ valid })
  3. Importing fluent-json-schema in TypeScript

    main

    Depending on your tsconfig.json configuration, use one of the following import patterns:

    With "esModuleInterop": true:

    import S from 'fluent-json-schema'

    With "esModuleInterop": false:

    import * as S from 'fluent-json-schema'

    Native ESM (Named Export):

    import { S } from 'fluent-json-schema'
    // ESM
    import { S } from 'fluent-json-schema'
    
    const schema = S.object()
      .prop('foo', S.string())
      .prop('bar', S.number())
      .valueOf()
  4. Basic usage of fluent-json-schema

    main

    Use the fluent API to build JSON Schema (draft-07) objects. You can define properties, types, formats, and requirements using a chainable interface. Use .valueOf() to export the final plain JavaScript object representing the JSON schema.

    const S = require('fluent-json-schema')
    
    const ROLES = {
      ADMIN: 'ADMIN',
      USER: 'USER',
    }
    
    const schema = S.object()
      .id('http://foo/user')
      .title('My First Fluent JSON Schema')
      .description('A simple user')
      .prop('email', S.string().format(S.FORMATS.EMAIL).required())
      .prop('password', S.string().minLength(8).required())
      .prop('role', S.string().enum(Object.values(ROLES)).default(ROLES.USER))
      .prop(
        'birthday',
        S.raw({ type: 'string', format: 'date', formatMaximum: '2020-01-01' }) // formatMaximum is an AJV custom keywords
      )
      .definition(
        'address',
        S.object()
          .id('#address')
          .prop('line1', S.anyOf([S.string(), S.null()])) // JSON Schema nullable
          .prop('line2', S.string().raw({ nullable: true })) // Open API / Swagger  nullable
          .prop('country', S.string())
          .prop('city', S.string())
          .prop('zipcode', S.string())
          .required(['line1', 'country', 'city', 'zipcode'])
      )
      .prop('address', S.ref('#address'))
    
    console.log(JSON.stringify(schema.valueOf(), undefined, 2))
  5. Configure Array validation with ArraySchema

    main

    Use ArraySchema([options]) to define a schema for arrays.

    Options:

    • options.schema: A StringSchema to serve as the default schema for array elements.
    • options.generateIds: A boolean (defaults to false). If true, it automatically generates IDs (e.g., #properties.foo).
    // Example usage of ArraySchema
    const schema = ArraySchema({ 
      schema: S.string(), 
      generateIds: true 
    });
  6. Configure ObjectSchema properties and constraints

    main

    Use these methods to define and constrain object schemas:

    • id(id): Defines a URI for the schema. Note: Calling id on an ObjectSchema sets the ID on the root of the object rather than in its properties.
    • additionalProperties(value): Determines how child instances validate for properties not matching properties or patternProperties. value can be a boolean or a FluentSchema.
    • maxProperties(max): Limits the maximum number of properties allowed in the object.
    • minProperties(min): Sets the minimum number of properties required in the object.
    object().minProperties(1).additionalProperties(false)
  7. Use array validation keywords: items, additionalItems, contains, and uniqueItems

    main

    These keywords control how array elements are validated:

    • items(items): Determines how child instances validate. If items is a schema, all elements must validate against it. If items is an array of schemas, each element validates against the schema at the corresponding position.
    • additionalItems(items): Determines how elements beyond those covered by items are validated. items can be a FluentSchema or a boolean.
    • contains(value): Validates that at least one element in the array matches the provided FluentSchema.
    • uniqueItems(boolean): If true, ensures all elements in the array are unique. Defaults to false if omitted.
    // Example: An array of strings where all items are unique
    const schema = ArraySchema().items(S.string()).uniqueItems(true);
    
    // Example: An array that must contain at least one number
    const schema = ArraySchema().contains(S.number());
  8. Configure BaseSchema metadata and identifiers

    main

    Use BaseSchema and its associated methods to define metadata for your schemas:

    • BaseSchema([options]): Creates a base schema. Options include schema (default schema) and generateIds (boolean).
    • id(id): Defines a URI for the schema and a base URI for resolving references.
    • title(title): Adds a short title for UI decoration.
    • description(description): Adds an explanation of the schema's purpose.
    • examples(examples): An array of strings used to provide usage examples.
    • ref(ref): A reference to another schema using a valid ID (e.g., #properties/foo).
    const schema = BaseSchema()
      .id('https://example.com/schema.json')
      .title('User Profile')
      .description('A schema representing a user')
      .examples(['{"name": "John"}']);
  9. Configure String validation with StringSchema

    main

    Use StringSchema to define validation rules for string instances. Available keywords include:

    • minLength(min) / maxLength(max): Constraints on the number of characters.
    • format(format): Validates the string against specific formats like email, hostname, ipv4, ipv6, uri, date, date-time, etc.
    • pattern(pattern): Validates the string against an ECMA 262 regular expression.
    • contentEncoding(encoding): Defines how the string should be interpreted as binary data (e.g., via RFC 2045).
    • contentMediaType(mediaType): Defines the media type of the instances (RFC 2046).
    S.string().minLength(3).maxLength(10).format('email')
  10. Select or remove properties from a schema

    main

    You can create smaller, reusable schemas from a larger Fluent Schema using .only() or .without().

    • .only(['prop1', 'prop2']): Returns a new schema containing only the specified properties.
    • .without(['prop1', 'prop2']): Returns a new schema excluding the specified properties.
    const S = require('fluent-json-schema')
    const userSchema = S.object()
      .prop('username', S.string())
      .prop('password', S.string())
      .prop('id', S.string().format('uuid'))
    
    // Keep only username and password
    const loginSchema = userSchema.only(['username', 'password'])
    
    // Remove id
    const bodySchema = userSchema.without(['id'])