Writing a test REST api with NodeJs for learning purposes.
Currently I only have 1 route which accepts a parameter (which works fine).
I'm using an express router to route the GET request to my controller.
All of the routing is working as expected.
My ServiceController currently has a ctor function which accepts 2 parameters. Both of these parameters are passed into the ctor function by the router during instantiation.
In the ServiceController ctor I store the parameters in to fields.
The issue is, when I try to access these fields in a class method I'm getting a "TypeError: Cannot read property 'exec' of undefined".
I did write both of these values to the console to ensure that the ServiceController was receiving these values correctly (which it is).
So, i'm unsure why im getting this error when I attempt to access either "this.exec" or "this.logger" in the get method.
Router
import express from 'express';
import { exec } from 'child-process-promise';
import ServiceController from '../controllers/serviceController';
let routes = (logger) => {
const router = express.Router();
let controller = new ServiceController(exec, logger);
router.route('/status/:name')
.get(controller.get);
return router;
};
module.exports = routes;
ServiceController
export default class ServiceController {
constructor(childProcess, logger) {
this.logger = logger;
this.exec = childProcess;
}
get(req, res) {
if (!req.params.name) {
res.status(400).send('A service name was not provided');
} else {
this.exec(`sc query ${req.params.name}`).then(result => {
if (result.stderr) {
this.logger.log.warn(`stderr: ${result.stderr}`);
}
const regex = /STATE\s+:\s+\d+\s+(\w+)/;
let [, status] = result.stdout.toString().match(regex);
if (!status) {
throw new Error('Status query unsuccessful');
}
let service = {
name: req.params.name,
status: status
};
res.json(service);
return service;
}).catch(error => {
this.logger.log.error(`${error.name} ${error.message}`);
res.status(500).send('An error occurred while executing command');
});
}
}
}
It's a problem of this context. You use the get method on a context which is not your ServiceController instance.
Bind the method on your instance:
router.route('/status/:name')
.get(controller.get.bind(controller));
Or you can also define an arrow function in the ServiceController class.
Related
i have a class with a Constructor and Async functions.
I have done module.exports so that i could call my Class from my GUI.js file and in my GUI.js file, i have required that class, and everything works fine.
But inside my class, im trying to do this ipcRenderer.send('message', 'Hello');
And im getting this error:
TypeError: Cannot read property 'send' of undefined
is it possible to remote the ipcRenderer in my GUI.js?
Thanks.
i have required the module in my main file, and in my renderer file it sends ipcRenderer.send('startMyClass');
And in my Main file: ipcMain.on('startMyClass', (event, args) => {
const client = new myClass();
client.Start();
})
This is my class/index.js file that is being required in my main file.
const request = require('request-promise');
const cheerio = require('cheerio');
const { ipcRenderer } = require('electron')
class myClass {
constructor() {
this._jar = request.jar();
this._request = request.defaults({ jar: this._jar });
}
async Start() {
await this.Test();
};
async Test() {
ipcRenderer.send('myMessage', 'Hello');
}
}
module.exports = myClass;
EDIT:
If i dont require it, and have the whole class in my main file, i can do event.sender.send('myMSG', 'hello');
But i want to do it in my class, that's NOT in the same file as my main.
Sending message from Main to Renderer should be done by sending to a specific webContents. That's why event.sender.send('myMSG', 'hello') works, while ipcRenderer.send not. The latter sends from Renderer to Main as stated in docs (and also, cannot be accessed from Main process as your Error told you it's undefined).
As explainded in ipcMain's docs you should access the webContents you want to send to and call send on that.
So to correct your code you can
Pass a reference to the main window to myClass and call send on that
class myClass {
constructor(args) {
// ...
this.mainWindow = args.win
}
// ...
async Test() {
this.mainWindow.webContents.send('myMessage', 'Hello');
}
}
Or send to the actually focused window (BrowserWindow.getFocusedWindow()) if that fits your needs
class myClass {
// ...
async Test() {
BrowserWindow.getFocusedWindow().webContents.send('myMessage', 'Hello');
}
}
Here is the scenario:
I have 3 files (modules):
app.js
(async () => {
await connectoDB();
let newRec = new userModel({
...someprops
});
await newRec.save();
})();
The app.ts is the entry point of the project.
database.ts
interface ConnectionInterface {
[name: string]: mongoose.Connection;
}
export class Connection {
public static connections: ConnectionInterface;
public static async setConnection(name: string, connection: mongoose.Connection) {
Connection.connections = {
...Connection.connections,
[name]: connection,
};
}
}
export async function connectToDB() {
const conn = await mongoose.createConnection('somePath');
await Connection.setConnection('report', conn);
}
model.ts
const userSchema = new mongoose.Schema(
{
..someprops
},
);
const userModel = Connection.connections.report.model('User', userSchema);
export default userModel;
What I am trying to do: I need to have multiple mongoose connections, so I use an static prop called connections in Connection class (in database.ts); every time that I connect to a database I use setConnection to store the connection in mentioned static prop, so I can access it from every module in my project by its name which is report in this case.
Later, In model.ts I use Connection.connections.report to access the connection report to load my model!
Then, When I run app.ts I get the following error which is logical:
const aggregationModel = Connection.connections.report.model('User', userSchema)
^
TypeError: Cannot read property 'report' of undefined
The reason that causes this (I think) is, while loading imported modules in app.ts, .report is not declared because the app.ts isn't run completely (connectoDB() defines the .report key).
The codes that I have mentioned have been simplified for preventing complexity. The original app is an express app!
Now, How should I solve this error?
Thanks in advance.
You can wait for the connection to finish before using it if you change up your class slightly.
const connection = await Connection.getConnection()
const model = connection.example
...
class Connection {
...
public static async getConnection() => {
if (!Connection.connection) {
await Connection.setConnection()
}
return Connection.connection
}
}
I have a project in nodejs using sequelize.
I am tring to do a FindAll inside a model but I am getting this error:
Cannot read property 'findAll' of undefined
I import the models like this:
const db = require('./index');
And in the model hooks I am trying to do this:
hooks: {
afterFind: async function(User){
let ranks_db = await db.rank.findAll({ });
// let rank = await checkRank(User.exp);
User['exp'] = {
value: User.exp,
rank
};
return User;
}
}
The findAll it is working in the controller but not in the model
. I want to know if it is posible to do a FindAll inside a model
It is likely that you have a circular dependency between your models and that db.rank has not been populated yet when the Model you are showing here is constructed. Instead of importing the models via require you can reference them from the sequelize variable that is passed into the Model.
module.exports = function Model(sequelize, DataTypes) {
return sequelize.define('user', { /* columns */ }, {
hooks: {
afterFind: async function(user) {
const ranks = await sequelize.models.rank.findAll();
// user.ranks = ranks;
},
}
});
}
I have a couple questions regarding Class files. I have the below Class
class CouchController {
constructor(couchbase, config) {
// You may either pass couchbase and config as params, or import directly into the controller
this.cluster = new couchbase.Cluster(config.cluster);
this.cluster.authenticate(config.userid, config.password);
this.bucket = cluster.openBucket(config.bucket);
this.N1qlQuery = couchbase.N1qlQuery;
}
doSomeQuery(queryString, callback) {
this.bucket.manager().createPrimaryIndex(function() {
this.bucket.query(
this.N1qlQuery.fromString("SELECT * FROM bucketname WHERE $1 in interests LIMIT 1"),
[queryString],
callback(err, result)
)
});
}
}
my problem is how can I go and access the doSomeQuery function from outside the class file? Inside there is no issue accessing the function but I need to be able to call it from outside.
I tried something like this
const CouchController = require("../controllers/CouchController")(couchbase, config)
let newTest = new CouchController
doing so newTest never exposes the doSomeQuery method.
Also what are the limitations of a method ? Can it only be a simple one or can it be async and use promises etc ?
There are 2 main things that you should consider with the following problem.
Export it properly first. I'm not sure if you meant to leave this out, but it's important to export the class for use outside as a require. Here is the NodeJS exports documentation if you wish for the technical details.
// common module default export
module.exports = class CouchController {
constructor(couchbase, config) {
// You may either pass couchbase and config as params, or import directly into the controller
this.cluster = new couchbase.Cluster(config.cluster);
this.cluster.authenticate(config.userid, config.password);
this.bucket = cluster.openBucket(config.bucket);
this.N1qlQuery = couchbase.N1qlQuery;
}
doSomeQuery(queryString, callback) {
this.bucket.manager().createPrimaryIndex(function() {
this.bucket.query(
this.N1qlQuery.fromString("SELECT * FROM bucketname WHERE $1 in interests LIMIT 1"),
[queryString],
callback(err, result)
)
});
}
}
The class initialization is slightly incorrect. You can see the docs on this here. You can change your require and initialization to...
const CouchController = require('../controllers/CouchController');
const newTest = new CouchController(couchbase, config);
// now you can access the function :)
newTest.doSomeQuery("query it up", () => {
// here is your callback
})
If you were using ES6 modules or typescript you could export something like...
export default class CouchController {
// ...
}
... and import something like...
import CouchController from '../controllers/CouchController';
const newTest = new CouchController(couchbase, config);
You need to instantiate the class after importing it
Change the following
const CouchController = require("../controllers/CouchController")(couchbase, config)
let newTest = new CouchController
to
const CouchController = require("../controllers/CouchController")
let newTest = new CouchController(couchbase, config)
Also you need to export your class like this
export default class CouchController {
and then access method like this
newTest.doSomeQuery(...)
I figured it out after some forth and back, part of my problem was the fact that for some reason visual studio code did not show me the method which threw me off. Manually typing made it ultimately avail.
Here is my Class and i actually moved the config and couchbase itself into the class file so no need to pass it anymore.
const couchbase = require("couchbase")
const config = require("../config/config")
class CouchController {
constructor() {
// You may either pass couchbase and config as params, or import directly into the controller
this.cluster = new couchbase.Cluster(config.cluster);
this.cluster.authenticate(config.userid, config.password);
this.bucket = this.cluster.openBucket(config.bucket);
this.N1qlQuery = couchbase.N1qlQuery;
}
getDoc2(docID){
return new Promise((resolve,reject)=>{
this.bucket.get(docID ,(err, result)=>{
if(err) return reject(err);
return resolve({docID,result});
});
});
}
}
module.exports = CouchController
And here is how i call my Class now and connect to the backend to fetch my data.
const CouchController = require("./controllers/CouchController")
let newTest = new CouchController
const test= async()=>{
let { docID, result } = await newTest.getDoc2("grid_info::20b05192-79e9-4e9d-94c9-91a4fc0a2765")
console.log(docID)
console.log(result)
}
I'm using Typescript 2.3.4, Node.JS 8.0.0, and the Feathers framework (version 2.1.1). I am making an express route that uses a service, and when I try to use the service after grabbing the singleton instance on the feathers app, Typescript is throwing an error TS2532: Object is possibly 'undefined' error, even after an explicit type guard.
routes.ts
import feathers = require('feathers');
export default function(this: feathers.Application) {
const app = this;
app.post(
'/donuts',
async (req, res) => {
const someService = app.service<any>('predefined-service');
if(typeof someService === "undefined" || !authService) {
res.redirect('/fail');
return;
}
try {
let data = someService.create({test: 'hello'});
console.log(data);
res.redirect('/success');
} catch(err) {
res.redirect('/fail');
}
}
}
I've also tried writing someService!.create... but that didn't work either.
From the Feathers typings:
interface Service<T> extends events.EventEmitter {
create?(data: T | T[], params?: Params, callback?: any): Promise<T | T[]>;
The create method itself is optional (for whatever reason). If you're sure the method exists you could put the ! operator after, like this:
let data = someService.create!({test: 'hello'});