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:
- Path: The path defining your API route (e.g.,
/api/library/books/:id
). - Method: The HTTP method (
GET
,POST
,PUT
,DELETE
,PATCH
). - 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
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:
pnpm silgi install
Run the silgi prepare
command that comes with Silgi CLI to create your schemas and services:
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.
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/
.
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:
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.
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:
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.
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:
- Routes: Define API routes
- Schemas: Validation and type definitions with Zod
With Silgi, you can define type-safe, modular, and readable API schemas in this way.