Giter Club home page Giter Club logo

mount's Introduction

koa-mount

Mount other Koa applications as middleware. The path passed to mount() is stripped from the URL temporarily until the stack unwinds. This is useful for creating entire apps or middleware that will function correctly regardless of which path segment(s) they should operate on.

Installation

$ npm install koa-mount

Examples

View the ./examples directory for working examples.

Mounting Applications

Entire applications mounted at specific paths. For example you could mount a blog application at "/blog", with a router that matches paths such as "GET /", "GET /posts", and will behave properly for "GET /blog/posts" etc when mounted.

const mount = require('koa-mount');
const Koa = require('koa');

// hello

const a = new Koa();

a.use(async function (ctx, next){
  await next();
  ctx.body = 'Hello';
});

// world

const b = new Koa();

b.use(async function (ctx, next){
  await next();
  ctx.body = 'World';
});

// app

const app = new Koa();

app.use(mount('/hello', a));
app.use(mount('/world', b));

app.listen(3000);
console.log('listening on port 3000');

Try the following requests:

$ GET /
Not Found

$ GET /hello
Hello

$ GET /world
World

Mounting Middleware

Mount middleware at specific paths, allowing them to operate independently of the prefix, as they're not aware of it.

const mount = require('koa-mount');
const Koa = require('koa');

async function hello(ctx, next){
  await next();
  ctx.body = 'Hello';
}

async function world(ctx, next){
  await next();
  ctx.body = 'World';
}

const app = new Koa();

app.use(mount('/hello', hello));
app.use(mount('/world', world));

app.listen(3000);
console.log('listening on port 3000');

Optional Paths

The path argument is optional, defaulting to "/":

app.use(mount(a));
app.use(mount(b));

Debugging

Use the DEBUG environement variable to whitelist koa-mount debug output:

$ DEBUG=koa-mount node myapp.js &
$ GET /foo/bar/baz

  koa-mount enter /foo/bar/baz -> /bar/baz +2s
  koa-mount enter /bar/baz -> /baz +0ms
  koa-mount enter /baz -> / +0ms
  koa-mount leave /baz -> / +1ms
  koa-mount leave /bar/baz -> /baz +0ms
  koa-mount leave /foo/bar/baz -> /bar/baz +0ms

License

MIT

mount's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

mount's Issues

should mount('/image') match /images?

