schema-dsl Quick Start
Start here if you are new to schema-dsl. After this page, continue with DSL Syntax for authoring rules and Validation Guide for real validation flows.
Installation
Node.js requirement: >=18.0.0
The current TypeScript rewrite uses Node.js >=18.0.0 as the only runtime baseline and no longer promises compatibility with older Node versions.
5-minute Quick Start
1. Hello World in 30 seconds
import { s, validate } from 'schema-dsl/pure';
// Define a schema
const schema = s({
name: 'string:1-50!',
email: 'email!'
});
// Validate data with the convenience helper
const result = validate(schema, {
name: 'John',
email: 'john@example.com'
});
console.log(result.valid); // true
Explanation:
'string:1-50!' means a required string with length 1 to 50.
'email!' means a required email field.
! marks a field as required.
2. DSL syntax cheat sheet in 1 minute
// Basic types
'string' // string
'number' // number
'integer' // integer
'boolean' // boolean
'email' // email
'url' // URL
'date' // date
// Constraints
'string:3-32' // length 3-32
'string:100' // max length 100, shorthand
'string:-100' // max length 100, explicit form
'string:10-' // min length 10, no maximum
'number:18-120' // range 18-120
// Required
'string!' // required string
'email!' // required email
// Enum
'active|inactive|pending' // one of three values
// Arrays
'array<string>' // string array
'array:1-10<string>' // 1 to 10 strings
'array<string:1-50>' // array items with constraints
Syntax rules:
type:max -> maximum value or length, shorthand
type:min-max -> range
type:min- -> minimum only
type:-max -> maximum only
3. Chainable fields in 2 minutes
The recommended authoring entry is schema-dsl/pure with the s namespace. Keep simple fields as DSL strings, wrap a DSL seed with s('...') when you need chain methods such as .label(), .messages(), .pattern() or .custom(), and use s.xxx() factories when you want the strongest TypeScript method discovery.
import { s } from 'schema-dsl/pure';
const schema = s({
// DSL seed + chain methods
email: s('email!')
.pattern(/custom/)
.label('email address'),
username: s('string:3-32!')
.pattern(/^[a-zA-Z0-9_]+$/)
.messages({
pattern: 'Only letters, numbers, and underscores are allowed'
})
.label('username'),
// Simple fields can stay as plain DSL strings
age: 'number:18-120',
role: 'user|admin',
// Factory entry with full method discovery
recoveryEmail: s.email()
.label('recovery email')
.require()
});
Available methods:
.pattern(regex) - regular expression validation
.label(text) - field label
.messages(obj) - custom messages
.description(text) - description
.custom(fn) - custom validator
4. Complete example in 2 minutes
import { s, validate } from 'schema-dsl/pure';
// Define a user registration schema
const registerSchema = s({
// Username: regex validation
username: s('string:3-32!')
.pattern(/^[a-zA-Z0-9_]+$/)
.label('username')
.error({
pattern: 'Only letters, numbers, and underscores are allowed'
}),
// Email: label
email: s('email!').label('email address'),
// Password: complex regex
password: s('string:8-64!')
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/)
.label('password')
.error({
pattern: 'Must include uppercase letters, lowercase letters, and numbers'
}),
// Simple fields
age: 'number:18-120',
role: 'user|admin'
});
// Validate data
const testData = {
username: 'john_doe',
email: 'john@example.com',
password: 'Password123',
age: 25,
role: 'user'
};
const result = validate(registerSchema, testData);
if (result.valid) {
console.log('Validation passed!');
} else {
console.log('Validation failed:', result.errors);
}
Best Practices
1. Use plain DSL for simple fields
import { s } from 'schema-dsl/pure';
const schema = s({
name: 'string:1-50!', // concise
age: 'number:18-120', // clear
role: 'user|admin' // direct
});
2. Use chain APIs for complex fields
import { s } from 'schema-dsl/pure';
const schema = s({
email: s('email!')
.pattern(/custom/)
.messages({...})
.label('email'),
username: s('string:3-32!')
.pattern(/^\w+$/)
.custom(checkExists),
recoveryEmail: s.email()
.label('recovery email')
.require()
});
3. The 80/20 rule
Use plain DSL strings for simple fields. Use s('...') when a field needs labels, messages, regexes or custom validators. Use s.xxx() factories when you want the most complete TypeScript method discovery.
Common Scenarios
const formSchema = s({
email: s('email!').label('email address'),
password: s('string:8-64!').label('password'),
nickname: s('string:2-20').label('nickname'),
bio: 'string:500',
website: 'url',
age: 'number:18-120',
gender: 'male|female|other'
});
Custom validation
.custom() supports synchronous functions. If it returns a Promise, use validateAsync(). Synchronous validate() returns an explicit error when it sees a Promise-returning custom validator.
const schema = s({
username: s('string:3-32!')
.custom((value) => {
if (value === 'admin') {
return 'Username already exists';
}
})
});
Nested objects
const schema = s({
user: {
profile: {
name: s('string:1-50!').label('name'),
avatar: s('url').label('avatar'),
social: {
twitter: s('url').pattern(/twitter\.com/),
github: s('url').pattern(/github\.com/)
}
}
}
});
Object arrays
const orderSchema = s({
orderNo: 'string!',
items: s.array({
sku: 'string!',
quantity: 'integer:1-999!',
price: 'number:0-!'
}).min(1)
});
Next Steps
Learn more
Example code
Other topic examples are linked from the bottom of their own documents and use stable GitHub example links.
Advanced features
Entry choices
Use schema-dsl/pure for ordinary application code. It gives you the same schema authoring API without installing global String methods.
Use schema-dsl/runtime when a framework, plugin host or multi-tenant app needs an isolated runtime instance:
import { createRuntime } from 'schema-dsl/runtime';
const runtime = createRuntime();
const schema = runtime.s({
email: 'email!',
username: runtime.s('string:3-32!').label('username')
});
Use String Extensions or the compile-time transform only when you intentionally want direct string-chain authoring such as 'email!'.label('Email'). See String Extensions and Runtime Isolation for the boundary.
For design background and benchmark data, continue with Design Philosophy and Performance Guide.
FAQ
Q: What is the difference between String extensions and plain DSL?
A:
- Plain DSL: best for simple fields, concise syntax.
s('...') chain API: best for complex fields without relying on global prototype changes.
s.xxx() factories: best when you want the strongest TypeScript method discovery.
- String extensions: available when you intentionally enable direct string-chain authoring.
// Plain DSL, simple
name: 'string:1-50!'
// s() chain API, complex and recommended
email: s('email!')
.pattern(/custom/)
.messages({...})
// String extensions, explicitly enabled
import 'schema-dsl/register-string';
import { s } from 'schema-dsl/pure';
const schemaWithStringExtension = s({
email: 'email!'.pattern(/custom/).messages({...})
});
Q: How do I explicitly enable String extensions?
A:
import 'schema-dsl/register-string';
import { s } from 'schema-dsl/pure';
const schema = s({
email: 'email!'.label('Email')
});
For cleanup in tests or legacy compatibility details, see String Extensions.
Q: Does schema-dsl support TypeScript?
A: Yes. schema-dsl ships complete TypeScript type definitions.
Congratulations
You now know the core schema-dsl workflow.
Key takeaways:
- DSL syntax is concise and readable.
schema-dsl/pure + s is the recommended default entry for application code.
s('...') gives explicit DSL seeds plus builder hints.
s.xxx() factories provide the strongest method discovery.
Start using it: npm install schema-dsl
Corresponding Example File
Example entry: quick-start.ts
Description: Covers the Hello World flow, the schema-dsl/pure + s authoring path, a user registration example, and the basic validate() plus Validator.compile() reuse path from the quick start. It can be run directly as a reference.