The GuildPass SDK includes a lightweight, framework-independent runtime validation system for verifying unknown data from remote APIs before exposing it as typed public data.
TypeScript types disappear at runtime. This validation system ensures that data received from external sources matches expected schemas, preventing malformed or incompatible responses from propagating through applications.
All validators implement the Schema<T> interface:
interface Schema<T> {
parse(input: unknown, path?: string[], depth?: number): ValidationResult<T>;
}Validation returns a discriminated union:
type ValidationResult<T> =
| { success: true; data: T }
| { success: false; error: ValidationError };Validation errors include machine-readable path information:
interface ValidationError {
message: string;
path: string[]; // e.g., ['data', 'members', '[2]', 'id']
}string()- Validates stringsnumber()- Validates finite numbers (rejects NaN, Infinity)boolean()- Validates booleansnullType()- Validates null values
literal(value)- Validates exact literal values (string, number, boolean)optional(schema)- Allows undefined/null or validates against schemanullable(schema)- Allows null or validates against schema (rejects undefined)array(itemSchema)- Validates arrays where each item matches itemSchemaobject(shape, options)- Validates objects with explicitly declared keysrecord(valueSchema)- Validates dictionary-like objects with uniform value typesunion(...schemas)- Tries each schema sequentially until one succeeds
The object schema supports configurable unknown key handling:
enum UnknownKeyHandling {
STRIP = "strip", // Remove unknown keys (default)
REJECT = "reject", // Fail validation if unknown keys present
PRESERVE = "preserve" // Keep unknown keys in result
}import { string, number, object } from '@guildpass/sdk';
const userSchema = object({
name: string(),
age: number(),
});
const result = userSchema.parse({ name: "John", age: 30 });
if (result.success) {
console.log(result.data.name); // TypeScript knows this is a string
} else {
console.error(`Validation failed at ${result.error.path.join('.')}: ${result.error.message}`);
}const responseSchema = object({
users: array(object({
id: string(),
name: string(),
age: optional(number()),
})),
});const statusSchema = union(
literal("active"),
literal("inactive"),
literal("pending")
);const metadataSchema = record(string());
const result = metadataSchema.parse({ key1: "value1", key2: "value2" });import { UnknownKeyHandling } from '@guildpass/sdk';
const strictSchema = object(
{ name: string() },
{ unknownKeys: UnknownKeyHandling.REJECT }
);All validation is bounded by MAX_DEPTH (20 levels) to prevent DoS attacks via deeply nested or circular data:
import { MAX_DEPTH } from '@guildpass/sdk';Invalid values are never returned as successful typed data. Validation failures are guaranteed to include path information for debugging.
The validation system does not execute arbitrary code from input data. It only performs type checking and structural validation.
- Always validate external data - Never trust data from APIs, user input, or external sources
- Use specific schemas - Prefer specific schemas over generic ones when possible
- Handle validation failures - Always check
result.successbefore accessingresult.data - Configure unknown keys appropriately - Use
REJECTfor strict validation,STRIPfor lenient validation - Test validation schemas - Unit test schemas with both valid and invalid inputs
The validation system integrates seamlessly with the HTTP transport layer:
import { HttpTransport } from '@guildpass/sdk';
import { object, string } from '@guildpass/sdk';
const transport = new HttpTransport({ baseUrl: 'https://api.example.com' });
const userSchema = object({ name: string() });
const response = await transport.request({ method: 'GET', path: '/user' });
const validated = userSchema.parse(response);- Validation is synchronous and fast for typical response sizes
- Depth limiting prevents performance degradation on pathological inputs
- No runtime dependencies - pure TypeScript implementation
- Small footprint suitable for SDK distribution
Validation errors provide structured information:
if (!result.success) {
const { message, path } = result.error;
const pathString = path.length > 0 ? path.join('.') : 'root';
console.error(`Validation failed at ${pathString}: ${message}`);
}Path format uses dot notation for objects and bracket notation for arrays:
user.name- nested object fieldusers[2].id- array element with nested field[0]- root array element