imo mount('/image') should only match /image, /image/, and /image/*.
mount('/image/') should only match /image/, /image/*

right now, mount('/image') is matching /imageklasjdfklajsdfjk.

Cannot read property 'middleware' of undefined

I'm using the example given. But it doesn't work.

const mount = require('koa-mount');
const Koa = require('koa');

const app = new Koa();

async function world(ctx, next) {
  await next();
  ctx.body = 'World';
}

app.use(mount('/public'), world);

Versions
"koa": "^2.5.1",
"koa-mount": "^3.0.0",

[Question] Nested mounting and ctx.mountPrefix

Hi all!

First of all, I would like to say "thanks" for this module ๐Ÿ‘

Backgound + Problem

My problem is: I cannot make redirecting properly because I use nested mounting.
I think the code snippet might look like this:

const Koa = require('koa')
const mount = require('koa-mount')
const Router = require('koa-trie-router')

let app = new Koa()

let router = new Router() // documents router
router.get('/add', function(ctx) {
  ctx.redirect(ctx.mountPath) // <-- my problem is here
})

let middleware = mount('/documents', router.middleware()) // (1) level

app.use(mount('/admin', middleware)) // (2) level
app.listen()

When I start my server and go to http://localhost/admin/documents/add page then the server redirects me to /documents url which does not exists. I mean the target url should be /admin/documents in my case.
My log looks like this:

ctx.path          /admin/documents/add
ctx.mountPath     /documents
ctx.url           /admin/documents/add?foo
ctx.originalUrl   /admin/documents/add?foo

And I see what I need something like ctx.mountPrefix which would be calculated using next simple way:

let index =  ctx.path.lastIndexOf(ctx.mountPath)
ctx.mountPrefix = ctx.path.slice(0, index)

In that case the log will look like this:

ctx.path          /admin/documents/add
ctx.mountPrefix   /admin
ctx.mountPath     /documents
ctx.url           /admin/documents/add?foo
ctx.originalUrl   /admin/documents/add?foo

And I could be write something like this for solving my problem:

router.get('/add', function(ctx) {
  ctx.redirect(ctx.mountPrefix + ctx.mountPath) // --> /admin/documents
})

Question

What do you think about including ctx.mountPrefix in koa-mount package?
Is it make sense or not?

Notices

1

Why I use nested mounting?
I automated mounting and routing which depends on folder structure using koa-architect.

2

Incorrect redirecting is one part of the problem. Also I have the links in templates which forms same way:

<a href="{{ ctx.mountPrefix }}{{ ctx.mountPath }}/edit/{{ item.id }}">edit</a>

Use of async function causes problems in recent versions (but not latest) of node.js

When using the latest version of koa-mount I run into a parsing problem with Node.js understanding the async function syntax when importing koa-mount. I updated to the latest version of node which supports async natively and now things are working.

I wouldn't have expected that - would have expected koa-mount to export a transpiled version that would work without native async support.

next branch??

There was a next branch here for Koa^2.0, it's seemingly vanished.

Is an updated version being maintained elsewhere?

Context's app reference

var assert = require('assert');
var koa    = require('koa');
var mount  = require('koa-mount');

var other = koa();
other.use(function* () {
  assert(this.app === other);
});

var app = koa();
app.use(mount('/other', other));
app.listen(3000);

Had expected the above to work, but it raises. Should the app reference in the context refer to the main/base app or the currently handling app?

Guarenteed ordening of middleware. From outer app down the path

Consider a main app, and an adminApp mounted on /admin both containing middleware.

Currently the order in which middleware executes for all routes inside /admin (i.e: middleware defined on app and middleware defined on adminApp) depends on where app.use(mount('/admin', adminApp)); is executed: before or after app.use(someOtherMiddleware)

Would it make sense to be opinionated about this as follows:

always execute middleware of the outermost app first and travel inwards (i.e: down the path), no matter in which order and when `app.use(mount(...)) calls are done.

Path unmounted if error occurs

When using request logging or error capturing middlewares, the path printed in the log will be incorrect, if any error was thrown from within the mounted app.

Example:

// a casual request logging middleware
app.use(async (ctx, next) => {
    console.log("[>>>] %s %s", ctx.method, ctx.path)
    try {
        await next()
    } catch(err) {
        if (err.status) {
            ctx.status = err.status
        }
    } finally {
        console.log("[%s] %s %s", ctx.status, ctx.method, ctx.path)
    }
})

// mouting another app
app.use(mount('/api', async (ctx, next) => {
    return ctx.throw(403, 'Forbidden')
}))

The result would be:

[>>>] GET /api/user
[403] GET /user

instead of what is expected:

[>>>] GET /api/user
[403] GET /api/user

IMHO it's better to wrap mounts within try-finally blocks, like this diff I created.

Protected path with auth ๐Ÿšง -- Question

Hello everyone ๐Ÿ––

Is it possible to protect a path in the public directory?
Right now I'm developing a project using sessions for the auth, but I want to protect the resources for the no authenticated people.

This is my code rigth now

app.use(mount('/assets', serve(__dirname + '/../public')))

I'm trying to implement a middleware before serve the files but all what I tried failed.

Does anyone have some idea to solve it ?

Thanks ๐Ÿ˜Š

Custom error thrown inside mounted app is converted to native Error

It's fairly common to throw objects inherited from Error, such as this:

class HttpError extends Error {
    constructor(public status: number, message?: string) {
        super(message);
        this.name = 'HttpError';
        Error.captureStackTrace(this, HttpError)
    }
}

and then use middleware to capture and log these errrors and provide user friendly response:

const errorMiddleware = async (ctx, next) => {
    try {
        await next();
    } catch (error) {
        console.error(error);
        if (error instanceof HttpError) {
            ctx.status = error.status;
            ctx.body = {
                message: error.message,
            };
            return;
        }
        ctx.status = 500;
        ctx.body = {
            message: 'Internal server error',
        };
    }
};

Problem I'm facing is that thrown errors are "converted" back to Error and error instanceof HttpError returns false. Is this expected behaviour? I'm throwing them like throw new HttpError(401, 'You are not logged in!');

Needs to be updated for latest koa-router?

The example didnt work immediately out of the box, as it would not load the router and then stumbled over the app.get() so I checked the koa-router examples.

Changing the line at the start from
var router = require('koa-router');
to
var router = require('koa-router')();

and having

app.use(router.routes());

router.get('/handle_facebook_callback', function* (next) {
  console.log(this.query);
  this.body = JSON.stringify(this.query, null, 2);
});

router.get('/handle_twitter_callback', function* (next) {
  console.log(this.query);
  this.body = JSON.stringify(this.query, null, 2);
});

made it run and work for twitter at least.

issue with multiple mounts

Have not had much time to investigate but here is a failing test case

var app = koa();
var a = koa();
var b = koa();

a.use(function *(next){
  yield next;
  this.body = 'foo';
});

b.use(function *(next){
  yield next;
  this.body = 'bar';
});

app.use(mount('/foo', a));
app.use(mount('/bar', b));

request(app.listen())
.get('/foo/bar')
.expect('foo', done);

Missing trailing slash and relative paths

Code used:

app.use(mount("/prefix", serve("โ€ฆ")));

Issue:

  • Case 1:
    When requesting 127.0.0.1/prefix it loads 127.0.0.1/prefix/index.html correctly but all relative resources are being fetched from 127.0.0.1/ instead of 127.0.0.1/prefix.

    Example

  • Case 2:
    When requesting 127.0.0.1/prefix/(note the trailing slash) it works correctly and all relative resources are being fetched from 127.0.0.1/prefix.

    Example

mounted app.keys ignored

per @vinerz on koajs/koa#203:

... I want to set a isolated session path on /connect because this the only place I will use sessions, my app is a stateless API but I have to use sessions for Grant (Facebook & Twitter Sign-in). I don't want to share my API v1 keys with all other endpoints of the server.

var app = koa();

// first session handler
var connect = koa();
connect.keys = ['awesome-key'];
connect.use(session({
    store: redisStore()
}));

app.use(mount('/connect', connect));

// second session handler
var connect2 = koa();
connect2.keys = ['other-key'];
connect2.use(session({
    store: redisStore()
}));

app.use(mount('/connect2', connect2));

If both the parent app and mounted app have dissimilar keys arrays, the context may need a keys-merged keygrip instantiated upon mount descent.

match against original

these should both execute however only the first match will:

app.use('/bar', function *(next){
  yield next;
  this.body = 'bar';
})

app.use('/bar/baz', function *(next){
  yield next;
  this.body = 'baz';
})

Should redirects be absolute?

Since this.url is relative, I do not think that redirects should be absolute.

though currently they are. For example, if you do

website.use(function *() {
   if (this.url == "/proper")  {
       this.body = "Rejoice";
   } else {
       this.redirect("/proper");
   }
})

app.use(mount('/website', website))

Then navigating to example.com/website/not-proper redirects to example.com/proper and not example.com/website/proper

Behavior Regarding "yield next" is Dependent on When Mounting is Done

It seems that koa-mount has different behavior for statements declared after yield next in nested contexts depending on when each mount is done. For example,

const koa = require('koa');
const mount = require(`koa-mount`);

const topApp = koa();
const middleApp = koa();
const bottomApp = koa();

/* If we uncomment these, in either order, only "Bottom app before..." is called. Huh? */
// topApp.use(mount(middleApp));
// middleApp.use(mount(bottomApp));

