ajv-validator / ajv

The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927)
https://ajv.js.org
MIT License
13.48k stars 864 forks source link

How can I access `definitions` in schema-store using `$ref`? #2420

Closed imjuni closed 2 weeks ago

imjuni commented 1 month ago

Motivation

When developing a RESTful server using Express.js, Fastify.js, etc. in a Node environment, it uses json-schema to validate API request inputs. If you're using json-schema to validate request inputs, integrating multiple schemas can be more manageable as the number of APIs grows.

interface IPeople {
  firstName: string;
  lastName: string;
}

interface IStudent extends IPeople {
  major: string;
}

interface IProfessor extends IPeople {
  room: number;
}

The example above is in typescript, but it can also be expressed in json-schema.

{
  "$id": "IPeople",
  "properties": {
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    }
  },
}
{
  "$id": "IStudent",
  "properties": {
    "major": {
      "type": "string"
    },
    "$ref": "IPeople"
  },
}
{
  "$id": "IProfessor",
  "properties": {
    "room": {
      "type": "number"
    },
    "$ref": "IPeople"
  },
}

It's useful to have a structured schema like this.

Problem

I'm looking for an way to configure a schema registry per API in one json-schema store. For example, I'm looking for a way to access the contents inside definitions in two schema stores, https://json.schemastore.org/eslintrc.json and https://json.schemastore.org/partial-eslint-plugins.json.

import Ajv from 'ajv';

const ajv = new Ajv();

const [reply01, reply02] = await Promise.all([
  fetch('https://json.schemastore.org/eslintrc.json', { method: 'GET' }),
  fetch('https://json.schemastore.org/partial-eslint-plugins.json', {
    method: 'GET',
  }),
]);

const [resp01, resp02] = await Promise.all([reply01.json(), reply02.json()]);

ajv.addSchema(resp02);
ajv.addSchema(resp01);

const refs = [
  'https://json.schemastore.org/eslintrc.json/definitions/stringOrStringArray',
  'https://json.schemastore.org/eslintrc.json/#/definitions/stringOrStringArray',
  'https://json.schemastore.org/eslintrc.json#/definitions/stringOrStringArray',
  '#/definitions/stringOrStringArray',
  'https://json.schemastore.org/partial-eslint-plugins.json/definitions/ruleNumber',  
  'https://json.schemastore.org/partial-eslint-plugins.json#/definitions/ruleNumber',  
  'https://json.schemastore.org/partial-eslint-plugins.json/ruleNumber',  
]

const validators = refs.map((ref) => {
  try {
    const validator = ajv.compile({ $ref: ref, });
    return validator
  } catch (error) {
    console.log(error);
    return undefined;
  }
});

Expectation

How do I access the above eslintrc.json/definitions?

Environments

  1. Node v20.11.1
  2. AJV 8.12.0
  3. Reproducable Repo.
    1. https://github.com/imjuni/json-schema-ref-test.git
jasoniangreen commented 2 weeks ago

Hi @imjuni, after digging into the error messages when trying to load the refs (or actually I tested using ajv.getSchema as it was simpler) I found that the original schemas were not loading correctly because of missing formats that are being used in those schemas.

I fixed this by adding ajv-formats to the example.

import Ajv from 'ajv';
import addFormats from "ajv-formats";

const ajv = new Ajv();
addFormats(ajv);

From here you can see which of your ref syntax works properly.