1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-20 15:14:08 +00:00
zap/doc/zig-ception.md
renerocksai 551d033edc API cleanup #2
--------------

- Middleware: no more MixContexts
    - zig structs are fine
- more documentation (comments -> autodoc).
- websocket docs
2024-01-08 19:26:16 +01:00

81 lines
2.9 KiB
Markdown

# ZIG-CEPTION!
In ZAP, we have great zig-ception moment in the [middleware
example](../examples/middleware/middleware.zig). But first we need to introduce
one key function of `zap.Middleware`: **combining structs at comptime!**
## Combining structs at runtime
Here is how it is used in user-code:
```zig
// create a combined context struct
const Context = struct {
user: ?UserMiddleWare.User = null,
session: ?SessionMiddleWare.Session = null,
};
```
Why do we create combined structs? Because all our Middleware handler functions
need to receive a per-request context. But each wants their own data: the User
middleware might want to access a User struct, the Session middleware might want
a Session struct, and so on. So, which struct should we use in the prototype of
the "on_request" callback function? We could just use an `anyopaque` pointer.
That would solve the generic function prototype problem. But then everyone
implementing such a handler would need to cast this pointer back into - what?
Into the same type that the caller of the handler used. It gets really messy
when we continue this train of thought.
So, in ZAP, I opted for one Context type for all request handlers. Since ZAP is
a library, it cannot know what your preferred Context struct is. What it should
consist of. Therefore, it lets you combine all the structs your and maybe your
3rd parties's middleware components require - at comptime! And derive the
callback function prototype from that. If you look at the [middleware
example](../examples/middleware/middleware.zig), you'll notice, it's really
smooth to use.
**NOTE:** In your contexts, please also use OPTIONALS. They are set null at
context creation time. And will aid you in not shooting yourself in the foot
when accessing context fields that haven't been initialized - which may happen
when the order of your chain of components isn't perfect yet. 😉
## The zig-ception moment
Have a look at an excerpt of the example:
```zig
// create a combined context struct
const Context = struct {
user: ?UserMiddleWare.User = null,
session: ?SessionMiddleWare.Session = null,
};
// we create a Handler type based on our Context
const Handler = zap.Middleware.Handler(Context);
//
// ZIG-CEPTION!!!
//
// Note how amazing zig is:
// - we create the "mixed" context based on the both middleware structs
// - we create the handler based on this context
// - we create the middleware structs based on the handler
// - which needs the context
// - which needs the middleware structs
// - ZIG-CEPTION!
// Example user middleware: puts user info into the context
const UserMiddleWare = struct {
handler: Handler,
// .. the UserMiddleWare depends on the handler
// which depends on the Context
// which depends on this UserMiddleWare struct
// ZIG-CEPTION!!!
```
## 🤯
The comments in the code say it all.
**Isn't ZIG AMAZING?**