Validation with Zod
Extended Zod schemas with FiveM handle type support for runtime validation.
Overview
True Life uses Zod for runtime schema validation. The @core/zod module extends Zod with schemas for FiveM's typed handle system.
Why Handle Schemas?
FiveM uses branded number types (HEntity, HPed, HVehicle, etc.) for compile-time type safety. These schemas validate and transform plain numbers into properly typed handles.
Usage
import { z } from "@core/zod";
// Standard Zod schemas work as expected
const playerSchema = z.object({
name: z.string(),
health: z.number(),
});
// FiveM handle schemas for entity handles
const spawnEventSchema = z.object({
entity: z.hentity(),
ped: z.hped(),
vehicle: z.hvehicle(),
});
// Parse validates and transforms to typed handles
const data = spawnEventSchema.parse({
entity: 12345,
ped: 67890,
vehicle: 11111,
});
// data.entity is typed as HEntity
// data.ped is typed as HPed
// data.vehicle is typed as HVehicle
Available Handle Schemas
| Schema | Handle Type | Description |
|---|---|---|
z.handle() | Handle | Generic handle |
z.hentity() | HEntity | Any entity |
z.hped() | HPed | Pedestrians/NPCs |
z.hvehicle() | HVehicle | Vehicles |
z.hobj() | HObj | World objects |
z.hcam() | HCam | Cameras |
z.hblip() | HBlip | Map blips |
z.hplayer() | HPlayer | Players |
z.hpickup() | HPickup | Pickups |
z.hinterior() | HInterior | Interiors |
z.hfireId() | HFireId | Fire instances |
Common Patterns
RPC Payload Validation
const transferSchema = z.object({
fromAccount: z.string(),
toAccount: z.string(),
amount: z.number().positive(),
});
@Server
async transfer(ctx: EventContext<[payload: unknown]>): Promise<Result> {
const [rawPayload] = ctx.args;
const payload = transferSchema.parse(rawPayload);
// payload is now typed and validated
}
Entity Event Validation
const damageEventSchema = z.object({
victim: z.hped(),
attacker: z.hped().optional(),
damage: z.number(),
weaponHash: z.number(),
});
Best Practices
- Validate at boundaries - Parse external data (NUI, events) early
- Use handle schemas - Maintain type safety across serialization
- Define schemas near usage - Keep schemas with their consuming code