Core API

Table of contents

  1. SchemaNode
    1. .toObject()
    2. .toJsonLd()
    3. .toScript()
    4. .toString()
    5. .validate()
    6. .safeParse()
  2. makeFactory
  3. SchemaGraph / createGraph
    1. createGraph(nodes)
    2. .add(node)
    3. .toArray()
    4. .toJsonLd()
    5. Cross-referencing with @id
  4. SchemaIds
    1. SchemaId type
    2. Well-known IDs
    3. .custom(fragment)
    4. .forPath(path, fragment)
    5. .ref(fragment)
  5. schema() β€” Unified Factory
  6. extendThing

SchemaNode

Every factory function returns a SchemaNode<T>. It wraps your validated data and provides output methods.

class SchemaNode<T extends ZodRawShape> {
  toObject(): z.infer<ZodObject<T>>
  toJsonLd(): Record<string, unknown>
  toScript(): string
  toString(): string
  validate(): this
  safeParse(): SafeParseReturnType
}

.toObject()

Returns the validated data as a plain object without @context. Use this when nesting one schema inside another.

const person = createPerson({ name: 'Alice' });
person.toObject();
// { "@type": "Person", "name": "Alice" }

.toJsonLd()

Returns the data as a plain object with "@context": "https://schema.org" added. Ready to pass to JSON.stringify().

person.toJsonLd();
// { "@context": "https://schema.org", "@type": "Person", "name": "Alice" }

.toScript()

Returns a complete <script type="application/ld+json"> HTML string. Inject directly into <head>.

person.toScript();
// <script type="application/ld+json">
// { "@context": "https://schema.org", "@type": "Person", ... }
// </script>

.toString()

Returns prettified JSON string with @context included. Equivalent to JSON.stringify(this.toJsonLd(), null, 2).

.validate()

Re-runs Zod parsing and throws a ZodError if invalid. Returns this if valid, so it’s chainable:

const product = createProduct({ name: 'Widget', ... }).validate();

Use this in CI/build pipelines to catch schema issues before deployment.

.safeParse()

Validates without throwing. Returns { success: true, data } or { success: false, error: ZodError }.

const result = product.safeParse();
if (!result.success) {
  result.error.issues.forEach(i => console.error(i.message));
}

makeFactory

function makeFactory<T extends ZodRawShape>(
  schema: ZodObject<T>
): (data: Omit<z.input<ZodObject<T>>, '@context'>) => SchemaNode<T>

Internal factory builder used by all type modules. You can use it to create custom schema types:

import { z } from 'zod';
import { makeFactory, ThingSchema } from 'schemaorg-kit';

const CustomSchema = ThingSchema.extend({
  '@type': z.literal('CustomType').default('CustomType'),
  myField: z.string(),
});

export const createCustom = makeFactory(CustomSchema);

const node = createCustom({ myField: 'hello' });
node.toScript(); // "@type": "CustomType"

Key behavior: @type fields with .default() are optional in the input β€” the default is applied automatically. You can still override @type when the schema uses a union (e.g., createLocalBusiness({ '@type': 'HairSalon', ... })).


SchemaGraph / createGraph

SchemaGraph holds multiple schema nodes and serializes them as a JSON-LD @graph.

function createGraph(
  nodes: Array<SchemaNode<any> | Record<string, unknown>>
): SchemaGraph
class SchemaGraph {
  add(node: SchemaNode<any> | Record<string, unknown>): this
  toArray(): Record<string, unknown>[]
  toJsonLd(): { '@context': 'https://schema.org'; '@graph': unknown[] }
  toScript(): string
  toString(): string
}

createGraph(nodes)

Creates a SchemaGraph from an array of nodes. Accepts both SchemaNode instances and plain objects.

import { createGraph, createOrganization, createWebPage } from 'schemaorg-kit';

const graph = createGraph([
  createOrganization({ name: 'Acme', url: 'https://acme.com' }),
  createWebPage({ name: 'Home', url: 'https://acme.com' }),
]);

graph.toScript();
// <script type="application/ld+json">
// { "@context": "https://schema.org", "@graph": [...] }
// </script>

.add(node)

Fluent API to append a node after construction:

const graph = createGraph([org]);
graph.add(page).add(breadcrumb);

.toArray()

Returns the array of plain objects (without @context):

graph.toArray();
// [{ "@type": "Organization", ... }, { "@type": "WebPage", ... }]

.toJsonLd()

Returns { "@context": "https://schema.org", "@graph": [...] }.

Cross-referencing with @id

Use @id fields to cross-reference nodes within the same graph. The SchemaIds helper (see below) makes this easy:

import { SchemaIds, createGraph, createOrganization, createWebPage } from 'schemaorg-kit';

const ids = new SchemaIds('https://acme.com');

const org = createOrganization({
  '@id': ids.organization(),
  name: 'Acme',
});

const page = createWebPage({
  name: 'Home',
  url: 'https://acme.com',
  publisher: ids.ref('organization'), // { "@id": "https://acme.com/#organization" }
});

createGraph([org, page]).toScript();

SchemaIds

Centralized ID generator for JSON-LD @id values. Avoids typos and keeps cross-references consistent.

import { SchemaIds } from 'schemaorg-kit';

const ids = new SchemaIds('https://example.com');

SchemaId type

A branded string type for type-safe @id values. Assignable to string (works with all @id fields), but prevents accidental use of arbitrary strings.

Well-known IDs

Each method returns a SchemaId with the format {origin}/#{fragment}:

ids.organization()        // "https://example.com/#organization"
ids.localBusiness()       // "https://example.com/#localbusiness"
ids.person()              // "https://example.com/#person"
ids.product()             // "https://example.com/#product"
ids.event()               // "https://example.com/#event"
ids.place()               // "https://example.com/#place"
ids.movie()               // "https://example.com/#movie"
ids.website()             // "https://example.com/#website"
ids.webpage()             // "https://example.com/#webpage"
ids.article()             // "https://example.com/#article"
ids.breadcrumb()          // "https://example.com/#breadcrumb"
ids.dataset()             // "https://example.com/#dataset"
ids.recipe()              // "https://example.com/#recipe"
ids.course()              // "https://example.com/#course"
ids.softwareApplication() // "https://example.com/#softwareapplication"
ids.faqPage()             // "https://example.com/#faqpage"
ids.jobPosting()          // "https://example.com/#jobposting"
ids.vacationRental()      // "https://example.com/#vacationrental"
ids.profilePage()         // "https://example.com/#profilepage"

.custom(fragment)

Generate an ID with any custom fragment:

ids.custom('logo')          // "https://example.com/#logo"
ids.custom('contactpoint')  // "https://example.com/#contactpoint"

.forPath(path, fragment)

Generate a page-scoped ID:

ids.forPath('/about', 'webpage')   // "https://example.com/about#webpage"
ids.forPath('/blog/post', 'article') // "https://example.com/blog/post#article"

.ref(fragment)

Returns a { "@id": "..." } reference object for cross-referencing entities in @graph. Use with fields like publisher, organizer, author:

createWebSite({
  '@id': ids.website(),
  publisher: ids.ref('organization'),  // { "@id": "https://example.com/#organization" }
});

schema() β€” Unified Factory

function schema<T extends SchemaType>(
  type: T,
  data: Omit<z.infer<(typeof REGISTRY)[T]>, '@type'>
): SchemaNode<...>

Creates any supported schema type by name string. Useful for dynamic/CMS-driven code:

import { schema } from 'schemaorg-kit';

// Type is determined at runtime
const contentType = getContentTypeFromCMS(); // 'Article' | 'Product' | 'Recipe'
const data = getDataFromCMS();

const node = schema(contentType, data);
document.head.innerHTML += node.toScript();

Supported type names:

Person, Organization, Product, ProductGroup, Event, Place, LocalBusiness, Movie, Article, NewsArticle, BlogPosting, WebPage, WebSite, Dataset, Recipe, Course, SoftwareApplication, MathSolver, Language, PronounceableText, JobPosting, FAQPage, QAPage, Quiz, Question, DiscussionForumPosting, ItemList, ProfilePage, VacationRental, ImageObject, Offer, Review, VideoObject


extendThing

function extendThing<TType extends string, T extends ZodRawShape>(
  type: TType,
  shape: T
): ZodObject<...>

Helper used internally to build all Thing subtypes. Takes a string type literal and a Zod shape, and returns a Zod object that extends ThingSchema with @type defaulted to the given string.

import { extendThing } from 'schemaorg-kit';
import { z } from 'zod';

const MyTypeSchema = extendThing('MyType', {
  specialField: z.string(),
});
// MyTypeSchema includes all ThingSchema fields + '@type': 'MyType' default + specialField