Custom Schemas
When your project requires Schema.org types not included in the built-in catalog (such as PodcastEpisode, MedicalWebPage, or TechArticle), use defineSchema() to construct your own custom builders with identical validation and @graph capabilities.
1. Creating a Custom Builder
Section titled “1. Creating a Custom Builder”Use defineSchema() by providing the Schema.org @type name and a Zod schema:
import { defineSchema } from '@unschema-graph/core';import { z } from 'zod';
export const PodcastEpisode = defineSchema( 'PodcastEpisode', z.object({ name: z.string().min(1), url: z.string().url(), duration: z.string().optional(), partOfSeries: z.string().optional(), }));You can now use your custom builder exactly like any built-in builder:
import { PodcastEpisode } from '../../lib/schemas/podcast';
const episode = PodcastEpisode({ '@id': '#episode-42', name: 'Building with Astro & unschema-graph', url: 'https://example.com/podcast/episode-42', duration: 'PT45M', partOfSeries: '#podcast-series',});Every custom builder automatically:
- Injects the Schema.org
@typeattribute ("PodcastEpisode"). - Accepts an optional
@idproperty. - Rejects unknown top-level properties to prevent typos.
- Obeys global and per-call
onErrorseverity modes (throw,warn,silent). - Exposes
.schema,.entityType, and.safeParse().
2. Composing with Built-in Schemas
Section titled “2. Composing with Built-in Schemas”Every built-in schema is exported with the *Schema suffix (e.g. PersonSchema, OrganizationSchema, ImageObjectSchema, IsoDateSchema). You can nest them directly into your custom schemas:
import { defineSchema, PersonSchema, ImageObjectSchema,} from '@unschema-graph/core';import { z } from 'zod';
export const PodcastSeries = defineSchema( 'PodcastSeries', z.object({ name: z.string(), description: z.string(), author: PersonSchema, image: ImageObjectSchema.optional(), }));