Handling invalid requests with koa-router and koa-mount - koa

My app uses koa-router, and it mounts the router using koa-mount, as in:
var Router = require('koa-router');
var mount = require('koa-mount');
app = koa();
var router = new Router();
router.get('/foo', function *() { this.body = { success: true }));
app
.use(mount('/api', router.middleware()))
.use(RedisBoot)
;
The desired behavior is that a route that starts with api that isn't defined should give a 503 or something. Instead, the request falls through to the RedisBoot handler. I've tried adding additional rules at the start and end of router but for some reason they do not seem to be being called.
I notice that newer versions of koa-router supported nested routes and have some other nice features, so maybe it would be easier to get this working now without koa-mount?

Instead of mounting the router directly to the parent app, create a secondary koa app (they're not heavyweight constructs so it shouldn't be a performance issue).
In this sub-app, add the router as the first middleware, add a catch-all handler as the second middleware, then mount the sub-app to the main app under /api.
var Router = require('koa-router');
var mount = require('koa-mount');
var app = koa();
var subApp = koa();
var router = new Router();
router.get('/foo', function *() { this.body = { success: true }));
subApp
.use(router.middleware())
.use(function *() { ... throw 503 or something... })
;
app
.use(mount('/api', subApp))
.use(RedisBoot)
;

Related

Dynamically proxying of several pages in NuxtJS

In my NuxtJS application I has a folder with html pages, that can be added/deleted in any time from outside (/static/pages/page1.html, /static/pages/page2.html, ...) and I got a mapping to real uri's for this pages
{ '/foo': 'page1.html', '/bar': 'page2.html', ... }
I know I can use #nuxtjs/proxy, but it requires to rebuild an app every time mapping changes. I also know I can use nginx's rewrites for this, but changing it's config every time is painful too.
I also tried using 'pages/_.vue' file, read .html in component and place it's content to html using v-html, but files contains full html page (w/ scripts), and nuxt throw and error in this case, 'cos v-html don't allow using js (or maybe another reasons, which I can't understand)
How can I make dynamic proxy for this in NuxtJS?
For someone looking for answer for same question
Solve this by creating simple server middleware
in /pages_proxy/index.js:
const path = require('path');
const { Router } = require('express');
const express = require('express')
const app = express()
const router = Router()
router.get('*', async (req, res, next) => {
const pages = { '/foo/': 'page1.html', '/bar/': 'page2.html', ... }
const page = pages[req.path];
if (page) {
res.sendFile(path.join(__dirname, '../static/pages', page));
} else {
next();
}
});
app.use(router)
module.exports = app
in nuxt.config.js
serverMiddleware: {
'/': '~/pages_proxy'
},

Why is my koa application timing out when testing a route with Jest/Supertest

Summary of Problem
Receiving : Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout when trying to run a test with Jest and supertest.
Specs
Koa2 project, Jest/Supertest testing, Babel 7.9.0 recommended configuration
What I've tried
I have a simple test from the same file running which I omitted from the code below for brevity. I've also tried sending an HTTP request from the browser - this file is imported & 'listen'ed in a server file. The request is failing because it is blocked by a CORS policy - I think this is a problem for another day and isn't affecting my test timing out.
I also tried removed .callback() from the supertest(..) call:
const response = await supertest(app).post('/save-material');
at which point I get TypeError: app.dress is not a function.
Here is the content of my test file:
process.env.NODE_ENV = 'test';
const app = require('../../src/server/app.js')
const supertest = require('supertest')
test('save-material returns response', async() => {
const response = await supertest(app.callback()).post('/save-material');
expect(response.status).toBe(200);
expect(response.body.status).toBe('success');
expect(response.body.msg).toBe('Material saved')
});
Here is the content of the imported file (app.js) from above:
require('#babel/register'); // not entry point - but is entry point for some tests
const Koa = require('koa');
var Router = require('koa-router')
const app = new Koa();
const router = new Router();
router
.post('/save-material', async(ctx) => {
ctx.response = {
status: 'success',
msg: 'Material saved'
}
return ctx;
})
app.use(router.routes());
app.use(router.allowedMethods());
module.exports = app;

