How to pass koa-session data to page components in NextJS? - javascript

In my NextJS project, I created a custom server with koa + koa-session, so that I can have some session data per each request, like the code below,
import next from "next";
import Koa from "koa";
import Session from "koa-session";
import Router from "koa-router";
...
const next_app = next({...});
const handle = next_app.getRequestHandler();
next_app.prepare().then(async () => {
const server = new Koa();
const router = new Router();
server.use(Session(server)); // use koa-session middleware
...
router.get("(.*)", async (ctx) => {
console.log("server.js session: ", ctx.session);
...
// create or update session data
ctx.session.custom_data += 123;
// but how to pass ctx.session to handle(), then to page components?
await handle(ctx.req, ctx.res);
});
As said in the comment, how to pass ctx.session data to page components?

Related

ExpressJS API endpoint promises timing out with Sinon unit tests

I am trying to test some API endpoints without actually hitting the database (happens when using supertest). So I am trying to stub/fake the routes and just force a return. The other issue is if I wanted to mock the internal methods that are in the routes, I am having problems doing that as well.
The API is structured like this:
controller.js
export const route = "/named-route";
export const controller = new Router();
controller.get("/:id", async (req, res, next) => {
try {
const post = await getPost(req);
res.status(200).json(post);
} catch (err) {
next(err);
}
});
router.js
import {
controller as myController,
route as myRoute
} from "./controller.js";
router.use(myRoute, myController);
const router = Router();
export default router;
app.js
import router from "./router.js";
const app = express();
app.use(router);
export default app;
My testing attempts
import { expect } from "chai";
import sinon from "sinon";
import request from "supertest";
import app from "./app.js";
import { controller } from "./controller.js";
describe("my controller", function () {
describe("GET", function () {
it("tries to get a post", async function () {
// attempt 1 -- times out
const controllerStub = sinon.stub(controller, "get").resolves({..json});
await request(controllerStub)
.get("/named-route")
.then(async (res) => {
sinon.assert.match(res.body.statusCode, 200);
});
// attempt 2 -- times out
const controllerStub = sinon.stub(controller, "get").callsFake(
async () => new Promise((resolve) => {
resolve({statusCode: 200})
});
);
// same test call as above
// works but hits actual DB, problematic for POST type requests
const response = await request(app)
.get("/named-route");
expect(response.statusCode).to.equal(200);
});
});
});

ReferenceError when using MongoDB Collection variable in external resolver file that was imported via mergeResolvers

This is a totally reduced example to better explain the issue! So when I use the resolver Query getAllUsers, the MongoDB Collection Users is not available in the external resolver file user.js. So when I send that query I get:
ReferenceError: Users is not defined
That's a correct behaviour. But I do not want to include all the resolvers in my index.js, because I have a better modularization in this way. So I have all my typedefs and resolvers in external files like this.
Current file structure
index.js
/graphql
/typdef
user.graphql
/resolver
user.js
The user.graphql schema is correctly working. It is just the user.js that is producing the error when I execute the query with the not available Users variable, as already said.
Here the index.js and user.js.
index.js
import express from 'express'
import cors from 'cors'
const app = express()
app.use(cors())
import bodyParser from 'body-parser'
import {graphqlExpress, graphiqlExpress} from 'graphql-server-express'
import {makeExecutableSchema} from 'graphql-tools'
import {fileLoader, mergeTypes, mergeResolvers} from 'merge-graphql-schemas';
import {writeFileSync} from 'fs'
const typeDefs = mergeTypes(fileLoader(`${__dirname}/graphql/typedef/*.graphql`), { all: true })
writeFileSync(`${__dirname}/graphql/typedef.graphql`, typeDefs)
export const start = async () => {
try {
const MONGO_URL = 'mongodb://localhost:27017'
const MongoClient = require('mongodb').MongoClient;
MongoClient.connect(MONGO_URL, function(err, client) {
console.log("Connected successfully to server");
const db = client.db('project');
const Users = db.collection('user')
});
const URL = 'http://localhost'
const homePath = '/graphql'
const PORT = 3001
app.use(
homePath,
bodyParser.json(),
graphqlExpress({schema})
)
app.use(homePath,
graphiqlExpress({
endpointURL: homePath
})
)
app.listen(PORT, () => {
console.log(`Visit ${URL}:${PORT}${homePath}`)
})
} catch (e) {
console.log(e)
}
}
user.js
export default {
Query: {
getAllUsers: async () => {
return (await Users.find({}).toArray()).map(prepare)
}
}
}
What is the best way to pass the MongoDB or the Users collection to the resolver files. Or is there an even better solution for this issue?
First of all, this is NOT a proper solution, because declaring global variables while outsourcing schema is a bad design at all. But it works out and maybe this way someone gets an idea about how to improve this fix.
So to solve the issue all I had to do is changing the variable from local const to global.
So in index.js const Users = db.collection('user') is rewritten by global.Users = db.collection('user').
Same for the user.js. Here return (await Users.find({}).toArray()).map(prepare) is rewritten by return (await global.Users.find({}).toArray()).map(prepare).

Engine not found for the ".js" file extension

I want to use koa-views with Koa and Koa-Router with Next.js. In previous projects, I had no issues with express but in this project, I have to use Koa. Using its router, I want to render a page: /some/page/:id. Following the same Nextjs way:
router.get('/some/page/:id', async (ctx, next) => {
const actualPage = '/some/page/id' // id.js (not actual name 😝)
await ctx.render(actualPage, {/* could pass object */})
});
That would work if I was using express. With Koa:
const Koa = require('koa');
const views = require('koa-views');
// const render = require('koa-views-render'); <-- I what's this?
[..] // Making things short here
const server = new Koa();
const router = new Router();
// My issue, I'm seeing tutorials using other engines: .ejs etc
// I'm not using any, I only have .js files
server.use(views(__dirname + "/pages", { extension: 'js' }));
Using the same router.get... function as above, I get:
Error: Engine not found for the ".js" file extension
When I go to /some/page/123, I'd expect it to render the file /pages/some/page/id.js. How?
It turns out I do not need any extra modules to achieve this 🙀
Create a function called, ie, routes then pass app and router as a param
const routes = (router, app) => {
router.get('/some/page/:id', async (ctx) => {
const { id } = ctx.params
const actualPage = '/some/page/id'
// Render the page
await app.render(ctx.req, ctx.res, actualPage, {foo: 'Bar'})
}
}
module.exports = routes
Inside your server.js file:
// const routes = require('./routes);
// const app = next({ dev }); // import other modules for this section
// app.prepare().then(() => {
// const router = new Router();
// [..]
// routes(router, app)
// })
The commented out section is a slim down version to make a point in where things should be.

Express baseUrl param not available

Let's say I have the following routes:
// routes.js
import PhotoRoutes from './photoRoutes';
const UserBaseRoute = Router();
UserBaseRoute.use('/api/:userId', PhotoRoutes);
// photoRoutes.js
const PhotoRoute = Router();
PhotoRoute.get('/', (req, res) => {
console.log(req.params);
res.end();
});
export default PhotoRoute;
When I hit /api/123/ I expect to use {"userId: 123}' But I don't. Why is the :userId defined in the baseRoute not passed up?
This is a issue related to nested router.
You need to set the mergeParams of child router as true to access params from parent router.
So try following code:
const PhotoRoute = Router({mergeParams: true});
BTW, this option came with Express version 4.5. For more details, refer to the API document

How to use router in actions.js of vuex?

This is code from main.js where I`ve configured my router
enter code here
const router = new Router({
routes,
mode: 'history',
saveScrollPosition: true
});
sync(store, router);
const app = new Vue({
router,
store,
render: h => h(App )
}).$mount('#app');
Then in vuex/actions.js I`ve action registerUser where I do request to API and if response is success I want to redirect user to main page
enter code here
export const registerUser = ({commit, state}, userData) => {
commit(types.REQUEST_USER_REGISTER);
api.register(userData).then(success => {
const response = success.data;
localStorage.setItem('id_token', response.token);
commit(types.USER_OBJECT, response.user)
//TODO: find better solution, then put router instance into window var
window.router.replace('/');
}, failure => {
})
}
This code above works well, because I`ve set router instance into window var in build.js, maybe somebody know better solution instead of it
Move router declaration to separate file and export it. Then import in main file and any other file which you wish to use it. The api is the same as for $router property on vue prototype.

Categories

Resources