Skip to content

Advanced Schema Operations

farrow-schema provides rich Schema manipulation tools, allowing you to flexibly derive and combine types.

Field Metadata

Use field function to add field descriptions:

typescript
import { field } from 'farrow-schema'

class User extends ObjectType {
  id = ID

  name = field({
    __type: String,
    description: 'User name'
  })

  email = field({
    __type: String,
    description: 'User email',
    deprecated: 'Please use contactEmail'
  })
}

Schema Operation Tools

pick - Select Fields

typescript
import { pickObject } from 'farrow-schema'

class FullUser extends ObjectType {
  id = String
  name = String
  email = String
  password = String
  createdAt = Date
}

// Select public fields
const PublicUser = pickObject(FullUser, ['id', 'name'])

type PublicUserType = TypeOf<typeof PublicUser>
// { id: string, name: string }

omit - Exclude Fields

typescript
import { omitObject } from 'farrow-schema'

// Exclude sensitive fields
const SafeUser = omitObject(FullUser, ['password'])

type SafeUserType = TypeOf<typeof SafeUser>
// { id: string, name: string, email: string, createdAt: Date }

// Create input type
const CreateUserInput = omitObject(FullUser, ['id', 'createdAt'])

type CreateUserInputType = TypeOf<typeof CreateUserInput>
// { name: string, email: string, password: string }

partial - Convert to Optional

typescript
import { partialObject } from 'farrow-schema'

class User extends ObjectType {
  id = String
  name = String
  email = String
}

const PartialUser = partialObject(User)

type PartialUserType = TypeOf<typeof PartialUser>
// { id?: string, name?: string, email?: string }

// Practical application: update input
const UpdateUserInput = partialObject(
  omitObject(FullUser, ['id', 'createdAt'])
)
// All fields are optional, used for PATCH requests

required - Convert to Required

typescript
import { requiredObject } from 'farrow-schema'

class OptionalUser extends ObjectType {
  id = Optional(String)
  name = Optional(String)
}

const RequiredUser = requiredObject(OptionalUser)

type RequiredUserType = TypeOf<typeof RequiredUser>
// { id: string, name: string }

keyof - Get Field Keys

typescript
import { keyofObject } from 'farrow-schema'

const keys = keyofObject(User)
// ['id', 'name', 'email']

💡 Tip: All tool functions have Object and Struct specific versions:

  • pickObject / pickStruct
  • omitObject / omitStruct
  • partialObject / partialStruct
  • requiredObject / requiredStruct
  • keyofObject / keyofStruct

It's recommended to use specific versions for better type hints.

Intersection Types

typescript
import { Intersect } from 'farrow-schema'

class BaseUser extends ObjectType {
  id = String
  name = String
}

class ContactInfo extends ObjectType {
  email = String
  phone = String
}

// Merge multiple types
const FullUser = Intersect(BaseUser, ContactInfo)

type FullUserType = TypeOf<typeof FullUser>
// { id: string, name: string, email: string, phone: string }

Modifier Types

Strict / NonStrict

Control validation mode at field level:

typescript
import { Strict, NonStrict } from 'farrow-schema'

class Product extends ObjectType {
  price = Strict(Number)      // Must be pure number
  quantity = NonStrict(Int)   // Accept "42" and convert to 42
}

ReadOnly / ReadOnlyDeep

typescript
import { ReadOnly, ReadOnlyDeep } from 'farrow-schema'

class Config extends ObjectType {
  settings = ReadOnly(Record(String))      // Shallow read-only
  nested = ReadOnlyDeep(ComplexObject)     // Deep read-only
}

Metadata Extraction

Basic Usage

typescript
import { Formatter } from 'farrow-schema/formatter'

class User extends ObjectType {
  id = ID
  name = String
  age = Number
  tags = List(String)
}

const { typeId, types } = Formatter.format(User)

console.log(typeId)  // 0 (ID of root type)
console.log(types)
/*
{
  "0": {
    "type": "Object",
    "name": "User",
    "fields": {
      "id": { typeId: 1, $ref: "#/types/1" },
      "name": { typeId: 2, $ref: "#/types/2" },
      "age": { typeId: 3, $ref: "#/types/3" },
      "tags": { typeId: 4, $ref: "#/types/4" }
    }
  },
  "1": { type: "Scalar", valueType: "string", valueName: "ID" },
  "2": { type: "Scalar", valueType: "string", valueName: "String" },
  "3": { type: "Scalar", valueType: "number", valueName: "Number" },
  "4": { type: "List", itemTypeId: 2, $ref: "#/types/2" }
}
*/

Application Scenarios

Generate API Documentation

typescript
import { Formatter, isNamedFormatType } from 'farrow-schema/formatter'

class CreateUserInput extends ObjectType {
  name = field({
    __type: String,
    description: 'User name'
  })
  email = field({
    __type: String,
    description: 'User email'
  })
}

const { typeId, types } = Formatter.format(CreateUserInput)

// Traverse types to generate documentation
for (const [id, formatType] of Object.entries(types)) {
  if (formatType.type === 'Object') {
    console.log(`## ${formatType.name}`)

    for (const [fieldName, field] of Object.entries(formatType.fields)) {
      console.log(`- **${fieldName}**: ${field.description || ''}`)
    }
  }
}

Generate Client Types

typescript
function generateTypeScript(types: FormatTypes): string {
  let code = ''

  for (const [id, formatType] of Object.entries(types)) {
    if (formatType.type === 'Object') {
      code += `export interface ${formatType.name} {\n`

      for (const [fieldName, field] of Object.entries(formatType.fields)) {
        const fieldType = getTypeScriptType(types[field.typeId])
        code += `  ${fieldName}: ${fieldType}\n`
      }

      code += `}\n\n`
    }
  }

  return code
}

Combination Operation Examples

typescript
// Define complete model
class FullUser extends ObjectType {
  id = String
  name = String
  email = String
  password = String
  role = String
  createdAt = Date
  updatedAt = Date
}

// Derive multiple Schemas
const PublicUser = pickObject(FullUser, ['id', 'name'])
const CreateUserInput = omitObject(FullUser, ['id', 'createdAt', 'updatedAt'])
const UpdateUserInput = partialObject(CreateUserInput)
const AdminUser = FullUser  // Admins can see complete information

// Type inference
type PublicUserType = TypeOf<typeof PublicUser>
// { id: string, name: string }

type CreateUserInputType = TypeOf<typeof CreateUserInput>
// { name: string, email: string, password: string, role: string }

type UpdateUserInputType = TypeOf<typeof UpdateUserInput>
// { name?: string, email?: string, password?: string, role?: string }

This is a third-party Farrow documentation site | Built with ❤️ and TypeScript