Closed rynz closed 10 years ago
right now, you can do the same, except with app.callback()
with koa apps. probably won't add it to the examples until we make vhost
its own repo, which we'll do in connect 3 / express 4
@jonathanong Could you please reference an app.callback()
vhost example with koa apps in this ticket in the meantime please?
well it's the same except instead of app.use(express.vhost('example.com', main));
it'll be app.use(express.vhost('example.com', main.callback()));
. this is assuming app
is a connect/express app, and main
is a koa app.
I think I've misinterpreted koajs. Can I not write a web app purely in koajs without express/connect?
yes. that's why it's not an example. vhost
is a connect/express middleware, and there isn't one for koa, yet.
Okay thanks for your help @jonathanong - Do you have a timeline for the koajs vhost middleware? It seems to be the only thing really missing currently and koajs is so awesome I can't wait to use it! Maybe I could have a crack at porting the express version to koajs to be merged with the main repo?
well.. connect's pretty much works without connect. it's just a shame it's a part of connect, which is why we're moving it to its own repo in connect 3. we're not going to add it to koa though, it should still be its own module/middleware.
you could make your own if you want. i think it should sit between the server/apps. the api should look like this:
var vhost = require('vhost')()
var server = http.createServer(vhost)
vhost.use('*.example.com', connect())
vhost.use('example.com', koa().callback())
server.listen()
Ah so no koa-vhost
(https://github.com/koajs/vhost) in the future? That's a shame. Okay I'll roll my own for now and replace it with connect's when it is separated out. Thanks for your support.
there's no need. there's no need for koa-
this and express-
that when they work just fine on their own.
Yeah I agree but I kind of like the idea of koa-
for "officially supported" generator based middleware I guess. If you could please update this ticket / documentation in koajs somewhere when vhost is it's own module/middleware that would be greatly appreciated.
here you go! https://github.com/expressjs/vhostess if you want an example up quickly, open a PR :)
unless you want to do mounting... which I'm -1 (for the most part).
@jonathanong Fantastic thank you. I guess this is where a koa-vhost
relevance / question might come in. I'm curious if generators could be used with vhostess
for more elegant global middleware?
Eg: Express you can do the following:
var app = express();
var expressApp = express();
var secondExpressApp = express();
// Log ALL requests.
app.use(function(req, res, next) {
req.startTime = new Date;
function logRequest() {
res.removeListener('finish', logRequest);
res.removeListener('close', logRequest);
var ms = new Date - req.startTime;
console.log('%s %s - %s', req.method, req.originalUrl || req.url, ms);
};
res.on('finish', logRequest);
res.on('close', logRequest);
next();
});
app.use(express.vhost('express.app.com', expressApp));
app.use(express.vhost('secondexpress.app.com', secondExpressApp));
Which would provide a global middleware for logging all vhost application requests. If vhostess
or an alternative koa-vhost
implementation were supported, we could write elegant generator based global middleware rather than implementing the same middleware in each koa app separately.
Eg: Koa example derived from above express example
var hostess = require('vhostess')();
var koaApp = koa();
var secondKoaApp = koa();
// Log ALL requests.
hostess.use(function *(next) {
var start = new Date();
yield next;
var ms = new Date() - start;
console.log('%s %s - %s', this.method, this.url, ms);
});
hostess.use('express.app.com', koaApp.callback());
hostess.use('secondexpress.app.com', secondKoaApp.callback());
Which achieves the same global middleware with generators. The global middleware isn't only specific to logging and has many use cases, eg: Global Cross Origin Request middleware for APIs etc.
not sure why you need to do that. just app.use(logger)
in each app. you can also bundle a bunch of middleware together using koa-compose to make your life easier.
koa also has this.subdomains
, so i'm not sure how useful a vhost
middleware would be. connect on the other hand does not have req.subdomains
.
maybe i just need an example where vhost as a middleware is required.
I guess koa-compose is a good solution too in certain situations. I was just thinking that for example a single node instance may run multiple APIs for unique products that all depend on some common code / authentication session etc. Eg: Logging, CORS, Authentication, Validation, Error Handling etc.
Being able to mix in global middleware at the vhost level means we can yield at different points of the request/response lifecycle.
Eg:
// koa-logger
app.use(logger());
// koa-cors
app.use(cors());
// koa-etag
app.use(etag());
// koa-session
app.use(session());
// koa-compress
app.use(compress());
// generator version of express-validator
app.use(validator());
// Authentication Api.
app.use(koa.vhost('auth.mycompany.com', authApp));
// Generator for authentication testing shared across all App Apis.
// Eg: Session, token, facebook, twitter etc, one authorisation test for all Apps
// when successfully achieved at `auth.mycompany.com`.
// Responds with generic 401 if the user is not authenticated.
app.use(authentication());
// Other middleware which records metrics of logged in users etc..
// App Apis.
app.use(koa.vhost('awesomeapp.mycompany.com', awesomeApp));
app.use(koa.vhost('fantasticapp.mycompany.com', fantasticApp));
app.use(koa.vhost('realtimeapp.mycompany.com', realtimeApp));
// Common JSON Response errors from Apis.
app.use(function *(next) {
yield next;
// 404
if (null == body && 200 == this.status) {
this.status = 404;
this.body = {code: 404, message: 'Ah, where am I?'};
}
// 500
if (500 == this.status) {
this.body = {code: 500, message: 'Kaboom!'};
}
});
// Error logging.
app.on('error', function(err) {
log.error('server error', err);
});
app.on('error', function(err, ctx) {
log.error('server error', err, ctx);
});
app.listen(3000);
Which is where I think it would be beneficial for a koa-vhost
specific module that behaves like a koa
application to achieve this similar to express
does.
If I was to use koa-compose it would work for the initial generators, however being able to use
a generator mid way through and at the end would not be possible. We would have to be very careful to have the same dependencies at the same points in each app and if they were to be defined globally this way, you can clearly understand common functionality between each.
Thoughts?
in that case, i would just do:
app.use(function*(next){
if (this.host === 'api.awesomeapp.com') yield awesomeApp.call(this, next);
else yield next;
})
since you're doing multiple subdomains, i would do one large switch statement.
although this could be its own repo, i'd personally rather teach people how to do this instead.
of course you'll run into different issues. you'll have to use compose(awesomeApp.middleware)
and use that generator instead. in fact, i wouldn't even bother creating separate apps - just create different bundles of middleware.
note that subapps in koa are not encapsulated or inherited like in express. all the subapps will use the main app's context and keys, and there's no way it can't. to solve this, you'll have to use vhostess
and do separate .callback()
s
If I was to use koa-compose it would work for the initial generators, however being able to use a generator mid way through and at the end would not be possible. We would have to be very careful to have the same dependencies at the same points in each app and if they were to be defined globally this way, you can clearly understand common functionality between each.
i'm not sure what this means
By Generators / Dependencies in that last phrase I meant "koa based" middleware sorry.
Sorry I'm a little confused, could you please show me an example with "global" middleware at the start, between two hosts and at the end?
I'm not sure what you mean by "global" or "at the end". For each vhost in your example, I would use a conditional middleware as in my comment above.
Can you give a different example?
Sure, how would you re-write this using your proposed solution taking into account compose()
issue and not creating separate apps with an example of the same GET /test request outputting different JSON body for each host.
Eg:
// koa-logger
app.use(logger());
// koa-cors
app.use(cors());
// Authentication Api, with with GET /test Route {from: 'auth'}
app.use(koa.vhost('auth.mycompany.com', authApp));
// Placeholder for Authentication Middleware.
// Will only be used if `auth.mycompany.com` was not processed.
app.use(function *(next) {
console.log('Test App Authenticated');
yield next;
});
// App Apis.
// with GET /test Route {from: 'awesomeapp'}
app.use(koa.vhost('awesomeapp.mycompany.com', awesomeApp));
// with GET /test Route {from: 'fantasticapp'}
app.use(koa.vhost('fantasticapp.mycompany.com', fantasticApp));
// Common JSON Response errors from Apps.
app.use(function *(next) {
yield next;
// 404
if (null == body && 200 == this.status) {
this.status = 404;
this.body = {code: 404, message: 'Ah, where am I?'};
}
// 500
if (500 == this.status) {
this.body = {code: 500, message: 'Kaboom!'};
}
});
// Error logging.
app.on('error', function(err) {
log.error('server error', err);
});
app.on('error', function(err, ctx) {
log.error('server error', err, ctx);
});
app.listen(3000);
// assuming all apps are koa apps, "compose" them first.
var compose = require('koa-compose')
authApp = compose(authApp.middleware)
awesomeApp = compose(awesomeApp.middleware)
fantasticApp = compose(fantasticApp.middleware)
// koa-logger
app.use(logger());
// koa-cors
app.use(cors());
app.use(function* (next) {
// Authentication Api, with with GET /test Route {from: 'auth'}
if (this.host === 'auth.mycompany.com') yield authApp.call(this, next);
// Placeholder for Authentication Middleware.
else yield otherAuthentication.call(this, next);
})
// App Apis.
app.use(function* (next) {
switch (this.host) {
// with GET /test Route {from: 'awesomeapp'}
case 'awesomeapp.mycompany.com': return yield awesomeApp.call(this, next);
// with GET /test Route {from: 'fantasticapp'}
case 'fantasticapp.mycompany.com': return yield fantasticApp.call(this, next);
}
// everything else
yield next;
})
// Common JSON Response errors from Apps.
app.use(function *(next) {
yield next;
// 404
if (null == body && 200 == this.status) {
this.status = 404;
this.body = {code: 404, message: 'Ah, where am I?'};
}
// 500
if (500 == this.status) {
this.body = {code: 500, message: 'Kaboom!'};
}
});
// Error logging.
app.on('error', function(err) {
log.error('server error', err);
});
app.on('error', function(err, ctx) {
log.error('server error', err, ctx);
});
app.listen(3000);
your error handler is actually incorrect, but that's an entirely different issue. don't think we have good error handling docs or examples yet
Great thank you, yes I fixed the error handling with try yield / catch error
to catch upstream errors correctly. You mentioned using different bundles of middleware rather than separate koa apps. How would you go about achieving this?
it's fine like this as long as you compose them. probably would be better to keep them like this (using apps) if you ever plan to use them separately. also, doing only app.use()
makes life simpler vs. mixing apps and arrays.
otherwise, i would just do var stack = []
, stack.push(function*(){})
your middleware, then app.use(compose(stack))
.
Okay fantastic thank you for all your help. Would you like me to PR an example for others to learn from?
sure. i was going to do it eventually, but i think your case is a good test case.
it's basically a special type of conditional middleware. the only thing that isn't supported is something like *.example.com
, but in that case, doing this.subdomains.length === 1
is probably better.
Please create an example similar to https://github.com/visionmedia/express/tree/master/examples/vhost as it would be invaluable :)