-
-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Middleware API #7190
Comments
A really interesting concept and I'd love to see it implemented in some form or another, but it does concern me that this could fall victim to some similar issues as Structures.extend did which resulted in it's removal - giving users the ability to modify objects and behaviours that discord.js expects to exist in a certain way. |
These are valid concerns. However, middleware is executed after djs has finished processing the raw ws event but before the the actual client event is invoked. This means that only users listening to events will receive changes not discord.js itself. So the only thing that changes is external behavior observed by the consumers of the library. We can compare this to structures.extend which modifies both internal and external behaviors, which can lead to many unintended side effects. |
I might be misunderstanding something from your example then - the translation middleware appears to be extending the Interaction class and overriding the reply method. While I agree this doesn't modify discord.js internal behaviours, how does it ensure they are still executed? Would this act as a form of "outgoing" middleware which must eventually call the base |
I suggested this few months back. I'd like a middleware so I can edit a message payload before its sent. Some use cases: |
We can do a thing simpler like Express with its .use method, that listens to event and can block, pass or update outgoing events ?? |
Feature
Providing addons that are extensions to discord.js has been possible for a great while now, however the methods of doing so aren't very standardized. In addition, the ways of doing extensions for discord.js involve things like client subclassing. Often times, a developer just wants a way to preprocess the data recieved by discord, rather than trying to create a new client based on discord.js.
Instead, a more abstract way to interact with data received by the discord should be made. Introducing discord.js middleware.
What is Middleware?
As the name implies, middleware is code that runs inbetween two other code operations. This means custom tranformations or extra logic can be injected into the standard code execution sequence. All of these injections are done in a standardized way provided by the API.
The idea of middleware is far from uncommon, many popular libraries and frameworks have ways to inject middleware.
These packages include:
In this proposal, I'm proposing an API to inject middleware between the raw websocket event and the
Client.on('event')
public-facing events. This allows developers to easily preprocess data before the itgets emitted to the client, which in turns allows for powerful addons.
How is this different from just attaching listeners to the client?
Event listeners aren't executed in the "middle" of anything. For example if I make a transformation in one client.on() event listener that doesn't affect the types in other client.on() listeners. Basically there's
never a preprocessing phase.
Ideal solution or implementation
To represent the potential power and simplicity of discord.js middleware, I'll set up some scenarios.
Scenario 1 - Translation/Internationalization Middleware
Since discord will be releasing a way to detect locale from an interaction, I want to set up a middleware that injects translation ability into the data object sent to the API.
I can create my middleware like so:
Let's go over some of the things that happened here:
next()
function is a function that signifies that the middleware is complete and accepts data as a parameter to hand-off to the next middleware in the middleware pipeline. This allows data to be conditionally transformed based on conditions.As for the user who wants to use this middleware, the setup is painless.
This code should result in and
reply
content
being translated based on the key you give it. You barely even need to touch the middleware library, and you don't need to learn any new methods for your interactions.Scenario 2 - Custom Command Handler
This one is a classic one, a basic command handler. A command handler can be middleware too. Let's see how we would accomplish that.
Similarly to before, we can easily integrate this into our client:
Just like that our command handler is fully integrated.
Ok but like what's the point of command handlers even being middleware?
Good question! The reason I gave a command handler as an example of middleware is not show that the command handler itself is better, but to instead show how it can integrate with other middleware.
Middleware works Together, not Apart
Ok great I have a translation middleware and a command middleware. But I want them to work together, I don't want to have to pick one over the other?
Good news, middleware moves in a Pipeline
The Pipeline
The pipeline works by passing information from one middleware to another in a sequential fashion. This is one of the purposes of the
next
function. It simply passes the information along to the next middleware in the pipeline. In essence, the pipeline enables the following:Scenario 3 - Using Scenario 1 and 2 Together
Let's make both of these middlewares work together.
All I needed to do was add
TranslationMiddleware
to the front ofCommandMiddleware
inclient.use
. Now it can take full advantage of the translations while not having to change anything about the command-handler middleware.A Visual Representation
Pipeline Suspension
Pipelines can be suspended if
next()
is never invoked. This is useful for scenarios that cannot move on to another middleware. It's also useful for filtering events - for example, you can create explicit message filter:content
through its filter and detects explicit message content.Note that
next()
is never invoked from this middleware. This means that this middleware has suspended the pipeline, this also means that theclient.on('messageCreate')
will never be called.Feedback on implementation details is much appreciated.
Alternative solutions or implementations
No response
Other context
No response
The text was updated successfully, but these errors were encountered: