You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+45-6Lines changed: 45 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -154,6 +154,7 @@ import * ZagoraTypes from 'zagora/types';
154
154
import*zagoraUtilsfrom'zagora/utils';
155
155
```
156
156
157
+
[Back to top](#zagora)
157
158
158
159
## Why zagora?
159
160
@@ -164,6 +165,8 @@ Both `tRPC` and `oRPC` are promoted as "backend", or specifically for when you'r
164
165
165
166
They are built around the network, Zagora is built around functions with excellent egonomics, developer experience, and no assumptions. **It produces just functions, i cannot stress that enough.**
166
167
168
+
[Back to top](#zagora)
169
+
167
170
### Why Zagora over oRPC/tRPC/neverthrow/effect.ts?
168
171
169
172
- Zagora is focused on producing "just functions", not networks, routers, or groups.
@@ -193,12 +196,16 @@ They are built around the network, Zagora is built around functions with excelle
193
196
194
197
Funny enough, you can use Zagora to build fully type-safe CLIs with auto-generated detailed help, based on the provided schemas. I have another library for that, which i will overhaul soon - [zodest](https://npmjs.com/package/zodest).
195
198
199
+
[Back to top](#zagora)
200
+
196
201
### Why Zagora over plain TypeScript functions?
197
202
198
203
- Plain TypeScript offers compile-time types but no runtime validation — a mismatch between runtime and
199
204
compile-time can blow up.
200
205
- Zagora combines runtime validation/transforms (StandardSchema) with full compile-time inference, and returns a safe, uniform result tuple inspired by true functional programming
201
206
207
+
[Back to top](#zagora)
208
+
202
209
### Why Zagora over standalone Zod/Valibot usage?
203
210
204
211
- zagora gives a small ergonomic layer
@@ -209,6 +216,8 @@ Funny enough, you can use Zagora to build fully type-safe CLIs with auto-generat
209
216
- single place to validate inputs/outputs/errors
210
217
- unified non-throwing result shape
211
218
219
+
[Back to top](#zagora)
220
+
212
221
## Features
213
222
214
223
### Type-Safe Input/Output Validation
@@ -229,25 +238,27 @@ const procedure = zagora()
229
238
// NOTE: you don't need to pass `age` because it has a default value set in schema.
Define custom error types with schemas for better error handling.
@@ -285,6 +296,9 @@ if (res.ok) {
285
296
}
286
297
```
287
298
299
+
[Back to top](#zagora)
300
+
301
+
### Error Type Guards
288
302
Isn't it amazing? You will never see `Error` or `try/catch` blocks again, and everything is typed top to bottom, well-known and intuitive.
289
303
290
304
But wait, there's more: **Type Guards**!
@@ -419,6 +433,8 @@ hello('invalid-keys');
419
433
420
434
**Important:** if you want the error validation to actually throw on unknown keys passed to the error helper, then you need to make the error object schema more strict - just like `z.object(...).strict()`.
421
435
436
+
[Back to top](#zagora)
437
+
422
438
### Context Management
423
439
424
440
In some advanced scenarios, you may need to pass runtime context to handlers, like `req`, `db`, `user`, sessions, and other dependencies. It's fully typed dependency injection mechanism.
@@ -443,6 +459,7 @@ procedure('charlie');
443
459
// => 'foo-bar has foo -> qux, id = charlie'
444
460
```
445
461
462
+
[Back to top](#zagora)
446
463
447
464
### Object Inputs
448
465
@@ -453,6 +470,8 @@ zagora()
453
470
.callable()
454
471
```
455
472
473
+
[Back to top](#zagora)
474
+
456
475
### Tuple Inputs (Multiple Arguments)
457
476
458
477
Tuple schemas is used for defining multiple arguments in a handler. It's one of the ABSOLUTE KEY features of Zagora, and it is one of the most complex stuff to achieve - but the end results is astonishing. It just works - you define schema and you get fully typed and validated arguments, INCLUDING per-argument reporting/diagnostics, INCLUDING filling defaults if `.default()` is used and respecting `.optional()`.
@@ -494,6 +513,8 @@ fnTwo('Barry', 25) // => Barry is 25, from unknown
494
513
fnTwo('Barry', 33, 'USA') // => Barry is 33, from USA
495
514
```
496
515
516
+
[Back to top](#zagora)
517
+
497
518
### Default Values
498
519
499
520
Optionals and default values are supported at any level with any schema, whether it's through the Tuple Args, or if it's primitive schema for `string`, `array`, or `object`.
@@ -512,6 +533,8 @@ fn({ name: 'John' }) // age defaults to 18
512
533
// => 'John is 18, from unknown'
513
534
```
514
535
536
+
[Back to top](#zagora)
537
+
515
538
### Async Support
516
539
517
540
Zagora is fully async-aware at every level of the system:
Built-in caching with custom cache adapter. Cache key includes the input, the input/output/error schemas, and handler function body. That can be used to implement custom caching strategies, and memoization.
@@ -622,6 +647,8 @@ const res = await proc(22);
622
647
623
648
You can also provide the cache through `.callable({ cache })`. That is useful, if you want to provide it at "execution place", not at "definition place". For example, you'd have a set of procedures written at one place, then throgh "router" or some object that combiens them you want to call them at a `Request/Response` server handler.
624
649
650
+
[Back to top](#zagora)
651
+
625
652
### Environment Variables
626
653
627
654
You can provide the runtime env vars (either `process.env` or `import.meta.env`) through the second argument of `.env(schema, envs)` or at later stage through the `.callable({ env })` call. Either way, they will be validated. The parsed variables will be accessible through the handler's `options` object.
@@ -645,6 +672,8 @@ Keep in mind that if you have `autoCallable: true` enabled in the instance, then
0 commit comments