Skip to content

Custom Schema

Schema is the core of Velite. It defines the structure and type of your content and validates it.

Refer to Velite Schemas for more information about built-in schema.

Velite supports custom schema. A schema is a JavaScript function that returns a Zod schema object.

Generally, I divide the schema into two categories: one for data validation and the other for data transformation.

Define a Validation Schema

ts
import { defineSchema, s } from 'velite'

// `s` is extended from Zod with some custom schemas,
// `s` also includes all members of zod, so you can use `s` as `z`

// for validating title
export const title = defineSchema(() => s.string().min(1).max(100))

// for validating email
export const email = defineSchema(() => s.string().email({ message: 'Invalid email address' }))

// custom validation logic
export const hello = defineSchema(() =>
  s.string().refine(value => {
    if (value !== 'hello') {
      return 'Value must be "hello"'
    }
    return true
  })
)

Refer to Zod documentation for more information about Zod.

Define a Transformation Schema

ts
import { defineSchema, s } from 'velite'

// for transforming title
export const title = defineSchema(() => s.string().transform(value => value.toUpperCase()))

// ...

Example

Remote Image with BlurDataURL Schema

ts
import { getImageMetadata, s } from 'velite'

import type { Image } from 'velite'

/**
 * Remote Image with metadata schema
 */
export const remoteImage = () =>
  s.string().transform<Image>(async (value, { addIssue }) => {
    try {
      const response = await fetch(value)
      const blob = await response.blob()
      const buffer = await blob.arrayBuffer()
      const metadata = await getImageMetadata(Buffer.from(buffer))
      if (metadata == null) throw new Error(`Failed to get image metadata: ${value}`)
      return { src: value, ...metadata }
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err)
      addIssue({ fatal: true, code: 'custom', message })
      return null as never
    }
  })

Schema Context

TIP

Considering that Velite's scenario often needs to obtain metadata information about the current file in the schema, Velite does not use the original Zod package. Instead, it uses a custom Zod package that provides a meta member in the schema context.

ts
import { defineSchema, s } from 'velite'

// convert a nonexistent field
export const path = defineSchema(() =>
  s.custom<string>().transform((value, ctx) => {
    if (ctx.meta.path) {
      return ctx.meta.path
    }
    return value
  })
)

Reference

the type of meta is ZodMeta, which extends VeliteFile.

Distributed under the MIT License.