Comments (28)
i personally do const { request, response } = this
all the time and always try to use those. i try to do the same in open source projects so its easier to understand.
PRs so that the following would be easier to do would be welcomed:
app.use(async ({ request, response }) => {
await Promise.resolve('something');
response.body = 'hi';
})
from discussions.
I can almost guarantee that if you move to (req, res, next)
you will get bogged down with "why doesn't this [meant-for-express] middleware work". Having just started with koa, I like the(ctx, next)
signature. At the very least, the signature says "hey, this is koa middleware" and disambiguates from connect-style middleware. The ctx
is also a great place to expand information that might be shared between middleware (eg. ctx.user
). After only several days of testing, the destructuring does not bother me as I don't use request and response "everywhere" as implied earlier in this thread. I have even started doing things like this:
app.use(async (ctx, next) => ctx.user = { name: "Ryan" });
app.use(async ({user, response}, next) => ...)
Going back to the point of this thread, I definitely think the shortcut aliases are confusing. I'd even pull it into a koa-shortcuts
module or put it behind a flag so people can turn it on if they want. This way the "shortcut" stuff can stay separate and documented all in one place - allowing users to be in control of their own confusion.
from discussions.
totally agree, I stop to use ctx.body ect...
Newcomers on my koa projects are always lost with that kind of aliases.
Maybe ctx.req.x/ctx.res.x or cyx.in.x/ctx.out.x
from discussions.
I can definitely agree with the ambiguity aspect. I also think getters/setters (mostly setters) are kind of bad practice, like doing response.type = "text"
still warrants double-checking documentation since you may be wrong about its existence, vs a method call.
Koa definitely straddles that line between work-horse and magic, perhaps a bit too much magic.
from discussions.
I found this confusing too. The other one that confuses me is ctx.req
v.s. ctx.request
(same for response). And the shorter one, which is easier to write, is not the one that is used more often.
from discussions.
Been working for awhile with Koa now, but It doesn't seem to confused me at all.
from discussions.
I do think ctx
is a great abstraction on koa, especially in enterprise development. It's very helpful to make tracer
impl in a easy way, most like ThreadLocal
in Java.
It can store many information about the current request context and use it in the following process flows. (e.g.: request a down stream PRC with the tracer data)
More detail can find on "Context on egg"
from discussions.
As a newcomer, I find the shortcut confusing. I don't think it adds value.
Should I use ctx.type
or should I use ctx.response.type
?
Since there can be ambiguity on some properties, I decided to prefer the long form.
On a larger team, I would now have to educate the team on that decision and maintain it through code reviews and through team turnover. If I decide not to enforce consistency on the choice, I still have to educate the team that inconsistency is ok.
It is better to not have a choice.
es6 de-structuring offers a much better shortcut. There is less cognitive load
for those coming to Koa from other frameworks who are used to separate request and response parameters.
In submitting documentation PRs, I would have liked to written this example:
app.use(async ({request, response}, next) => {
if (!request.accepts('xml')) ctx.throw(406);
await next();
response.type = 'xml';
response.body = fs.createReadStream('really_large.xml');
});
This does not work because now I've lost throw
in de-structuring.
What drew me to Koa was its minimalistic sensibility. Context aliases seem directly opposed to that.
I'd recommend:
- go "full delegation" on context in v2.x to support de-structuring
- update all documentation to use de-structuring instead of aliases (I volunteer to update docs)
- indicate the aliases are deprecated in v2.x with scheduled removal in 3.x.
- Move the alias setup to a separate module, required in 2.x, call it yourself in 3.x
from discussions.
I agree that ctx
is actually a nice pattern to have. In the Express world since there's no obvious place to put things people augment the req/res
objects in non-standard ways, and then you have to dig through all of the existing properties on them to figure out where things are stored.
ctx.request.params
isn't bad to type at all, it's explicit. And if it feels too long, you can always destructure. (I honestly destructure in most of my middleware already like @jonathanong.)
So yeah, +1 to this idea. It would be awesome if the shortcuts were deprecated (maybe with logging in NODE_ENV == 'development'
) and removed in the next major version.
from discussions.
@jonathanong Isn't this solved in Koa 2.0 since the context is now passed in as the first parameter instead of overriding this?
from discussions.
FWIW, I've changed my thinking now, and I think ctx
is no longer necessary either. Instead you can always just use a getter like:
const body = await getBody(req)
β¦and no longer need a place to store things, since they can be retrieved on demand. And anything that is expensive or a singleton can be cached in a WeakMap
with the req
itself as the cache key.
micro
does this and it works really well to make things explicit.
from discussions.
It makes me confused too.
Sometimes, have to check the API to use them.
from discussions.
Many of these can only be read or write, ctx.method, ctx.path, ctx.status for example don't make sense for a response. A few are definitely ambiguous (ctx.body, ctx.headers), the rest are fine IMO, you only need to memorize 3 or 4.
Even ctx.headers
doesn't even really apply since you'd never do ctx.headers['Content-Type'] = foo
in Node, they have to be normalized before setting.
I don't disagree that it's potentially confusing in those cases but if you don't like the Koa sugar you can access the request/response directly as you mentioned, just a compromise.
from discussions.
@ShimShamSam The example provided is of Koa@2 signature. That's why he can deconstruct the argument (ctx
).
from discussions.
π I think that's fair, though if we're destructuring we might as well just pass (req, res)
in the signature, I'm not a fan of ctx.request.stuff
or destructuring always personally.
Ideally node core was more usable out of the box in terms of what Koa introduces so we wouldn't have to touch any of that. It's pretty ugly having to replace them since it doesn't help node form a thriving middleware ecosystem around primitives that everyone agrees on.
Especially now with async/await allowing for more generic middleware that don't use callbacks. Go's net/http is very Koa-like but I think it's a great model of this if anyone wants inspiration.
from discussions.
@tj considering what you wrote, would a getter/setter function on context which throws/validates on use be a good idea?
app.use(async (ctx, next) => {
// pseudo fn-name
ctx.safeGet('typ') // throws
ctx.safeGet('type') // get from KoaRequest by default
ctx.safeSet('type', 'application/json') // set on KoaResponse by default
ctx.safeGet('response', 'typ') // -> throws
ctx.safeSet('request', 'type', 'hack')
})
from discussions.
Or just req.type
and res.type()
etc, getters are maybe reasonable if they always have a value, but if they're sometimes null then that's ambiguous as well, they probably shouldn't even be in JS to be honest haha.
from discussions.
Radical.
At first I thought Context
didn't have much purpose once it moved from this
to a parameter and that (req, res)
would represent "least surprise." However, Request
and Response
don't extend http
anyway, so there is always going to be some surprise.
Would (req, res)
be worth the turmoil of another signature change? Is that really on the table?
I can imagine down the line, someone opening an issue "Why so much de-structuring?"
I have a hard time imaging someone opening an issue "merge request and response" after a split.
Recap of options
- Prefer de-structuring in the docs, use aliases (included or separate) or long form if you don't like de-structuring.
- Prefer long form in the docs, use aliases (included or separate) or de-structuring if you don't like long form.
- Change signature to (req, res, next).
- Prefer aliases in the docs, de-structure or use long form if you don't like them.
The setters take some getting used to, but I don't mind them.
I haven't tried this, but could one do something like this to catch mis-assignment to properties?
if (process.env.NODE_ENV !== 'production') {
app.use(async (ctx, next) => {
ctx.freeze();
ctx.request.freeze();
ctx.response.freeze();
await next();
});
}
from discussions.
Yea I think splitting is nicer than basically forcing the destructuring. Originally I (obviously) wanted this
to be the focus so context made a bit more sense there, I still prefer this
over ctx
since I would never have closures in a route, always await'd calls. having callbacks in the routes is definitely an anti-pattern but hey.
Clarity-wise ctx.body(val)
and friends makes the most sense, not as "cute" but whatever. One potential solution is to have a proxy which yells at you if you use the wrong setter/getter haha.... but yeaaaahhh that's just getting weird, or maybe freezing like you mention, I've never actually used freezing, all these features seem weird in JS.
from discussions.
safeGet/safeSet to mustGet/mustSet like MustXxx in golang π
from discussions.
I haven't tried this, but could one do something like this to catch mis-assignment to properties?
@JeffRMoore once you start to enforce things like that β you hit the opposite case (when you want to unfreeze stuff) π Why? For example, you're in the middle of a huge code refactoring and you need to do some weird things with variable / header names. Including request mangling...
I remember working in Python:Flask where Request was freezed by default and it was a PITA for corner cases like that.
I've never actually used freezing, all these features seem weird in JS.
π
from discussions.
Hehe, freezing is weird, but generators and accessors are not? :)
I see the point of .mustGet(field)
, but .field
or .getField()
is more friendly to static tools like flowtype, TypeScript, autocomplete, etc.
from discussions.
Accessors are weird, generators are fine, I don't think they add to the list of error-prone JS features personally.
from discussions.
if we have any chance to change that (e.g. koa v3), strongly hope to change signature be (request, response, next)
.
from discussions.
Request / response aliases are bad idea
I'm agree. Because we need keep in mind the mapping.
Furthermore, for example, if we parse request params
ctx.params = ctx.request.params = {...}
we should care about two references.
https://en.wikipedia.org/wiki/Single_source_of_truth
For me, ctx
by itself is good because I can pass it to another function/middleware very simple.
from discussions.
I still think if Node's core had this useful stuff it would be the best for everyone, then all middleware could easily share the (req, res)
signature. Granted you can already do that, you just lose a lot of the convenience.
from discussions.
There is another signature possibility along the lines of eliminating context
that offers some additional error detection possibilities.
The request
and context
objects could be combined with the delegation removed. response
is left alone except for getting a duplicate throw and assert and cookies. Then This PR could be used to enable response
to be the value of the promise returned by next
.
So you'd end up with either
app.use(async (request, next) => {
const response = await next()
response.body = 'Hello World'
return response
});
or
app.use(async (request, next) => {
const response = await next()
response.body = 'Hello World'
});
Requiring the return is more verbose, but allows the framework to detect this case where await
is left off:
app.use(async (request, next) => { next() })
It doesn't have the familiarity of (req, res)
but maybe makes sense given that you have to pay attention to keeping the Promise
chain intact anyway.
from discussions.
I have never used any of the context shortcut methods as they make the code harder to understand, I'd advise others to avoid also.
from discussions.
Related Issues (20)
- Move koa modules into this organization HOT 1
- ctx.req.on and ctx.req.pipe limitations ?
- Hosting mp3 file HOT 2
- Why Koa ctx.body not working in if statement? HOT 3
- EPIPE error occurs when the server returns mp3 file. HOT 1
- Cannot make middleware to coerce type of query parameter HOT 1
- Question on inheriting Koa HOT 1
- How to exclude api router? HOT 1
- Access to XMLHttpRequest at 'https://localhost:3443/images' from origin 'http://localhost:4200' has been blocked by CORS policy
- Conditional middleware
- Hosting mp3 file
- Either 404 or wrong content HOT 1
- [koa] Unexpected exit with code 0 during an http request HOT 4
- Using both koa-compress and koa-static , extremely slow when visit static files from 127.0.0.1 or localhost οΌfrom public address is fastοΌ, why? HOT 2
- Brotli compress is slow HOT 2
- @fl0w becomes @miwnwski HOT 1
- How can I catch EADDRINUSE? HOT 5
- Uncaught `TypeError: Cannot read property 'hasHeader' of undefined` error
- Respond to Koa request immediately, continue middleware chain HOT 5
- Why the request url parameter + is turned into a space? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from discussions.