Hooks

useDocyrusDataSourceJsonSchema

Generate a JSON Schema (object schema, properties keyed by field slug) from a Docyrus data source field list.

Installation

pnpm dlx @docyrus/cli add @docyrus/hooks-use-docyrus-data-source-json-schema

This hook is distributed as source. It is fully standalone — it ships its own JsonSchema type and a Docyrus field-type → JSON Schema mapping, so it has no runtime dependency on the JSON Schema Designer or the form-fields system.

Overview

useDocyrusDataSourceJsonSchema turns an array of Docyrus field descriptors into a standard object JSON Schema (Draft 2020-12 / Draft-07 compatible). Each field becomes one property:

  • field.slug → the property key (machine name).
  • field.name → the property title (user-facing label).
  • field.type → the property type / format (via the built-in field-type map).
  • field.description → the property description.

Use it to drive AI Structured Outputs / tool definitions, runtime validation (Ajv, Zod-from-JSON-Schema, etc.), form generation, or any flow that needs a machine-readable contract for a data source.

The hook is a pure, memoized transform — no state, no effects, no fetching.

Usage

'use client';

import { useDocyrusDataSourceJsonSchema } from '@/hooks/use-docyrus-data-source-json-schema';

const fields = [
  { slug: 'full_name', name: 'Full Name', type: 'field-text', description: 'Contact full name' },
  { slug: 'email', name: 'Email', type: 'field-email' },
  { slug: 'status', name: 'Status', type: 'field-status', description: 'Pipeline stage' },
  { slug: 'tags', name: 'Tags', type: 'field-tagSelect' },
  { slug: 'revenue', name: 'Annual Revenue', type: 'field-money' },
];

export function ContactSchema() {
  const schema = useDocyrusDataSourceJsonSchema(fields, {
    title: 'Contact',
    description: 'A CRM contact record',
  });

  return <pre>{JSON.stringify(schema, null, 2)}</pre>;
}

Produces:

{
  "type": "object",
  "properties": {
    "full_name": { "type": "string", "title": "Full Name", "description": "Contact full name" },
    "email": { "type": "string", "format": "email", "title": "Email" },
    "status": { "type": "string", "title": "Status", "description": "Pipeline stage" },
    "tags": { "type": "array", "items": { "type": "string" }, "title": "Tags" },
    "revenue": { "type": "number", "title": "Annual Revenue" }
  },
  "additionalProperties": true,
  "title": "Contact",
  "description": "A CRM contact record"
}

OpenAI Structured Outputs (strict mode)

Set strict: true to constrain the output to OpenAI Structured Outputs rules — additionalProperties: false plus every field slug listed in required:

const schema = useDocyrusDataSourceJsonSchema(fields, { title: 'Contact', strict: true });
// schema.additionalProperties === false
// schema.required === ['full_name', 'email', 'status', 'tags', 'revenue']

Marking fields required

// every field required
useDocyrusDataSourceJsonSchema(fields, { required: true });

// only specific slugs required
useDocyrusDataSourceJsonSchema(fields, { required: ['full_name', 'email'] });

Refining individual fields with overrides

The built-in mapping uses pragmatic defaults (single-choice / reference fields → string, multi-choice → array of string, structured fields → open object). When a field needs a tighter shape, deep-merge an override keyed by slug:

const schema = useDocyrusDataSourceJsonSchema(fields, {
  overrides: {
    // narrow an enum/status field to a fixed set of option ids
    status: { enum: ['lead', 'qualified', 'won', 'lost'] },
    // describe the shape of a relation reference
    owner: { type: 'object', properties: { id: { type: 'string' }, name: { type: 'string' } } },
  },
});

Overrides are deep-merged onto the generated property (override wins; nested objects merge recursively), so title/description derived from the field metadata are preserved unless you replace them explicitly.

Outside React

Both buildDataSourceJsonSchema and fieldToJsonSchema are pure and can be called from server components, API routes, or AI tool-definition builders:

import { buildDataSourceJsonSchema } from '@/hooks/use-docyrus-data-source-json-schema';

const schema = buildDataSourceJsonSchema(fields, { strict: true });

API Reference

useDocyrusDataSourceJsonSchema(fields, options?)

ParameterTypeDescription
fieldsDataSourceFieldDescriptor[]Field list. Each entry: { slug, name, type, description? }.
optionsUseDocyrusDataSourceJsonSchemaOptionsGeneration options (see below).