How can I pass values between koa-router routes

I wanted to move the authentication procedure from all routes into one route (koa-router provides the all() middleware for all methods on a router for this). However, in the process, I decode a token whose decoding I need for further execution. How can I access this decoded token from another route?
const Router = require('koa-router');
const router = new Router({ prefix: '/test' });
router.all('/', async (ctx, next) => {
//decode
await next();
})
router.get('/', async ctx=> {
// Here I need to access decoded, too
});
the Koa Context object encapsulates the request, response and a state object, along with much more. This state object is the recommended namespace where you can pass data between middleware.
Modifiying the provided example gets:
const http = require('http')
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
const router = new Router({ prefix: '/test' })
router.all('/', async (ctx, next) => {
// decode token
const x = 'foo'
// assign decoded token to ctx.state
ctx.state.token = x
await next()
})
router.get('/', async ctx=> {
// access ctx.state
console.log(ctx.state.token)
})
app.use(router.routes())
http.createServer(app.callback()).listen(3000)
Navigate to http://localhost:3000/test and see the decoded token logged to the console.

How can I access the RethinkDB context set up by the Hapi plugin?

According to this Hapi JS plugin's documentation, The RethinkDB library and the connection are bound to the server context inside handlers on this.rethinkdb and this.rethinkdbConn.
However, I'm not being able to access those, only using request.server.plugins etc. Does anyone know why? Is it because the context set inside a plugin with server.bind() is only available inside the plugin itself ? Thanks!
EDIT:
Here's is some of the code on index.js
const plugins = [{register: require('hapi-rethinkdb'), options: {url: //url here}]
server.register(plugins, () => {
server.route(require('./routes'))
server.start()
})
The on routes.js, let's say a have a route like this:
{
method: 'GET',
path: '/list-of-things',
handler: function(request, reply) {
// I have to use it like this:
const r = request.server.plugins['hapi-rethinkdb'].rethinkdb;
const connection = request.server.plugins['hapi-rethinkdb'].connection;
// Because this throws undefined:
const r = this.rethinkdb;
const connection = this.rethinkdbConn
}
}

Node.js + Express + Cross-domain scripting

I am testing on localhost, and using module CORS
strange ... POST ajax call to get access_token ( using module node-oauth2-server ) succeed
but then trying to access resources, ajax calls are failing ( Origin ...not allowed by Access-Control-Allow-Origin)
I guess there is something wrong in my routing ...
var express = require('express'),
cors = require('cors'),
http = require('http'),
https = require('https'),
fs = require('fs'),
oauthserver = require('node-oauth2-server'),
categories = require('./models/category'),
products = require('./models/product');
var server_options = {
key: fs.readFileSync('/etc/ssl/self-signed/server.key'),
cert: fs.readFileSync('/etc/ssl/self-signed/server.crt')
};
var app = express();
//app.use(require('cors')());
app.use(require('browser-logger')());
app.use(cors()); // automatically supports pre-flighting
app.use(app.router);
app.configure(function() {
var oauth = oauthserver({
model: require('./models/oauth_mongodb'),
// See below for specification
grants: ['password', 'refresh_token'],
accessTokenLifetime: 2592000, // 30 days
debug: true
});
app.use(express.bodyParser()); // REQUIRED
app.use(oauth.handler());
app.use(oauth.errorHandler());
});
// categories
app.get('/categories/:id', categories.findById);
app.get('/categories', categories.findAll);
// products
app.get('/categories/:id/products', products.findByCategory);
app.get('/products/:id', products.findById);
app.get('/products', products.findAll);
https.createServer(server_options,app).listen(8000, 'node_ssl_server.local');
http.createServer(app).listen(3000, 'node_ssl_server.local');
Just a guess, but I think your ajax calls are probably originating from:
http://localhost:3000/
and pointing at
https://localhost:8000/
You may need to do a browser refresh to avoid the cross domain error.

Resources