topApp.use(function *(next) {
    console.log('Top app before next");

    yield next;

    console.log('Top app after next');
});

/* If we uncomment these, in either order, only "Top app before...", "bottom app before..."
   and "Top app after..." get called. */
// topApp.use(mount(middleApp));
// middleApp.use(mount(bottomApp));

middleApp.use(function *(next) {
    console.log('Middle app before next');

    yield next;

    console.log('Middle app after next');
});

/* If we uncomment these, in either order, all 5 console statements get logged. */
// topApp.use(mount(middleApp));
// middleApp.use(mount(bottomApp));

bottomApp.use(function *(next) {
    console.log('Bottom app');

    /* If we uncomment this, no matter where the "mount" statements are, all 5 console
       statements get logged. */
    // yield next;
});

// If we uncomment these, in either order, all 5 console statements get logged. */
// topApp.use(mount(middleApp));
// middleApp.use(mount(bottomApp));

topApp.listen(3000);

My problem with this behavior is that it is unexpected and inconsistent. If it were documented, I might have less of a problem with it, but it would still be inconsistent. The behavior I would expect is that, no matter where the "mount" statements are, "Top app before..."; "Middle app before..."; "Bottom app before..."; "Middle app after..."; "Top app after..."; would get logged, in that order; in other words, the behavior that happens when the "mount" statements are after middleApp's middleware gets defined.

If all middlewares have a yield next statement, all console statements get logged, no matter where the "mount" statements are, so this is not an issue. Ideally, to me, the behavior regarding middleware stacks where any middleware does not have a yield next would be:

1. All statements after `yield next` in middlewares in the same and higher contexts declared
    before the middleware without `yield next` should be executed.
2. In middlewares after the middleware without `yield next`, all statements after `yield
    next` should not be executed if the middleware is in the same or lower contexts.
3. For middlewares in contexts above the middleware without `yield next`, all statements
    should always run, no matter whether the middleware is declared before or after the
    middleware without `yield next`.

For middleware stacks with multiple middlewares without yield next, at different context levels, the behavior should still follow basically the same logic.

Please tell me if any of this is confusing or not right. Also please keep in mind this is only my opinion on an ideal workflow, and although I believe the current flow is not quite right, my suggested flow may not be the best for this middleware. I did not help write the code, I am just a concerned programming citizen who uses this middleware and happens to have noticed this issue.

Question: Mounted apps "compress" middleware

I'm currently attempting to mount my main server app within another app. This is in a "universal/isomorphic" project where one requirement is hot reloading for the server code as well as client code -- mounting the main app achieves this nicely.

One problem I'm facing, is that the middleware of the mounted app such as "compress" are not being applied, despite this mounted app is responsible for handling the requests and serving assets. Also I've had to add app.keys to the parent app for the session to work as expected.

Is this a bug, or is my attempted architecture just too strange?

Here's the code:
https://github.com/tomatau/breko-hub/blob/master/scripts/dev.js#L44

Error when upgrading koa-compose to 3.0

Hello,

when I update koa-compose to the recently released version 3.0 I keep getting the following error:

TypeError: downstream.call(...)[(intermediate value)] is not a function
      at Object.<anonymous> (/projects/intranet_redux/backend/node_modules/koa-mount/index.js:58:23)
      at [object Generator].next (native)
      at Object.<anonymous> (/projects/intranet_redux/backend/node_modules/koa-mount/index.js:60:14)
      at [object Generator].next (native)
      at Object.dispatch (/projects/intranet_redux/backend/node_modules/koa-router/lib/router.js:331:14)
      at [object Generator].next (native)
      at Object.<anonymous> (/projects/intranet_redux/backend/node_modules/koa-mount/index.js:58:23)
      at [object Generator].next (native)
      at Object.serve (/projects/intranet_redux/backend/node_modules/koa-static/index.js:41:14)
      at [object Generator].next (native)

I tried looking at the commits from koa-compose since 2.3 but couldn't figure it out.
I currently use it like this

// protected route - check if user has a valid token & permissions for ressource
app.use(mount('/api/v1/', compose([jwt({secret: publicKey, algorithm: 'RS256'}), permissionCheck(), apiRoutes.middleware()])));

Relative hrefs in mounted web app?

My use case is for koa-mount is mounting apps like this:

app.use(mount('/api', api));
app.use(mount('/apix', apix));
app.use(mount('/app', app));
app.use(mount('/website', webapp));

I found links didn't work in webapp even if made relative because they were incorrectly based by the browser, e.g.:

http://domain.com/website/ serving index.html containing
<link rel="stylesheet" href="css/normalize.css">
results in a browsers request for
http://domain.com/css/normalize.css
and not
http://domain.com/website/css/normalize.css

I don't want to hardcode all links in webapp with the mount prefix or even hardcode <base href="/website/"> so I am using mountPrefix (from PR #19) to add base(href='#{mountPrefix}/') to head in jade... which works...

I assumed the browser would have correctly based the relative refs... but meh...
Am I missing something here?

koa-mount v3

  • use async/await
  • bump deps
  • update examples
  • remove babel

mount on regexp prefix

would it make sense instead of testing this.url.indexOf(path) to test this.url against a new RegExp("^"+path) ?

I would like for example to mount koa apps on subdomains like $user.users.domain.com, $app.apps.domain.com.

I am stuffing the host into the pathname and with the regexp more complex schemes could be implemented. would it be a performance hog compared to simpler routing ?

Doesn't work with Koa 2.0

import 'babel-polyfill'
import Koa from 'koa'
import Router from 'koa-router'
import mount from 'koa-mount'

const api = new Koa
const router = new Router
router.get('/version', async ctx => { ctx.body = {version: '1.0'} })
api.use(router.routes(), router.allowedMethods())

const server = new Koa
server.use(mount('/api', api))

server.listen(8000)
console.log("App running at http://[::]:8000/")
> [email protected] start /home/nate/Code/koa2-test
> node lib/start.js

  koa-router defined route HEAD,GET /version +0ms
  koa:application use dispatch +4ms
  koa-mount mount /api unnamed +2ms
koa deprecated Support for generators will been removed in v3. See the documentation for examples of how to convert old middleware https://github.com/koajs/koa/tree/v2.x#old-signature-middleware-v1x lib/start.js:45:8
  koa:application use - +1ms
  koa:application listen +1ms
App running at http://[::]:8000/

After visiting /api/version

  koa-mount mount /api unnamed -> /version +7s
  koa-mount enter /api/version -> /version +1ms
  koa-router undefined undefined +1ms
  koa-router test /version /^\/version(?:\/(?=$))?$/i +0ms

  TypeError: next is not a function
      at Object.dispatch (/home/nate/Code/koa2-test/node_modules/koa-router/lib/router.js:315:32)
      at Object.<anonymous> (/home/nate/Code/koa2-test/node_modules/koa-mount/node_modules/koa-compose/index.js:25:28)
      at next (native)
      at Object.<anonymous> (/home/nate/Code/koa2-test/node_modules/koa-mount/index.js:58:5)
      at next (native)
      at onFulfilled (/home/nate/Code/koa2-test/node_modules/co/index.js:65:19)
      at /home/nate/Code/koa2-test/node_modules/co/index.js:54:5
      at new Promise (/home/nate/Code/koa2-test/node_modules/core-js/modules/es6.promise.js:193:7)
      at Object.co (/home/nate/Code/koa2-test/node_modules/co/index.js:50:10)
      at converted (/home/nate/Code/koa2-test/node_modules/koa-convert/index.js:17:15)

question on the mount code

I hope this is not a newbish question (fingers crossed ;-)

in koa mount you have

return function *(upstream){
  ...
  // compose
  var downstream = app.middleware
    ? compose(app.middleware)
    : app;

why not write this with a closure to avoid the compose call for every request :

// compose
var downstream = app.middleware
  ? compose(app.middleware)
  : app;

return function *(upstream){
   ...

there is probably a catch-22 that I am not seeing

Notes on updating from koa v1.x to 2.x using koa-convert

I'm embarking on updating a large app using the koa-convert method. I'm wondering if there are any tips on making it work with koa-mount. In koa-convert, docs it says you need to have the latest koa-router installed. Is it true for koa-mount too?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.