Skip to main content

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

SchemaHandle TypeDescription
z.handle()HandleGeneric handle
z.hentity()HEntityAny entity
z.hped()HPedPedestrians/NPCs
z.hvehicle()HVehicleVehicles
z.hobj()HObjWorld objects
z.hcam()HCamCameras
z.hblip()HBlipMap blips
z.hplayer()HPlayerPlayers
z.hpickup()HPickupPickups
z.hinterior()HInteriorInteriors
z.hfireId()HFireIdFire 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

  1. Validate at boundaries - Parse external data (NUI, events) early
  2. Use handle schemas - Maintain type safety across serialization
  3. Define schemas near usage - Keep schemas with their consuming code