Argument of type 'Session | undefined' is not assignable to parameter of type 'Session' - express

Here's a simplified version of my code:
npm install express express-cookies #types/cookie-session #types/express-session
import express from express();
import cookieSession from 'cookie-session';
const app = express();
app.use(cookieSession({
name: 'session',
keys: [
process.env.SESSION_KEY as string
]
}));
const router = express.Router();
const changeSession = (session = express.Session) => {
session.foo = 'bar'
}
router.get((req, res) => {
changeSession(req.session)
});
On changeSession(req.session) I get the error:
Argument of type 'Session | undefined' is not assignable to parameter of type 'Session'.
Type 'undefined' is not assignable to type 'Session'
Same happens when I use app.get instead of router.get
Not sure why express-cookies isn't registering the session object to the request properly.
Here's a link to #types/cookie-session: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/cookie-session/index.d.ts which provides typings for express-cookies
Any help?

The express-cookies typings specify that req.session can be undefined. As I understand, req.session will be defined only if you register the cookieSession middleware with express. So, if for any reason you won't register this middleware (e.g., delete the code which registers it by mistake), req.session will be undefined.
Because there is a possibility that the session middleware may not be registered, type-wise it is correct to expect that req.session may be undefined.
So, using TS you would need to check if req.session is defined before working with it:
if (req.session) {
changeSession(req.session)
}
Or throw an error explicitly if the session is mandatory for a route:
if (!req.session) {
throw new Error('Session is missing.');
}
changeSession(req.session)
Or as last resort, use the exclamation mark to tell TS that req.session is actually defined:
changeSession(req.session!)
But this won't be type-safe.

The error is quite explicit, express.Session may be undefined and the changeSession function is declared to expect a parameter of type Session (not Session | undefined).
If you are sure your express.Session object won't be undefined, you can assign the default parameter value like so
const changeSession = (session = express.Session!) => {
session.foo = 'bar'
}
Notice the exclamation mark (!) after the value. It forces the compiler to forget about undefined values.
This is quite tricky and, of course, you could end having runtime exceptions if this express.Session is undefined.
Hope it helps.

Related

"The original argument must be of type function" ERROR for promisifying client.zrem?

I am making a cron job instance that is running using Node to run a job that removes posts from my Redis cache.
I want to promisify client.zrem for removing many posts from the cache to insure they are all removed but when running my code I get the error below on line: "client.zrem = util.promisify(client.zrem)"
"TypeError [ERR_INVALID_ARG_TYPE]: The "original" argument must be of type function. Received undefined"
I have another Node instance that runs this SAME CODE with no errors, and I have updated my NPM version to the latest version, according to a similar question for this SO article but I am still getting the error.
TypeError [ERR_INVALID_ARG_TYPE]: The "original" argument must be of type Function. Received type undefined
Any idea how I can fix this?
const Redis = require("redis")
const util = require(`util`)
const client = Redis.createClient({
url: process.env.REDIS,
})
client.zrem = util.promisify(client.zrem) // ERROR THROWN HERE
// DELETE ONE POST
const deletePost = async (deletedPost) => {
await client.zrem("posts", JSON.stringify(deletedPost))
}
// DELETES MANY POSTS
const deleteManyPosts = (postsToDelete) => {
postsToDelete.map(async (post) => {
await client.zrem("posts", JSON.stringify(post))
})
}
module.exports = { deletePost, deleteManyPosts }
Node Redis 4.x introduced several breaking changes. Adding support for Promises was one of those. Renaming the methods to be camel cased was another. Details can be found at in the README in the GitHub repo for Node Redis.
You need to simply delete the offending line and rename the calls to .zrem to .zRem.
I've also noticed that you aren't explicitly connecting to Redis after creating the client. You'll want to do that.
Try this:
const Redis = require("redis")
const client = Redis.createClient({
url: process.env.REDIS,
})
// CONNECT TO REDIS
// NOTE: this code assumes that the Node.js version supports top-level await
client.on('error', (err) => console.log('Redis Client Error', err));
await client.connect(); //
// DELETE ONE POST
const deletePost = async (deletedPost) => {
await client.zRem("posts", JSON.stringify(deletedPost))
}
// DELETES MANY POSTS
const deleteManyPosts = (postsToDelete) => {
postsToDelete.map(async (post) => {
await client.zRem("posts", JSON.stringify(post))
})
}
module.exports = { deletePost, deleteManyPosts }

using redis in getServerSideProps results in error net.isIP is not a function

Correct me if I am wrong but getServerSideProps is used to pre-render data on each render? If I use the standard redis npm module in getServerSideProps I get the error net.isIP is not a function. From what I have researched this is due to the client trying to use the redis functions.
I am trying to create an application to where session data is saved in a redis key based on a cookie token. Based on the user Id a database is called and renders data to the component. I can get the cookie token in getServerSideProps but I I run client.get(token) I get the error net.isIP is not a function at runtime.
Am I not using getServerSideProps correctly or should I be using a different method / function? I am new to the whole Next.js world. I appreciate the help.
If I use the same functionality in an /api route everything works correctly.
import util from 'util';
import client from '../libs/redis' // using 'redis' module
// ... my component here
export async function getServerSideProps(context) {
const get = util.promisify(client.get).bind(client);
const name = await get('mytoken') // error `net.isIP is not a function`
return {
props: {
name
},
}
}
// redis.js
const redis = require("redis");
const client = redis.createClient();
client.on("error", function(error) {
console.error(error);
});
module.exports = client
I upgraded to next version 10.0.6 from 9.3 and I do not receive the error anymore.

