Hey everyone!
I’m still working hard on Gland, and after a lot of refactoring and R&D, I’m excited to share some of the new developments. Gl...
For further actions, you may consider blocking this person and/or reporting abuse
Do we really need another new web framework?
It really depends on what you’re looking for. Express still "works," and NestJS still "works," so if those fit your needs, there's no reason to switch.
But whether we need a new framework or not comes down to what problems you're trying to solve. Gland isn't trying to replace NestJS it’s taking a different, fully event-driven approach that makes code more modular and flexible. If that resonates with your project’s needs, then it’s worth considering. If not, sticking with what already works is totally fine.
This is the proper response. Even if it is the millionth JavaScript framework it is worth the attempt. At best it becomes something popular at worst you learned a lot about the problems that frameworks are trying to solve.
I agree. every new project is an opportunity to learn and grow, even if it's as simple as a basic calculator. For me, even if Gland doesn't become a huge success, the process of building it, the challenges I’ve faced, and the research I've conducted are incredibly valuable. Every experience, including failures, contributes to future successes as long as we learn from them.
Short answer? Yes. Even thousands more, as long people are creating them in the name of innovation.
This one is unique due to its event driven nature
Nice work, liked the idea, I would suggest to go beyond controllers and give more concrete implementation to leverage ore advanced patterns and architectures like domain driven design and clean architecture, I think as framework if u give some guideline and specific patterns to leverage those architectures with the use of events it will be very awesome
Also we need to think how can we build a package that uses events for db interactions and managing transactions
One last thing, we can use this pattern to add extra functionality for saving all interactions happen on the server through events, for example controller emits to service and service emits to db then emits back controller then to channel, we can add one extra functionality that records those events so it can be very useful for logs and traces
Thanks for the great insights! I really like the idea of going beyond just controllers and providing structured guidelines for architectures like DDD and Clean Architecture. That’s definitely something worth exploring—especially how event-driven patterns can naturally fit into these architectures and enhance modularity and maintainability.
I've already been working extensively on this idea and actively exploring ways to integrate these principles into Gland. Additionally, I plan to release third-party modules for database interactions that seamlessly integrate with Gland’s event-driven approach. These modules will provide a clean, decoupled way to manage data without tightly coupling the database logic to the application’s core.
In the future, I will also publish detailed documentation and guidelines on how to build third-party extensions for Gland, ensuring that developers can easily create plugins and integrations that align with its architecture. Of course, this will come after Gland reaches a stable release.
I also love the suggestion about event-based logging and tracing. Implementing a system that tracks and records all emitted events could be incredibly useful for debugging, monitoring, and even replaying events for auditing purposes. This is definitely something I’ll be looking into as Gland evolves.
Really appreciate the feedback! If you have any concrete ideas, specific use cases, or even suggestions on how you'd like to see these features implemented, feel free to share—I’d love to refine Gland’s event-driven approach even further.
Not sure I understand this. You're raising an event users.read.server assuming that some channel is implementing it, but the raiser has no type info to know what the channel will return. Am I supposed to do just project wide searches for strings to figure things out?
Good question! Right now, Gland’s event system is highly dynamic, allowing you to emit events without strict type constraints. While this provides flexibility, as you pointed out, it can make tracking responses harder.
One of the planned improvements is to introduce better type safety for emitted events, ensuring that the caller knows exactly what to expect. This could involve a type mapping system that links events to expected return types, making event-driven logic more predictable.
I’ll definitely take a closer look at this and explore ways to improve type inference in Gland’s event-driven flow. Thanks for the feedback!
In technology I see a recycling of ideas. One technology claims an improvement then comes another with its own baggages. I like the dynamics but the big question is why has not humanity succeeded in bringing a better technology we can all agree on? Is it even possible? What is wrong with us?
I like @m_mdy_m response but here's another perspective. Maybe the problem is just that humans can't remain fixed on one thing. They say change is nature and we die the moment we stop changing and improving.
If all humanity agrees to use one perfect framework then that eliminates variety and subsequently, change. There has to be options.
The problem is not with us, but with our idea of progress. Perhaps progress does not mean reaching a final and perfect technology at all, but rather the same constant movement between ideas, tests and failures.
Great idea! I see a lot of potential for microservices here. Breaking the code into asynchronous, event-driven modules can make it non-blocking and much more efficient, which really helps with the performance across services.
Hey!
This is very interesting approach 👍 with event driven framework, I feel like writing state-machines in the backend would fit nicely.
I have a thought about the ctx.emit. I feel like it should not return result of the channel's event handler (10). Emitter usually should not know or care who handled the event and listeners should act on their own behalf.
Typically, if original emitter needs data, some listener would emit a response event, which the original emitter would be listening to.
Thank you for your thoughtful comment! You’ve raised a really important point about how event-driven systems should work, and I completely agree with the idea that emitters shouldn’t necessarily care about who handles the event or how it’s handled. This is actually one of the core principles behind Gland’s design, and I’d love to explain how we’ve approached this.
In Gland, ctx.emit is designed to give developers flexibility. While it can return the result of the handler, it doesn’t have to. This is intentional because we want to support both scenarios:
When you need a response: Sometimes, you might want to ensure that an event has been handled before moving forward. For example, if you’re processing a payment, you might want to confirm that the payment was logged before proceeding. In this case, waiting for a response from the handler makes sense.
When you don’t need a response: In other cases, like sending a notification, you might not care about the result at all. Here, you can simply emit the event and let the listeners handle it independently.
This flexibility allows developers to choose the right approach for their specific use case. If you prefer a more decoupled workflow where the emitter doesn’t wait for a response, you can design your system so that listeners emit follow-up events, and the emitter listens for those if needed. This keeps the emitter completely unaware of who’s handling the event or how it’s being handled, which aligns with your suggestion.
For example, imagine a scenario where a user signs up:
user:signup
event.user:created
event.user:created
event and sends a welcome email.In this case, the emitter doesn’t need to know about the database or the email service. It just broadcasts the event, and the listeners handle the rest.
That said, I completely understand your concern about the emitter potentially becoming too involved in the handling process. This is something I’ll keep in mind for future updates. For example, we could introduce stronger type constraints or event-response contracts to make it clearer when and how responses should be used. This would help developers avoid unintended coupling while still maintaining the flexibility that Gland offers.
Thanks again for sharing your thoughts! Your feedback is incredibly valuable, and it’s exactly this kind of discussion that helps make Gland better. If you have more ideas or suggestions, I’d love to hear them!
There are many cases where you may care that the event was handled before you moved on to something else, you may not care who has handled the event, but you many care that it has been handled.
Thank you for highlighting that nuance! I completely agree—there are indeed many cases where you might need confirmation that an event was handled before moving on, even if you don't care who handled it. In Gland, we've intentionally designed ctx.emit to be flexible. You can simply emit an event without waiting for a response when that's all you need, or you can structure your workflow so that follow-up events or asynchronous callbacks provide the confirmation you require.
For instance, in scenarios where processing order is critical (like logging a payment or updating a user record), you can have a listener emit a follow-up event to signal that the handling is complete. This allows you to wait for confirmation without coupling your emitter to a specific handler. It’s all about letting you choose the approach that best fits your use case.
What we really need is a new web language.
Gland is a new event-driven JavaScript web framework inspired by NestJS and Angular. It features dependency injection, controllers, and modular channels instead of providers, making apps more scalable and flexible. Using an Event-Driven System (EDS), actions are triggered by events rather than direct responses, enhancing modularity. The framework is lightweight, with minimal dependencies. Gland is still evolving, and feedback or contributions are welcome to refine its approach! 🚀
Time to reset the clock
Good, Keep going. I'm also making my own Framework for web still not have done that much. 😅
Ive been commercially using Blazor for web development for over 3 years now, the freedom of no javascript and all the tooling is amazing, also really fast and responsive. Its 2025 no need for such an old language as Javascript any more and crazy tooling and dependancies.
I love the flexibility of this framework, but I feel as though one way to improve is potentially making it a bit simpler. That would grab the attention of a lot of young devs, and maybe boost your project a bit.
Good job though!!!
Thank you for your constructive suggestion about simplifying the framework. Can you elaborate further on which parts you find complex and what changes could help improve it?
Did you try moostjs? moost.org
No! I did not try it. But now I looked at its documentation. I only saw controllers with packages named "event", but I didn't see any traces of eds in them. What is your eds implementation?
But I was attracted by your package @wooksjs/http-body. It's a good idea, maybe I'll try it.
I'm not quite sure what you mean behind "EDS" and what kind of "eds traces" you are looking for. But I can explain a few base concepts behind wooks/moost projects.
At the very core there is wooks project, which implements events lifecycle with support of "event context" and "router". So each hypothetical event can be defined via some route + handler. Within the handler an event context is available at any place via
useAsyncEventContext
composable (yes, wooks brings "composables" term from frontend like vue, which is pretty similar to react hooks, that's why it's called wooks (W-web, (h)ooks)).Wooks supports adapters that bridges generic wooks events handling pipeline with the specific events sources/targets. For instance
@wooksjs/event-http
is an adapter for http events, so basically an alternative for express and fastify etc.The next level of abstraction is covered by moost. That one brings nestjs-like decorators and wraps wooks and wooks adapters into moost+decorators. Basically you get pretty much nestjs experience but IMHO much less boilerplate and much easier event context handling and much easier custom decorators.
The package
@wooksjs/http-body
that you found interesting is just a set of composables for@wooksjs/event-http
. Those composables are wrapped into decorators within@moostjs/event-http
package.If you want to take a glance of how it is supposed to work all together, just run
npm create moost@latest
, selecthttp
project and answer "no" to all the additional questions (or whatever you like, actually).P.S. I think the workflow adapter for wooks/moost could be very close to what you're doing. And if you have a strong reason why events should be defined your way, I can create another adapter that would support Gland-like events. (you're welcome to contribute as well :) )
Thanks for breaking down how Moost and Wooks work – really cool to see how you’re handling events with composables and adapters. Let me try to explain how Gland’s event-driven system (EDS) works differently, because I think it’s a fun twist on traditional frameworks.
In Gland, controllers don’t directly return responses. Instead, they just fire off events, and separate "channels" pick up those events to do the actual work. It’s like splitting the "what happened" from the "what to do about it." like this:
The beauty here is that the controller doesn’t know (or care) how the user is fetched – it just announces that someone asked for a user. The channel handles the nitty-gritty details. Want to add caching later? Just add another channel listener – no need to touch the controller.
Why this approach?
read:server:error
example).And this isn’t limited to HTTP. The same pattern works for WebSockets, database changes, message queues, or anything else that emits events.
Your Moost/Wooks approach with composables looks super clean for HTTP workflows! Where I think Gland differs is treating all actions as events – even non-HTTP ones. For example, you could have channels that respond to database changes or external API calls using the same pattern.
users:event
,orders:created
, etc.), so they stay organized.Your adapter offer
That’s super generous! I’d love to explore how Gland’s event-first approach could work with Moost’s composable system. Maybe:
I’ll definitely play with
npm create moost@latest
this week – the HTTP body parsing decorators already gave me ideas for simplifying Gland’s validation pipes.Ok, I think I am getting your idea. Although it raises many questions in my mind, like
1. Multiple listeners
What if multiple classes defined as
@Channel('users')
and subscribed for@On('read:server')
?2. What about
providers
?In other words why not just using a
provider
concept likewhere
UsersProvider
is a provider for users that gets instantiated by DI and can be whatever you want.3. Context
What is
Context
type? Is it the same for all kind of events? If no, how is user supposed to ensure proper types?Speaking about wooks/moost
Well, not only for HTTP workflows as http adapter is just one of the adapters for event handling pipeline. You can write websocket or kafka adapter to handle websocket/kafka events in similar matter. Currently there are
cli
andworkflow
adapters that streamline event processing with no relation to http.In moost app I would solve the decoupling you're talking about via
provider
as described above.When you want to add caching and/or logging, you just apply interceptors to event handler:
An interceptor has an initialization phase and 3 optional hooks:
before
hook is triggered after the handler arguments are resolved but before handler is triggered.after
hook is triggered after the handler is processed (with no error)onError
hook is triggered if a handler raised an error. On each step interceptor can overtake or overwrite the response.Gland-like event processing
If you still want to just emit some event from the controller, it is also doable within moost framework.
gland
events@Channel
and@On
that would provide metadata for your adapterHere's how moost code could look like if you had
gland
adapter:As I mentioned earlier the
@moostjs/event-wf
adapter is very close to what you are doing, it is a custom event type that can be raised out of controller. Although it is not about channels, it is about the exact wf implementation.Your approach is definitely clean and well-structured, especially for scenarios where the event pipeline remains tightly coupled to services. But Gland is taking a completely different route—one that is fully event-driven at its core.
Philosophy
Gland isn’t just about emitting events from controllers it treats everything as an event, including HTTP requests, responses, and even errors. Instead of relying on DI-bound providers, Gland decouples execution into autonomous event channels, making every action in the system observable, extendable, and dynamically modifiable.
Key Differences Between Gland & DI-Based Providers
Decoupling Execution from Invocation
Multiple Listeners, No Hard Bindings
@Channel('users')
handlers exist for@On('read:server')
, they all receive the event—making it trivially easy to extend behavior dynamically.No Dependency on Controllers
How This Works in Practice
1. Controller Emits an Event, Does Nothing Else
"user:created"
.2. A Channel Handles the Event
"user:created"
events.@
prefix (@response:send
) is a special namespace indicator, allowing seamless cross-channel communication.3. Another Channel Handles the Response
"send"
events and actually sends the HTTP response.Answers to your questions
1. What if multiple
@Channel('users')
handlers listen for@On('read:server')
?That’s exactly the point! Gland is designed to support multiple listeners per event.
This is something DI-based providers simply cannot do without additional complexity.
2. Why not just use DI providers?
Because DI forces hard dependencies, while Gland is all about loose coupling.
Your example:
UserController
directly depends onUsersProvider
.UsersProvider
requires modifications inUserController
.Now compare this with Gland:
3. What is
Context
, and how does it ensure type safety?Great question!
Context
is a unified request/response wrapper that remains consistent across all event types.Key Features of
Context
You can check out the full
Context
definition here:Gland Context Interface
Gland is not trying to be another DI-based framework. The entire goal is to embrace event-driven architecture natively, making everything modular, decoupled, and reactive.
Would Moost benefit from an event-based extension like Gland’s system? Maybe! Would Gland benefit from some of Moost’s features? Definitely worth exploring! But fundamentally, Gland is solving a different problem—moving away from service calls entirely and treating everything as an event.
It's good to hear an Eds based framework is coming, keep going!
Once I chose to build an application with event driven using angular reactive with rxjs, instead of using redux (which is message driven). Those apps still works well, is been more than 5 years, and many releases happened on top of it. The reason is its well suited for iot devices.
Apart from that, I'm curious to know what change deduction mechanism is been used in your framework.
"What? Again? This stupid country." -- M. Quimby.
Haha, I get it. "What? Again?" -- M. Quimby. But hey, sometimes a new approach can bring fresh perspectives! Gland’s fully event-driven design isn't just another NestJS clone, it’s a different way of thinking about how data flows. Check it out: Gland GitHub.
This has to be a joke.
why?
For anyone who's used/using Kafka, would this make it easier to work with Kafka in some way?
In any case, is it going to simplify seo related limitations with current frameworks? Nice project tho
SEO is a separate concern when it comes to backend frameworks like Gland, NestJS, Express, Koa, Fastify, and others. These frameworks primarily focus on server-side logic, and SEO optimization typically relates more to frontend rendering and how search engines crawl and index the content.
The main goal of Gland is to provide an event-driven and modular system, offering innovative ways to implement logic in backend applications. While SEO is important, it falls outside the scope of what Gland is designed to address.
Nice work! I do something similar to this with NestJS and the event-emitter package. What would you say is the key difference?
Yeah, using
event-emitter
in NestJS is a great way to introduce some level of event-driven behavior. But the key difference with Gland is that in NestJS, events are more of an extra tool, while in Gland, everything is an event it’s not just a feature, it’s the foundation.In NestJS, you still have controllers calling services, DI-based providers, and a structured dependency flow. You might emit events for things like notifications or background tasks, but at the core, your business logic is still function calls inside a class.
In Gland, the entire execution model is event-driven:
For example, in NestJS, you’d do something like:
Even if you use
event-emitter
, you're still relying on direct service calls.In Gland, it’s completely different:
No direct function calls. No dependencies. The controller doesn’t care what happens next.
A
Channel
picks it up:And another channel handles the response:
With this approach, you can add new behaviors without modifying the existing code want caching? Logging? Rate limiting? Just add more listeners.
Is another framework really necessary?
dev.to/m__mdy__m/comment/2m57f
Do events have to be magic strings? Can they be typed somehow?
Cool ideas though!
yes you can