-
Notifications
You must be signed in to change notification settings - Fork 67
Middlewares #770
Comments
Other usecases:
What is |
We have a similar approach for the Rust Surf HTTP client, which was inspired by |
@matthewmueller |
Yah I think so. I also think Express app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...')
next() // pass control to the next handler
}) Koa app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
}); Proposal prisma.use(async (query, next) => {
console.log('Before')
const data = await next(query)
console.log('After')
}) Open Questions with Proposal
|
@matthewmueller in the proposal what should be done with |
Great point! What are some use case where you'd want to manipulate the returned data in middleware? One example is perhaps unnesting the data. Something like: https://github.com/paularmstrong/normalizr I also wonder if it's possible to always have access to the latest data from middleware. Then we might not need anything (not sure if this is a good idea.) prisma.use(async (query, next) => {
const data = await next(query)
console.log(data) // would data be the transformed or not?
})
prisma.use(async (query, next) => {
const data = await next(query)
transform(data)
}) |
@matthewmueller good question. If we consider Manipulating the actual returned data; I'm not sure. Perhaps there are some interesting analysis we could do (instrument size if tracing enabled). But in a pattern as general as middleware I would generally err on the side of flexibility. |
I like the idea of The @matthewmueller making your proposal even a bit more koa-like, we could also do this: A:
|
Thanks organizing this discussion @timsuchanek. I'm slightly in favor of C, but one thing I can't reconcile with is what happens to that For example, // called first, then last, koa-style
prisma.use(async (query, next, data) => {
console.log(data) // undefined
await next(query)
console.log(data) // defined
})
prisma.use(async (query, next) => {
query.args.first = 10
const data = await next(query)
return data
}) It's a bit weird. Any ideas? Or maybe I'm missing what you mean @yoshuawuyts by Sidenote Blake has a nice generic implementation of this https://github.com/blakeembrey/compose-middleware. Edit doesn't look promise ready. |
I'd go with C. |
Sounds good! @matthewmueller the Let's think a bit how the API will work: client.use(async (query, next) => {
console.log('1')
query.x = 2
const data = await next(query)
console.log('4')
return data
})
client.use(async (query, next) => {
console.log('2')
query.x = 3
const data = await next(query)
console.log('3')
return data
}) This would print
And the Here is a prototype implementation: https://gist.github.com/timsuchanek/971d39047348e27190f667b8811f9d52 |
Ah excellent. That makes sense now. API looks good to me. Thanks! |
I'm quite confused by having
If we want to keep the |
True. Something like `proceed` or `execute` may sound better, prisma being
pretty different than other libraries mentioned here as a reference for
using `next`.
|
I don't have a huge preference on this. It's an advanced API and I think people will figure it out, but I'm also okay to rename it. Of the options provided, I don't think
|
@Jolg42 I was also confused a bit and agree in Express terminology it doesn't make too much sense, but from the It seems that the main contestants are But as soon as you have multiple middlewares, you literally call the next one. |
The only bit which puts me slightly against I found this middleware approach looking like aspect-oriented programming, where the terminology is more around |
Middlewares are implemented in This is how to use them: import { PrismaClient } from '@prisma/client'
async function main() {
const client = new PrismaClient()
client.use(async (params, next) => {
console.log(params)
return next(params)
})
const data = await client.user.findMany({})
client.disconnect()
}
main() This will log the following: {
args: {},
dataPath: [],
runInTransaction: false,
action: 'findMany',
model: 'User'
} |
Related to the discussion in #669
Users of Prisma Client need a way to hook into Prisma Client.
The system that allows this hooking-in needs to allow the following use-cases:
When having hooks in a system like Prisma Client, we can differentiate between blocking and non-blocking hooks. Blocking would mean - this function needs to succeed before we continue with executing the Prisma Client query. Non-blocking would be a traditional event emitter, for example used for logging. It will be called async and doesn't have to succeed for the client to continue with its business.
Why am I calling this a middleware instead of a hook?
A middleware clearly states, that it is the "blocking thing" that can impact the control flow of what Prisma Client does.
As a first start, we should think about a simple but effective API, that solves as many use-cases as possible, while minimizing the API surface.
The API I suggest for that looks like this:
This hook or middleware will then be called before every query, for example
prisma.user.findMany()
.The params include all the relevant information, like the args, the model and the method being used.
Details of the params have to be figured out.
With this approach, we can cover all the above-mentioned use-cases.
Please comment, if this API would be useful for you and if any use-cases are left out by this.
The text was updated successfully, but these errors were encountered: