Core API
Table of contents
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