Returns a memoized JsonSchema object. Memoization keys on fields and each option — pass a stable fields reference to avoid rebuilding on every render.

DataSourceFieldDescriptor

FieldTypeDescription
slugstringMachine name → JSON Schema property key. Fields without a slug are skipped.
namestringUser-facing label → property title.
typeIFieldTypeDocyrus field type (e.g. field-text, field-email, field-status). Drives type / format.
descriptionstring | nullOptional → property description.

UseDocyrusDataSourceJsonSchemaOptions

OptionTypeDefaultDescription
titlestring—title for the root object schema.
descriptionstring—description for the root object schema.
requiredboolean | string[]—true → all slugs required; string[] → listed slugs required; omitted → no required array.
strictbooleanfalseOpenAI Structured Outputs mode: forces additionalProperties: false + all slugs required. Overrides required and additionalProperties.
additionalPropertiesbooleantrueRoot-level additionalProperties. Ignored when strict.
includeTitlesbooleantrueEmit each property's title from field.name.
includeDescriptionsbooleantrueEmit each property's description from field.description.
includeDialectbooleanfalseInclude the $schema dialect keyword on the root.
dialectstringDEFAULT_JSON_SCHEMA_DIALECTDialect URI used when includeDialect is set.
overridesRecord<string, JsonSchema>—Per-slug schema overrides, deep-merged onto the generated property.

Exports

ExportTypePurpose
useDocyrusDataSourceJsonSchema(fields, options?) => JsonSchemaReact hook (memoized).
buildDataSourceJsonSchema(fields, options?) => JsonSchemaPure builder — usable outside React.
fieldToJsonSchema(field, options?) => JsonSchemaPure single-field → property converter.
FIELD_TYPE_SCHEMA_MAPPartial<Record<IFieldType, JsonSchema>>Field type → base schema fragment map.
DEFAULT_JSON_SCHEMA_DIALECTstringDefault dialect (https://json-schema.org/draft/2020-12/schema).
JsonSchemainterfaceSelf-contained JSON Schema type returned by the hook.
DataSourceFieldDescriptorinterfaceField descriptor input type.
UseDocyrusDataSourceJsonSchemaOptionsinterfaceOptions type.

Field type mapping

The defaults below are intentionally pragmatic. Refine any field with overrides.

JSON Schema outputDocyrus field types
stringfield-text, field-textarea, field-password, field-phone, field-color, field-icon, field-currency, field-display, field-htmlEditor, field-emailEditor, field-codeEditor, field-markdown, field-formula, field-code, field-identity, field-handlebars, field-fileStorageFolder, field-systemEnum
string + formatfield-email (email), field-url (uri), field-date (date), field-dateTime (date-time), field-time (time)
numberfield-number, field-money, field-percent, field-duration, field-rating
integerfield-autonumber
booleanfield-checkbox, field-switch, field-todo
string (id of choice / reference)field-enum, field-select, field-radioGroup, field-status, field-approvalStatus, field-userSelect, field-relation, field-locationSelect, field-button
array of stringfield-multiSelect, field-tagSelect, field-userMultiSelect, field-list, field-systemTextArray, field-systemUuidArray
array of objectfield-taskList, field-schemaRepeater
open objectfield-json, field-jsonSchema, field-jsonata, field-adaptiveCard, field-schema, field-dynamic, field-inlineData, field-inlineForm, field-conversationChannel, field-queryBuilder, field-systemBuffer, field-systemVector
stored-file objectfield-file, field-image, field-avatar
date-range object ({ from, to })field-dateRange
string (fallback)any unmapped / unknown type

How It Works

The hook reads FIELD_TYPE_SCHEMA_MAP for each field's base fragment, layers on title / description from the field metadata, deep-merges any per-slug overrides, and assembles the property bag under a root object schema. strict and required only affect the root additionalProperties / required keywords — they never mutate individual property shapes. The whole transform is pure and memoized against fields + options.

Out of scope

  • Fetching the field list — pass the data source's fields in yourself (e.g. from @docyrus/app-utils DataSourceField metadata).
  • Resolving enum options or relation targets into concrete schemas — supply those via overrides when you need them.
  • Validation — feed the returned schema to your validator of choice (Ajv, etc.).

On this page