@@ -14,6 +14,7 @@ This library is an attempt to provide a simple and opinionated way to do all of
1414
1515``` typescript
1616import { buildValidator } from " model-validator-ts" ;
17+ import { z } from " zod" ;
1718
1819const loginCommand = buildValidator ()
1920 // Your usual zod schema
@@ -71,6 +72,35 @@ if (!result.success) {
7172 // { user: { id: string, email: string }, token: string }
7273 console .log (result .result );
7374}
75+
76+ // Now use in your http handler
77+ app .post (" /login" , async (req , res ) => {
78+ const result = await loginCommand .run (req .body );
79+ if (! result .success ) {
80+ return res .status (400 ).json ({
81+ success: false ,
82+ errors: result .errors .toObject (),
83+ });
84+ }
85+ return res .status (200 ).json ({
86+ success: true ,
87+ result: result .result ,
88+ });
89+ });
90+
91+ // Or if using something like trpc
92+ const loginProcedure = publicProcedure
93+ .input (loginCommand .inputSchema )
94+ .mutation (async ({ input }) => {
95+ const result = await loginCommand .run (input );
96+ if (! result .success ) {
97+ throw new trpc .TRPCError ({
98+ code: " BAD_REQUEST" ,
99+ message: result .errors .toObject (),
100+ });
101+ }
102+ return result .result ;
103+ });
74104```
75105
76106For a more complex real-world example with multiple dependencies and rules, check out the [ order cancellation example] ( https://github.com/muniter/model-validator-ts/blob/main/src/order-cancellation.example.ts ) .
@@ -95,168 +125,12 @@ yarn add model-validator-ts
95125pnpm add model-validator-ts
96126```
97127
98- ## Quick Start
99-
100- ### Basic Validation
101-
102- ``` typescript
103- import { buildValidator } from " model-validator-ts" ;
104- import { z } from " zod" ;
105-
106- const userSchema = z .object ({
107- name: z .string ().min (3 ),
108- age: z .number ().min (18 ),
109- email: z .string ().email (),
110- });
111-
112- // Simple validation without dependencies
113- const validator = buildValidator ().input (userSchema ).rule ({
114- fn : async ({ data , bag }) => {
115- if (await isUserBlacklisted (data .email )) {
116- return bag .addError (" email" , " User is blacklisted" );
117- }
118- },
119- });
120-
121- const result = await validator .validate ({
122- name: " John" ,
123- age: 25 ,
124- 125- });
126-
127- if (result .success ) {
128- console .log (" Valid user:" , result .value );
129- console .log (" Context:" , result .context );
130- } else {
131- console .log (" Typed Validation errors to use in your UI:" , result .errors .toObject ());
132- }
133- ```
134-
135- ### User Login Example
136-
137- A complete example showing schema validation, business rules, context passing, and command execution:
138-
139- ``` typescript
140- import { z } from " zod" ;
141- import { buildValidator } from " model-validator-ts" ;
142-
143- interface User {
144- id: string ;
145- role: " admin" | " customer" ;
146- email: string ;
147- passwordHash: string ;
148- }
128+ ## Examples
149129
150- const loginSchema = z .object ({
151- email: z .string ().email (),
152- password: z .string ().min (8 ),
153- });
130+ If you want more complex examples, here are a few:
154131
155- const loginCommand = buildValidator ()
156- .input (loginSchema )
157- .rule ({
158- fn : async ({ data , bag }) => {
159- // Data is fully typed from the schema
160- const user = await userService .findByEmail (data .email );
161- if (! user ) {
162- return bag .addGlobalError (" Invalid email or password" );
163- }
164- if (! (await userService .validatePassword (user , data .password ))) {
165- return bag .addGlobalError (" Invalid email or password" );
166- }
167- // Pass user to next rules via context
168- return { context: { user } };
169- },
170- })
171- .rule ({
172- fn : async ({ data , bag , context }) => {
173- // Access context from previous rule
174- if (context .user .role === " admin" ) {
175- return bag .addError (" email" , " Admin users cannot login with password" );
176- }
177- },
178- })
179- .command ({
180- execute : async ({ context , bag }) => {
181- // Execute the business logic
182- const { user } = context ;
183- return {
184- user ,
185- token: await userService .generateToken (user ),
186- };
187- },
188- });
189-
190- // Usage in an endpoint
191- app .post (" /login" , async (req , res ) => {
192- const result = await loginCommand .run (req .body );
193- if (! result .success ) {
194- return res .status (400 ).json ({
195- success: false ,
196- errors: result .errors .toObject (),
197- });
198- }
199- return res .status (200 ).json ({
200- success: true ,
201- result: result .result ,
202- });
203- });
204- ```
205-
206- ### Money Transfer with External Services
207-
208- A more complex example showing dependency injection and errors happening on command execution and rules.
209-
210- ``` typescript
211- const transferMoneySchema = z .object ({
212- fromAccount: z .string (),
213- toAccount: z .string (),
214- amount: z .number ().positive (),
215- });
216-
217- const transferCommand = buildValidator ()
218- .input (transferMoneySchema )
219- .$deps <{ externalBankService: BankService }>()
220- .rule ({
221- id: " no-self-transfer" ,
222- fn : async ({ data , bag }) => {
223- if (data .fromAccount === data .toAccount ) {
224- bag .addError (" toAccount" , " Cannot transfer to same account" );
225- }
226- },
227- })
228- .rule ({
229- id: " balance-check" ,
230- fn : async ({ data , deps , bag }) => {
231- const balance = await deps .externalBankService .checkAccountBalance (
232- data .fromAccount
233- );
234- if (balance < data .amount ) {
235- bag .addError (" amount" , " Insufficient funds" );
236- }
237- },
238- })
239- .command ({
240- execute : async ({ data , deps , bag }) => {
241- try {
242- const result = await deps .externalBankService .executeTransfer (
243- data .fromAccount ,
244- data .toAccount ,
245- data .amount
246- );
247- return result ;
248- } catch (error ) {
249- // Handle runtime errors, or things that just can't be validated before execution
250- return bag .addGlobalError (` External service error: ${error .message } ` );
251- }
252- },
253- });
254-
255- // Execute with dependencies
256- const result = await transferCommand
257- .provide ({ externalBankService })
258- .run ({ fromAccount: " acc-123" , toAccount: " acc-456" , amount: 100 });
259- ```
132+ - [ Order Cancellation Example] ( https://github.com/muniter/model-validator-ts/blob/main/src/order-cancellation.example.ts ) - Complex e-commerce validation scenario that evaluate tons of rules neccessary for cancelling an order.
133+ - [ User Login Example] ( https://github.com/muniter/model-validator-ts/blob/main/src/login.example.ts ) - Login into an application that has a few special rules.
260134
261135## API Reference
262136
0 commit comments