JSON Schema $Ref Parser

repository·main·Indexed 22 days ago

https://github.com/apidevtools/json-schema-ref-parser

A tool for parsing, resolving, and dereferencing JSON Schema $ref pointers. It converts complex, multi-file, or remote-hosted schemas into navigable JavaScript objects, supporting circular references, mixed JSON/YAML formats, and custom parsers and resolvers.

Tokens
16.5K
Snippets
47
Records
63
Agent score
75%

What's inside @apidevtools/json-schema-ref-parser

  1. Use the $RefParser class

    main

    The $RefParser class is the default export of the library. You can use it in two ways:

    1. Instance methods: Create a new instance using new $RefParser() to access properties like schema and $refs which store the state of the last operation.
    2. Static methods: Call methods directly on the class (e.g., $RefParser.dereference()) if you do not need to maintain state.

    When using instance methods, the resulting schema or $refs object is stored in the parser.schema or parser.$refs properties respectively.

    let parser = new $RefParser();
    
    // Instance usage
    let schema = await parser.dereference("my-schema.json");
    console.log(parser.schema); // The dereferenced schema
    console.log(parser.$refs);  // The $Refs object
  2. How the $Refs class works

    main

    The $Refs class is a map of JSON References and their resolved values. It is provided to the callback function (or Promise) when you call the resolve method, and it is also accessible via the parser.$refs property on a $RefParser instance. It provides helper methods to navigate, inspect, and manipulate the JSON References within a schema.

    let parser = new $RefParser();
    // The $refs object is available on the parser instance after resolution
    await parser.dereference("my-schema.json");
    const refs = parser.$refs;
  3. Use static vs. instance methods in $RefParser

    main

    All methods in JSON Schema $Ref Parser are available as both static (class) methods and instance methods.

    • Static methods: Use these for quick, one-off operations. They create a new $RefParser instance internally, perform the operation, and return the result.
    • Instance methods: Use these when you need to access the state of the parser after the operation. By creating an instance with new $RefParser(), you can access the resulting parser.schema and parser.$refs properties at any time after the method completes.

    Example of the difference:

    // Static method (one-off)
    $RefParser.bundle("my-schema.json");
    
    // Instance method (retains state)
    let parser = new $RefParser();
    parser.bundle("my-schema.json");
    // You can now access parser.schema or parser.$refs
  4. Use Callbacks or Promises with $RefParser

    main

    JSON Schema $Ref Parser supports both Node.js-style error-first callbacks and Promises (async/await).

    • Callbacks: If you provide a callback function as the last argument to a method, it will be executed using the (err, result) convention.
    • Promises: If you do not provide a callback, the method returns a Promise, allowing for async/await or .then() syntax.

    Example of equivalent usage:

    // Callback syntax
    $RefParser.dereference(mySchema, (err, api) => {
      if (err) {
        // Error
      } else {
        // Success
      }
    });
    // async/await syntax
    try {
      let api = await $RefParser.dereference(mySchema);
      // Success
    } catch (err) {
      // Error
    }
  5. Configure parser execution order with `order`

    main

    The order property determines the sequence in which parsers are attempted.

    • If order is not specified, the parser runs last.
    • Lower numbers run earlier (e.g., order: 1 runs first).
    • You can use specific numbers to place your parser between built-in parsers (e.g., order: 201 runs after JSON/YAML but before plain-text/binary).

    Execution Logic:

    1. The engine checks canParse for all parsers.
    2. If only one parser matches, it is called immediately.
    3. If multiple parsers match, they are tried in order of their order property until one succeeds.
    4. If no parsers match via canParse, all parsers are tried in order until one succeeds or all fail.
  6. Handle circular $refs in JSON Schema

    main

    JSON Schema $Ref Parser supports circular $ref pointers. However, be aware that standard JSON.stringify() will fail when encountering circular structures.

    You have three ways to manage circular references:

    1. Default Behavior: Resolve and dereference them normally. Use a custom replacer function with JSON.stringify if you need to serialize the result.
    2. Disable/Error: Set the dereference.circular option to false. This will cause a ReferenceError if a circular reference is detected.
    3. Ignore: Set the dereference.circular option to "ignore". Non-circular references will be dereferenced, but circular ones will remain as $ref pointers in the schema.

    Alternative: Bundling Instead of dereference(), use the bundle() method. Bundling converts external $ref pointers to internal ones, which avoids creating circular references in the resulting object.

    Example of a circular reference structure:

    "person": {
        "properties": {
          "name": {
            "type": "string"
          },
          "spouse": {
            "type": {
              "$ref": "#/person"
            }
          }
        }
    }
  7. How JSON Schema $Ref Parser works

    main

    The library is a JSON Reference and JSON Pointer implementation designed to crawl complex JSON Schemas. It handles:

    • Mixed Formats: Uses both JSON and YAML schemas simultaneously.
    • Diverse Sources: Resolves $ref pointers to local files, remote URLs, and custom sources (like databases).
    • Complex References: Supports circular references, nested references, back-references, and cross-references between files.
    • Object Equality: Maintains object reference equality, meaning $ref pointers to the same value always resolve to the same object instance.
    • Bundling: Can bundle multiple files into a single schema containing only internal $ref pointers.
  8. Understand the File Info Object in plugins

    main

    When developing plugins for JSON Schema $Ref Parser (such as resolvers or parsers), the plugin methods canRead(), read(), canParse(), and parse() all receive a single parameter: a File Info Object.

    This object provides context about the file being processed. While the schema of this object may evolve, it currently provides the file's location, its original reference, the base URL used for resolution, the file extension, and the raw data.

  9. Implement custom resolvers

    main

    JSON Schema $Ref Parser allows you to add custom resolvers to support additional protocols or replace built-in resolvers (like HTTP/HTTPS or local filesystem). A resolver is an object passed to the resolve option in $RefParser methods (e.g., dereference).

    A resolver must implement the following properties/methods:

    • order (number, optional): Determines the execution priority. Lower numbers run earlier. If omitted, the resolver runs last. If multiple resolvers match a file via canRead, they are tried in ascending order until one succeeds.
    • canRead (RegExp | boolean | function, optional): Determines if the resolver is eligible for a specific file. If it's a function, it receives a file info object and a $refs object.
    • read(file, callback, $refs) (function, required): The core logic to fetch file contents. It must return the raw content (string or byte array). It supports synchronous returns, Node.js-style error-first callbacks, or ES6 Promises.
    let myResolver = {
      order: 1,
      canRead: /^mongodb:/i,
      read(file, callback, $refs) {
        MongoClient.connect(file.url, (err, db) => {
          if (err) {
            callback(err);
          }
          else {
            db.find({}).toArray((err, document) => {
              callback(null, document);
            });
          }
        });
      }
    };
    
    $RefParser.dereference(mySchema, { resolve: { mongo: myResolver }});
  10. Implement custom parsers

    main

    You can extend JSON Schema $Ref Parser by adding custom parsers to support new file types or by replacing built-in parsers (JSON, YAML, plain-text, and binary).

    A parser is an object containing three main properties: order, canParse, and parse.

    To register a custom parser, pass it within the parse option of a $RefParser method (like dereference or resolve). The key in the parse object should be a string representing the file extension (e.g., csv).

    let myParser = {
      order: 1,
      canParse: ".csv",
      parse(file) {
        let lines = file.data.toString().split("\n");
        return lines.map((line) => {
          return line.split(",");
        });
      },
    };
    
    $RefParser.dereference(mySchema, { parse: { csv: myParser } });
  11. Configure `$RefParser` methods with options

    main

    All $RefParser methods accept an optional options object to customize parsing, resolution, and dereferencing behavior. You do not need to specify every option; any omitted keys will use their default values.

    $RefParser.dereference("my-schema.yaml", {
      continueOnError: true,
      parse: {
        json: false,
        yaml: { allowEmpty: false },
        text: {
          canParse: [".txt", ".html"],
          encoding: "utf16",
        },
      },
      resolve: {
        file: false,
        http: { timeout: 2000, withCredentials: true },
      },
      dereference: {
        circular: false,
        excludedPathMatcher: (path) => path.includes("/example/"),
        onCircular: (path) => console.log(path),
        onDereference: (path, value) => console.log(path, value),
      },
    });