Dependency Injection (for HttpFetch) at setRoot in main.js Aurelia

I am having trouble getting dependency injection working for my AuthorizerService. Obviously, dep-inj is not ready until after Aurelia "starts", but I wasn't sure how to access it.
main.js:
aurelia.container.registerInstance(HttpClient, http.c());
// set your interceptors to take cookie data and put into header
return aurelia.start().then(() => {
let Authorizer = new AuthorizerService();
aurelia.container.registerInstance(AuthorizerService, Authorization);
console.log('Current State: %o', Authorizer.auth);
Authorizer.checkCookieAndPingServer().then(() => { console.log('Current State: %o', Authorizer.auth); aurelia.setRoot(PLATFORM.moduleName('app')); }, () => { aurelia.setRoot(PLATFORM.moduleName('login-redirect')); });
});
Now the problem is that if I do "new AuthorizerService()" then "this.http.fetch()" is not available in AuthorizerService.js.
Am I meant to pass "http.c()" (which delivers the HttpClient instance) as a parameter inside:
checkCookieAndPingServer(http.c())
or is there another way?
Can I delete "new AuthorizerService()" and just do (I made this up):
aurelia.container.getInstance(AuthorizerService);
Somehow FORCE it to do dependency-injection and retrieve the "registered Instance" of "http.c()"?
I can't just check cookie. I have to ping server for security and the server will set the cookie.
I think this is all sorts of wrong, because I need a global parameter that just is false by default, then it does the query to backend server and setsRoot accordingly. Perhaps only query backend in the "login page"? Okay but then I would need to do "setRoot(backtoApp); aurelia.AlsoSetLoggedIn(true);" inside login module. But when I setRoot(backtoApp) then it just starts all over again.
In other words, when setRoot(login); then setRoot(backToApp); <-- then AuthorizerService instance doesn't have its proper data set (such as loggedIn=true).
EDIT: Better Solution maybe:
main.js:
return aurelia.start().then(() => {
let Authorizer = aurelia.container.get(AuthorizerService);
let root = Authorizer.isAuthenticated() ? PLATFORM.moduleName('app') : PLATFORM.moduleName('login');
console.log('Current State: %o', Authorizer.auth);
aurelia.setRoot(root);
});
Authorizer.js
constructor(http) {
this.http = http;
this.auth = {
isAuthenticated: false,
user: {}
}
}
"this.auth" is no longer static. No longer "static auth = { isAuthenticated: false }" which was some example code I had found.
So now "auth" gets set inside "login" module. But this means the "login" module is displayed every single time the app loads briefly, before being redirected back to "setRoot(backToApp)"
If the class you want to get the instance is purely based on service classes and has no dependencies on some Aurelia plugins, it doesn't need to wait until Aurelia has started to safely invoke the container.
For your example:
aurelia.container.getInstance(AuthorizerService);
It can be
aurelia.container.get(AuthorizerService);
And you should not use new AuthorizerService(), as you have noticed in your question.

ESLint unexpected 'this' error in mongoose schema pre-save

When calling the mongoose schema pre save on my model, I'm reassigning the '_id' value to 'id'.
Everything works as expected and the value is correctly assigned during save, however ESLint is triggering an error for the 'no-invalid-this' config parameter.
What can I do to remove this error without turning off the ESLint config? Is there another way to reassign a value prior to saving the model without using 'this'?
Here is the code sample.
annotationSchema.pre('save', function (next) {
this.id = this._id;
next();
});
ESLINT Error:
Severity: Error
Provider: ESLint
Description: Unexpected 'this'. (no-invalid-this)
If you want to disable the single line for ESLint, adding the following, inline comment:
this.id = this._id; // eslint-disable-line no-invalid-this
Hope this helps!

Logging query string with express-bunyan-logger and graphql

I've made a short, self contained example of a problem I'm having with express-bunyan-logger.
I'm trying to add express-bunyan-logger as middleware, and utilize includesFn to log the query string and body as a custom fields.
const app = express();
const graphqlLogger = require('express-bunyan-logger')({
name: 'graphql-logger',
includesFn: (req, res) => {
const includes = {};
if (req.body) includes.req_body = JSON.stringify(req.body);
if (req.query) includes.req_query = JSON.stringify(req.query);
return includes;
},
});
app.use(graphqlLogger);
Problem is, even for a URL of the form http://localhost:4000/graphql?query=query%20%7B%0A%20%20books%20%7B%0A%20%20%20%20title%0A%20%20%7D%0A%7D, the req_query and req_body fields are always empty. If I inspect the req object that's passed to includesFn in a debugger, it doesn't contain any values for req.query or req.params.
I'm sure I'm doing something wrong here; how do I get the includesFn to log the query string and body correctly?
So, turns out the includesFn is being called twice, once per each request.
First, for the GET request, then you will get only the req.query object not empty.
The second request is the POST request that is being with JSON data, so in order to to get the req.body defined and not empty, you have to do the following:
run npm install body-parser.
include the following in your app.js file:
const bodyParser = require('body-parser');
...
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());