Skip to content

Using createSchema

In Silgi, schemas define the data contract and validation rules for your API. Schemas created with the createSchema function elevate both the developer experience and the reliability of your application.


Why Should I Use Schemas?

  • End-to-End Type Safety: Provides full type control with TypeScript.
  • Automatic Validation: Incoming and outgoing data are automatically validated.
  • Self-Documenting API: Schemas enable automatic documentation of your API.
  • Modularity & Reusability: You can keep your schemas in different files and reuse them.
  • Enhanced Developer Experience: IDE support, autocompletion, and quick error detection.
  • Automatic Binding: Your schemas are automatically found by Silgi CLI and linked to services.
  • Extensibility: Modules can add new parameters to schemas.

Schema Basics

In Silgi, schemas determine the data structure, validation rules, and routes of your API. The basic approach for schemas is as follows:

  1. Path: The path defining your API route (e.g., /api/library/books/:id).
  2. Method: The HTTP method (GET, POST, PUT, DELETE, PATCH).
  3. Setup: Contains the schema configuration (input, output, pathParams, queryParams).

INFO

In Silgi, schemas are not limited to your own definitions. Modules can dynamically extend the structure and types of a schema by adding new types, parameters, or validation rules.

Route Example:GET:/api/library/books/:id or POST:/api/library/books/create


Schema Parameters

You can define the following parameters for each endpoint:

  • input: Request body.
  • output: Response data.
  • pathParams: URL path parameters (e.g., /:id).
  • queryParams: URL query parameters (e.g., ?includeDetails=true).
  • Additional Parameters: Can be added by modules.

Supported Validation Libraries

Silgi supports libraries compatible with Standard Schema.


Step-by-Step Schema Creation

0. Setup

silgi.config.ts
typescript
import { defineSilgiConfig } from 'silgi/config'

export default defineSilgiConfig({
  // Default: zod.
  // It is possible to use multiple validation libraries in a project.
  // For a single library: 'zod'
  // For multiple libraries: ['arktype', 'zod', 'valibot']
  schemaVendor: 'zod',
  // ... other settings
})

Run the following command to install packages and necessary dependencies:

bash
pnpm silgi install

Run the silgi prepare command that comes with Silgi CLI to create your schemas and services:

bash
pnpm silgi prepare

1. Define Your Routes First (Optional)

You can define your routes with the createRoute function. This step is optional but provides a more organized code structure.

book.ts
typescript
import { createRoute } from 'silgi'

export const getBookByIdRoute = createRoute({
  route: '/api/library/books/:id',
})

export const getBooksRoute = createRoute({
  route: '/api/library/books',
})

export const createBookRoute = createRoute({
  route: '/api/library/books/create',
})

2. Define Your Schema

You can define your schemas under server/schemas/ or server/services/.

book.ts
typescript
import { createSchema } from 'silgi'
import { z } from 'zod'

// Base types
const authorType = z.object({
  id: z.string(),
  name: z.string(),
})

const bookType = z.object({
  id: z.string(),
  name: z.string(),
  description: z.string().optional(),
  author: authorType.optional(),
})

export const getBookByIdSchema = createSchema({
  path: '/api/library/books/:id',
  method: ['GET'],
  setup: {
    pathParams: z.object({
      id: z.string(),
    }),
    queryParams: z.object({
      includeAuthor: z.boolean().optional(),
      format: z.enum(['full', 'summary']).optional(),
    }),
    output: bookType,
  },
})

export const getBooksSchema = createSchema({
  path: '/api/library/books',
  method: ['GET'],
  setup: {
    queryParams: z.object({
      limit: z.number().int().positive().optional(),
      offset: z.number().int().nonnegative().optional(),
      authorId: z.string().optional(),
      sortBy: z.enum(['name', 'date']).optional(),
    }),
    output: z.array(bookType),
  },
})

export const createBookSchema = createSchema({
  path: '/api/library/books/create',
  method: ['POST'],
  setup: {
    input: z.object({
      name: z.string(),
      description: z.string().optional(),
      authorId: z.string(),
    }),
    output: bookType,
  },
})

INFO

Your exported schema must start with export const. Otherwise, Silgi CLI will not find the schema.

To update types and runtime files again:

bash
pnpm silgi prepare

3. How to Access the Schema Within a Service?

Silgi allows you to use schema information in services. You can access the parameters defined in the schema within your service handler:

  • input.parameters.path: URL path parameters.
  • input.parameters.query: URL query parameters.
  • input.args: POST/PUT/PATCH request body.
typescript
handler: async (input, shared) => {
  const bookId = input.parameters.path?.id
  const includeAuthor = input.parameters.query?.includeAuthor
  // ...business logic...
  return { /* validated response */ }
}

What Should I Do When Schema Changes?

When you make a change to your schema, run the following command again to update Silgi's types and runtime files:

bash
pnpm silgi prepare

Frequently Asked Questions

Can I work with different validation libraries?

Yes, other Standard Schema compatible libraries besides Zod are also supported.

Can modules or plugins extend my schemas?

Yes, modules and plugins can add new parameters or validation rules to schemas.

Are schemas automatically linked?

Yes, schemas and services with the same path structure and HTTP method are automatically matched.


Summary

With Silgi schemas, you can easily manage your API's data contract, validation, and type safety. Developing fast and reliable backend services is now much easier thanks to automatic binding, modularity, and strong validation!


Advanced Example: Book API

Below is an example of how advanced features can be used on the schema side.

server/schemas/book.schema.ts
typescript
import { createRoute, createSchema } from 'silgi'
import { z } from 'zod'

// 1. Define routes
export const getBookByIdRoute = createRoute({
  route: '/api/library/books/:id',
})

export const getBooksRoute = createRoute({
  route: '/api/library/books',
})

export const createBookRoute = createRoute({
  route: '/api/library/books/create',
})

// 2. Define schemas
const authorType = z.object({
  id: z.string(),
  name: z.string(),
})

const bookType = z.object({
  id: z.string(),
  name: z.string(),
  description: z.string().optional(),
  author: authorType.optional(),
})

export const getBookByIdSchema = createSchema({
  path: '/api/library/books/:id',
  method: ['GET'],
  setup: {
    pathParams: z.object({
      id: z.string(),
    }),
    queryParams: z.object({
      includeAuthor: z.boolean().optional(),
      format: z.enum(['full', 'summary']).optional(),
    }),
    output: bookType,
  },
})

export const getBooksSchema = createSchema({
  path: '/api/library/books',
  method: ['GET'],
  setup: {
    queryParams: z.object({
      limit: z.number().int().positive().optional(),
      offset: z.number().int().nonnegative().optional(),
      authorId: z.string().optional(),
      sortBy: z.enum(['name', 'date']).optional(),
    }),
    output: z.array(bookType),
  },
})

export const createBookSchema = createSchema({
  path: '/api/library/books/create',
  method: ['POST'],
  setup: {
    input: z.object({
      name: z.string(),
      description: z.string().optional(),
      authorId: z.string(),
    }),
    output: bookType,
  },
})

In this example:

  1. Routes: Define API routes
  2. Schemas: Validation and type definitions with Zod

With Silgi, you can define type-safe, modular, and readable API schemas in this way.

Released under the MIT License. (dev). Documentation design is a copy of vite.dev docs.