W Metropolis Quantum każdy system wymaga precyzyjnej walidacji danych — od terminali dostępowych po panele kontroli implantów. Zod to biblioteka walidacyjna zaprojektowana z myślą o TypeScript, która stała się standardem w ekosystemie Next.js. W przeciwieństwie do innych rozwiązań, Zod automatycznie generuje typy TypeScript na podstawie schematów walidacji, eliminując duplikację kodu.
W roku 2150, systemy Metropolis Quantum wymagają niezawodnej walidacji danych. Zod wyróżnia się na tle innych bibliotek walidacyjnych (Yup, Joi) z kilku powodów:
1npm install zod1import { z } from 'zod';Zod oferuje zestaw metod do definiowania typów danych — jak cyfrowe DNA opisujące strukturę informacji w systemie:
1import { z } from 'zod';
2
3// Typy prymitywne
4const nameSchema = z.string();
5const ageSchema = z.number();
6const isActiveSchema = z.boolean();
7
8// Schema obiektu — łączymy typy w strukturę
9const userSchema = z.object({
10 name: z.string(),
11 email: z.string().email(),
12 age: z.number().int().positive(),
13 isActive: z.boolean(),
14});Każdy
z.string(), z.number(), z.boolean() to podstawowy blok budulcowy schematu. Metoda z.object() łączy je w kompleksową strukturę — jak schemat terminala w Metropolis Quantum.Zod oferuje dwa sposoby walidacji danych:
1import { z } from 'zod';
2
3const schema = z.object({
4 name: z.string().min(2),
5 email: z.string().email(),
6});
7
8// 1. parse() — rzuca wyjątek przy błędnych danych
9try {
10 const data = schema.parse({ name: "Neo", email: "neo@quantum.io" });
11 console.log("Dane poprawne:", data);
12} catch (error) {
13 console.error("Błąd walidacji:", error);
14}
15
16// 2. safeParse() — NIE rzuca wyjątku, zwraca obiekt z wynikiem
17const result = schema.safeParse({ name: "", email: "zly-email" });
18
19if (result.success) {
20 console.log("Dane:", result.data);
21} else {
22 console.log("Błędy:", result.error.errors);
23 // [{ path: ["name"], message: "String must contain at least 2 character(s)" }, ...]
24}Zasada: Używaj
safeParse() w formularzach i Server Actions (bezpieczniejsze), a parse() gdy chcesz szybko przerwać wykonanie przy błędnych danych.Zod pozwala na precyzyjne definiowanie reguł walidacji z własnymi komunikatami — jak konfiguracja systemu bezpieczeństwa w terminalu:
1import { z } from 'zod';
2
3const registrationSchema = z.object({
4 username: z.string()
5 .min(3, { message: "Nazwa użytkownika musi mieć co najmniej 3 znaki" })
6 .max(20, { message: "Nazwa użytkownika może mieć maksymalnie 20 znaków" }),
7 email: z.string()
8 .email({ message: "Nieprawidłowy format adresu email" }),
9 age: z.number()
10 .int({ message: "Wiek musi być liczbą całkowitą" })
11 .min(13, { message: "Musisz mieć co najmniej 13 lat" }),
12 password: z.string()
13 .min(8, { message: "Hasło musi mieć co najmniej 8 znaków" })
14 .regex(/[A-Z]/, { message: "Hasło musi zawierać wielką literę" })
15 .regex(/[0-9]/, { message: "Hasło musi zawierać cyfrę" }),
16 acceptTerms: z.boolean()
17 .refine(val => val === true, {
18 message: "Musisz zaakceptować warunki korzystania z systemu"
19 }),
20});Metoda
.refine() pozwala na definiowanie własnych, niestandardowych reguł walidacji — przydatne, gdy wbudowane metody nie wystarczają.Jedną z najpotężniejszych cech Zod jest automatyczne generowanie typów TypeScript ze schematów. Nie musisz utrzymywać oddzielnych interfejsów:
1import { z } from 'zod';
2
3const implantSchema = z.object({
4 id: z.string().uuid(),
5 name: z.string().min(1),
6 powerLevel: z.number().min(0).max(100),
7 isActive: z.boolean(),
8 tags: z.array(z.string()),
9});
10
11// Typ generowany automatycznie ze schematu
12type Implant = z.infer<typeof implantSchema>;
13// Odpowiednik ręcznego: { id: string; name: string; powerLevel: number; isActive: boolean; tags: string[] }
14
15// Teraz typ i walidacja są ZSYNCHRONIZOWANE
16function processImplant(data: Implant) {
17 console.log(data.name); // TypeScript zna typ!
18}Dzięki
z.infer masz jedno źródło prawdy — schemat definiuje zarówno walidację, jak i typ TypeScript.Zod doskonale współpracuje z React Hook Form dzięki adapterowi
zodResolver. To najczęściej spotykany wzorzec w aplikacjach Next.js:1import { useForm } from 'react-hook-form';
2import { zodResolver } from '@hookform/resolvers/zod';
3import { z } from 'zod';
4
5// 1. Definiujemy schemat
6const loginSchema = z.object({
7 email: z.string()
8 .email({ message: "Nieprawidłowy adres email" }),
9 password: z.string()
10 .min(8, { message: "Hasło musi mieć co najmniej 8 znaków" }),
11});
12
13// 2. Generujemy typ
14type LoginForm = z.infer<typeof loginSchema>;
15
16// 3. Używamy w komponencie
17export default function LoginPage() {
18 const {
19 register,
20 handleSubmit,
21 formState: { errors, isSubmitting }
22 } = useForm<LoginForm>({
23 resolver: zodResolver(loginSchema), // Zod jako walidator
24 defaultValues: { email: '', password: '' }
25 });
26
27 const onSubmit = async (data: LoginForm) => {
28 // data jest już zwalidowane i typowane!
29 console.log("Logowanie:", data);
30 };
31
32 return (
33 <form onSubmit={handleSubmit(onSubmit)}>
34 <input {...register('email')} placeholder="Email" />
35 {errors.email && <p>{errors.email.message}</p>}
36
37 <input {...register('password')} type="password" placeholder="Hasło" />
38 {errors.password && <p>{errors.password.message}</p>}
39
40 <button type="submit" disabled={isSubmitting}>
41 {isSubmitting ? 'Logowanie...' : 'Zaloguj się'}
42 </button>
43 </form>
44 );
45}Kluczowa linia to
resolver: zodResolver(loginSchema) — łączy schemat Zod z systemem walidacji React Hook Form.W dużych aplikacjach warto tworzyć reusable schematy, które można łączyć i rozszerzać:
1import { z } from 'zod';
2
3// Bazowy schemat adresu — używany w wielu miejscach
4const addressSchema = z.object({
5 street: z.string().min(1, "Ulica jest wymagana"),
6 city: z.string().min(1, "Miasto jest wymagane"),
7 postCode: z.string().regex(/^d{2}-d{3}$/, "Format: XX-XXX"),
8});
9
10// Schemat użytkownika — rozszerzamy o adres
11const userSchema = z.object({
12 name: z.string().min(2),
13 email: z.string().email(),
14 address: addressSchema, // Zagnieżdżanie
15 shippingAddress: addressSchema.optional(), // Opcjonalny
16});
17
18// .merge() — łączenie dwóch schematów
19const baseProfile = z.object({ name: z.string(), bio: z.string() });
20const socialProfile = z.object({ twitter: z.string(), github: z.string() });
21const fullProfile = baseProfile.merge(socialProfile);
22
23// .extend() — dodawanie pól do istniejącego schematu
24const adminUser = userSchema.extend({
25 role: z.literal('admin'),
26 permissions: z.array(z.string()),
27});
28
29type AdminUser = z.infer<typeof adminUser>;Metoda
.refine() pozwala na walidację, której nie da się wyrazić deklaratywnie — np. porównanie dwóch pól:1import { z } from 'zod';
2
3const passwordSchema = z.object({
4 password: z.string().min(8),
5 confirmPassword: z.string(),
6}).refine(data => data.password === data.confirmPassword, {
7 message: "Hasła nie są identyczne",
8 path: ["confirmPassword"], // Błąd przypisany do pola confirmPassword
9});
10
11// superRefine — gdy potrzebujesz wielu błędów jednocześnie
12const advancedSchema = z.object({
13 accountType: z.enum(['personal', 'business']),
14 personalId: z.string().optional(),
15 companyId: z.string().optional(),
16}).superRefine((data, ctx) => {
17 if (data.accountType === 'personal' && !data.personalId) {
18 ctx.addIssue({
19 code: z.ZodIssueCode.custom,
20 message: "ID osobiste jest wymagane dla konta osobistego",
21 path: ['personalId'],
22 });
23 }
24 if (data.accountType === 'business' && !data.companyId) {
25 ctx.addIssue({
26 code: z.ZodIssueCode.custom,
27 message: "ID firmy jest wymagane dla konta biznesowego",
28 path: ['companyId'],
29 });
30 }
31});Zod to standard w ekosystemie Next.js, ale istnieją alternatywy:
Zod jest rekomendowany dla projektów Next.js, ponieważ łączy walidację z typami TypeScript i działa zarówno na serwerze, jak i kliencie.
Zod to niezbędne narzędzie w arsenale programisty Next.js:
z.string(), z.number(), z.object() — definiowanie schematówparse() i safeParse() — walidacja danych.min(), .email(), .regex() — reguły z komunikatami błędów.refine() — niestandardowa walidacjazodResolver — integracja z React Hook Formz.infer<typeof schema> — automatyczne generowanie typów