JSON Field - jsonSchema unable to generate types nor parse $ref correctly - uses payload-types.ts as target

Open
#11,383 6 comments 1 reaction 1 assignee View on GitHub

@kendelljoseph is already working on this.

Since Feb 27, 2025.

Assessment

This issue has not been assessed yet.

Description

Describe the Bug

I have this jsonSchema that uses $ref (recursively) to validate a JSON field

JSON Schema
// src/collections/Posts/index.ts
import type { JSONField } from 'payload';

type JSONSchema = Exclude<JSONField['jsonSchema'], undefined>['schema'];
const filterSchema: JSONSchema = {
  $schema: 'http://json-schema.org/draft-07/schema#',
  type: 'object',
  definitions: {
    condition: {
      type: 'object',
      properties: {
        _int_eq: {
          type: 'object',
          additionalProperties: { type: 'integer' },
        },
        _str_in: {
          type: 'object',
          additionalProperties: {
            type: 'array',
            items: { type: 'string' },
          },
        },
        _str_not_in: {
          type: 'object',
          additionalProperties: {
            type: 'array',
            items: { type: 'string' },
          },
        },
        _str_eq: {
          type: 'object',
          additionalProperties: { type: 'string' },
        },
        _bool_eq: {
          type: 'object',
          additionalProperties: { type: 'boolean' },
        },
        _str_not_eq: {
          type: 'object',
          additionalProperties: { type: 'string' },
        },
        _int_in_range_exc: {
          type: 'object',
          additionalProperties: {
            type: 'object',
            properties: {
              min: { type: 'integer' },
              max: { type: 'integer' },
            },
            anyOf: [{ required: ['min'] }, { required: ['max'] }],
            additionalProperties: false,
          },
        },
        _array_contains: {
          type: 'object',
          additionalProperties: {
            anyOf: [
              { type: 'string' },
              { type: 'number' },
              { type: 'integer' },
              { type: 'boolean' },
            ],
          },
        },
        _array_not_contains: {
          type: 'object',
          additionalProperties: {
            anyOf: [
              { type: 'string' },
              { type: 'number' },
              { type: 'integer' },
              { type: 'boolean' },
            ],
          },
        },
        _int_in_range_inc: {
          type: 'object',
          additionalProperties: {
            type: 'object',
            properties: {
              min: { type: 'integer' },
              max: { type: 'integer' },
            },
            anyOf: [{ required: ['min'] }, { required: ['max'] }],
            additionalProperties: false,
          },
        },
        _is_null: {
          type: 'object',
          additionalProperties: { type: 'boolean' },
        },
        _is_not_null: {
          type: 'object',
          additionalProperties: { type: 'boolean' },
        },
        _days_from_now: {
          type: 'object',
          additionalProperties: { type: 'integer', minimum: 0 },
        },
        _or: {
          type: 'array',
          items: { $ref: '#/definitions/condition' },
        },
        _and: {
          type: 'array',
          items: { $ref: '#/definitions/condition' },
        },
      },
      additionalProperties: false,
    },
  },
  properties: {
    _or: {
      type: 'array',
      items: { $ref: '#/definitions/condition' },
    },
    _and: {
      type: 'array',
      items: { $ref: '#/definitions/condition' },
    },
  },
  additionalProperties: false,
};

export const Posts: CollectionConfig<'posts'> = {
  // ...snipped
  fields: [
    {
      name: 'criteria_json',
      type: 'json',
      jsonSchema: {
        uri: 'dummy://foo.json',
        fileMatch: ['dummy://foo.json'],
        schema: filterSchema,
      },
    },
  ]
}

And during editing, the browser is able to autosuggest and validate the JSON field correctly:

Image


But when I run pnpm generate:types, I get this error:

generate:types Error
  $ pnpm generate:types
  
  > payload-website@1.0.0 generate:types /workspaces/my-monorepo-project/javascript/apps/payload-website
  > cross-env NODE_OPTIONS=--no-deprecation payload generate:types
  
  [05:47:24] INFO: Compiling TS types for Collections and Globals...
  node:internal/process/promises:391
      triggerUncaughtException(err, true /* fromPromise */);
      ^
  
  JSONParserError: Missing $ref pointer "#/definitions/condition". Token "condition" does not exist.
      at Pointer.resolve (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/pointer.js:118:23)
      at $Ref.resolve (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/ref.js:81:28)
      at $Refs._resolve (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/refs.js:168:21)
      at dereference$Ref (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:191:27)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:105:40)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44) {
    code: 'EMISSINGPOINTER',
    name: 'MissingPointerError',
    source: '/workspaces/my-monorepo-project/javascript/apps/payload-website/',
    path: null,
    toJSON: [Function: toJSON],
    targetToken: 'condition',
    targetRef: '#/definitions/condition',
    targetFound: '#/definitions',
    parentPath: '#/definitions/posts/properties/criteria_json/definitions/condition/properties/_or/items',
    [Symbol(nodejs.util.inspect.custom)]: [Function: inspect]

This worked before in Payload V2 and I did not have to explicitly specify the JSONSchema type as well.


If i change the $ref

from:

  $ref: '#/definitions/condition'

to:

  $ref: '#/definitions/posts/properties/criteria_json/definitions/condition'

then it's able to generate the types:

JSON field with schema - generated types
  // payload-types.ts
  export interface Post {
     // ...snip
    criteria_json?: {
      _or?: Condition[];
      _and?: Condition[];
    };
  }

  export interface Condition {
    _int_eq?: {
      [k: string]: number;
    };
    _str_in?: {
      [k: string]: string[];
    };
    _str_not_in?: {
      [k: string]: string[];
    };
    _str_eq?: {
      [k: string]: string;
    };
    // ...snipped...
  }

But in the browser, when editing the JSON field, it does not suggest / validate based on the schema:

Image
Image


Troubleshooting this, it seems that the relative path being used for the $ref is the payload-types.ts? If I specify an external schema file as the jsonSchema.uri, then it seems to work in both the generation of types and in the browser field editor. But I don't want to host the external JSON schema file separately.


Expected behavior: should be able to generate types with JSON field with JSON schema using $ref

Link to the code that reproduces this issue

https://github.com/payloadcms/payload/blob/main/packages/payload/src/bin/generateTypes.ts#L32

Reproduction Steps
  1. Create new project pnpx create-payload-app@latest -t website
  2. Add JSON field to src/collections/Posts/index.tsx
  3. Specify a local jsonSchema using $ref (same as in the ticket description)
  4. Run pnpm generate:types
  5. [BUG] Unable to generate the types, and console will show error.
Which area(s) are affected? (Select all that apply)

area: core

Environment Info
Binaries:
  Node: 20.18.2
  npm: N/A
  Yarn: N/A
  pnpm: 9.14.2
Relevant Packages:
  payload: 3.23.0
Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP Tue Jan 21 10:23:32 UTC 2025
  Available memory (MB): 27015
  Available CPU cores: 11
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from payloadcms/payload

All issues in payloadcms/payload

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.