I'm having a problem during the initialization of a system. In order to initialize this system, I'm calling the index.ts file. This file imports an initializer class called StartServer. This class has an async method called start that initialize all the system database connections and express server. The problem is, somehow Node initialize express files, index.routes.ts, where the routes are defined, and all it dependencies, before starting the index file with the initializer class, generating an error of undefined due to no connections exits at this point.
index.ts file :
import { StartServer } from './services';
StartServer.start();
StarterServer.ts file :
export class StartServer {
public static async start(): Promise<void> {
try {
const configPort: IConfig = config.get('App.port');
await Connections.startDatabaseConnections();
const server = ServerFactory.create();
await server.init();
console.log(`Server is listening at port ${configPort}...`);
} catch (error) {
console.error((error as Error)?.stack);
}
}
}
ExpressServer.ts file :
import routes from '../routes/index.routes';
export class ExpressApp {
private app = express();
private port = config.get('App.port');
routes() {
this.app.use(routes);
}
async init() {
this.routes();
this.app.listen(this.port);
}
}
index.routes.ts file :
import express from 'express';
const router = express.Router();
router.get('/', (_, res) => {
res.status(200).json({ message: 'App listening...' });
});
export default router;
ServerFactory.ts file :
import { ExpressApp } from '#src/services';
export class ServerFactory {
public static create() {
return new ExpressApp();
}
}
In Node, import (and require) are synchronous. As soon as you require a file it is executed.
// a.js
import b from './b';
console.log('a.js');
//b.js
import c from './c';
console.log('b.js');
// c.js
console.log('c.js');
That will output:
c.js
b.js
a.js
In your case, the index.routes.ts will be executed as soon as it's required by ExpressServer.ts. One pattern to avoid initialisation is to export a factory function to perform the setup. You're doing this for your server already!
import express from 'express';
export default function createRouter() {
const router = express.Router();
router.get('/', (_, res) => {
res.status(200).json({ message: 'App listening...' });
});
}
Related
I'm trying to refactor some existing code into an MVC model, and am not sure if I'm messing up the structure, or if I just can't figure out how to pass a variable, but, assuming my structure is good, how do I pass a sequelize instance through an Express route to a controller? Here's my code, hopefully simplified for clarity:
Structure:
src/
db.js
routes.js
server.js
controllers/mycontroller.js
models/mymodel.js
server.js:
'use strict';
import express from 'express';
import Sequelize from 'sequelize';
import { router as routes } from './routes';
import db from './db';
const app = express();
try {
await db.authenticate();
console.log('Connection has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}
db.myTable.sync(); // this works fine
app.use(express.urlencoded({ extended: false }));
app.use(routes); // this, I think, needs to pass db.myTable
app.listen( 3300, () => {
console.log('Listening on 3300');
});
db.js:
'use strict';
import Sequelize from 'sequelize';
import myTableModel from './models/mymodel';
const sequelize = new Sequelize('sqlite::memory');
const db = {};
db.authenticate = () => sequelize.authenticate();
db.myTable = myTableModel(sequelize);
export default db;
routes.js:
import express from 'express';
export const router = express.Router();
import MyController from './controllers/mycontroller';
const myController = new MyController();
... // other routes elided for brevity
router.post('/test', myController.store); // that db.myTable I thought I needed to pass above,
// I think I need to pass again here. Or, alternatively, I could put a constructor into
// MyController and pass it as an arg above when I call 'new MyController', but I still have to
// get it down here into this routes file.
mycontroller.js:
'use strict';
import MyTableModel from '../models/mymodel'; // This was an experiment I tried, but in retrospect,
// it of course makes no sense. I don't need to import the model, I need to have passed the
// instantiated model down here somehow
export default class MyController {
store = async (req, res, next) => {
await MyTable.create({ full: req.body.fullUrl}); // This fails (of course), because
// MyTable.create doesn't exist here.
res.redirect('/');
}
}
So, back to the question: assuming this structure looks correct (feel free to comment on that as well), how do I get that MyTable sequelize object passed all the way through to the controller, so it can do its thing?
Maybe in calling directly the model ?
'use strict';
import { myTable } from '../db';
export default class MyController {
store = async (req, res, next) => {
await MyTable.create({ full: req.body.fullUrl});
res.redirect('/');
}
}
I have written this method here
In redisController.js
export default getAllKeysWithValue = async (req,res) => {
client.keys('*', (err, keys)=> {
if (err) return console.log(err);
for(let i =0;i<keys.length;i++){
client.get(keys[i],function(err,data){
console.log(`key is ${keys[i]} and value is ${data}`);
res.send("LOOK AT YOUR LOG :)")
})
}
});
}
And in the routes.js
import express from 'express'
import getAllKeysWithValue from "../controllers/redisController"
import getRedisValue from "../controllers/redisController";
import postRedisValue from "../controllers/redisController";
var router = express.Router();
router.get("/getAll",getAllKeysWithValue)
router.get('/getKey/:keyRedis',getRedisValue);
router.post('/createKeyValue',postRedisValue);
export default router;
And in the server.js
import express from 'express'
import bodyParser from 'body-parser';
import redisRoute from './routes/routes';
const app = express();
const PORT = 3001;
bodyParser.urlencoded({extended : false})
app.use(express.json())
app.use("/redisLogic",redisRoute);
app.listen(PORT,() =>{
console.log("SERVER r");
})
But iam getting Cannot find module 'C:\Users\me\OneDrive\Desktop\firstTask\routes\routes' imported from C:\Users\me\OneDrive\Desktop\firstTask\server.js
Did you mean to import ../routes/routes.js?
!Structure]1
A default import can be imported in another file as import myDefaultImport from './myFile.js'
You cannot have multiple default exports in one file. So in redisController.js export your functions as
// declare function
export function getAllKeysWithValue() { ... }
export function postRedisValue () { ... }
// the one allowed default export per file
export default myFunction() { ... }
In other file you import them as
import myFunction, { getAllKeysWithValue, postRedisValue } from `path-to-controller`
These are ES6 in/exports and straightforward.
The path and usage of your redisRouter is correct. I guess the error happens cause you have mutliple default exports.
It can be so simple as a piece of cake.
You're using the CommonJS in your back-end side.
Try using ES6 method.
Syntax :
const . . . = require('./')
Hint: If you want to have multiple defaults in JavaScript, you can do it through this syntax:
export default { Function1, Function2 }
Hint:
In NodeJS, It's better to use module.exports and exports.Function = ..
However, There's a difference between them that you can surf the Internet to check it out.
I have a Nodejs with express application. I register my routes using tsoa.
When i add swagger-ui-express to my nodejs application I get the following error Error: TypeError: Router.use() requires a middleware function but got a undefined
I initialize the app as follows:
app.ts
import config from './api/build/config';
import express from 'express';
function startServer() {
const app = express();
require('./api/loaders').default(app);
app.listen(config.port, () => {
console.log(`
################################################
🛡️ Server listening on port: ${config.port} 🛡️
################################################
`);
});
}
startServer();
loaders\index.ts
import {Express} from 'express';
export default (app: Express) => {
require('./express').default(app);
console.log('✌️ Express loaded');
require('./swagger').default(app);
console.log('✌️ Swagger loaded');
};
express.ts
import bodyParser from 'body-parser';
import {Express, Request, Response, NextFunction} from 'express';
import logger from 'morgan';
import { RegisterRoutes } from '../routes';
import cors from 'cors';
export default function startExpress(app: Express) {
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(cors());
//register all routes from the routes generated by tsoa
RegisterRoutes(app);
// catch 404 and forward to error handler
app.use((request: Request, response: Response, next: NextFunction) => {
const error = new Error('404 Not Found');
error['status'] = 404;
next(error);
});
// error handlers
// error handler will print stacktrace only in development
app.use((error: any, request: Request, response: Response) => {
response.locals.message = error.message;
response.locals.error = request.app.get('env') === 'development' ? error : {};
response.status(error.status || 500);
response.send(error.message);
});
}
swagger.ts
import { Express } from 'express';
import swaggerUi from 'swagger-ui-express';
export default function startSwagger(app: Express) {
try{
const swaggerDocument = require('../build/swagger.json');
var options = {
explorer: true
};
app.use('/swagger', swaggerUi.server, swaggerUi.setup(swaggerDocument, options));
}
catch(error){
throw new Error(error);
}
}
I also tried to use import statements instead of require, but it doesn't make a difference. Why does my compiler suddenly say my app Express object is a Router object and how do I set up nodejs with express and registering routes in different files?
To answer your question...
Why does my compiler suddenly say my app Express object is a Router object...
It doesn't. You can see a reference to the Router.use() function because it is just eventually called inside the app.use() function.
The actual issue here as mentioned in the error message is the middleware function being undefined. This is because inside your swagger.ts file, you specified swaggerUi.server as the middleware function but it needs to be changed to swaggerUi.serve.
import { Express } from 'express';
import swaggerUi from 'swagger-ui-express';
export default function startSwagger(app: Express) {
try{
const swaggerDocument = require('../build/swagger.json');
var options = {
explorer: true
};
// The problem was here... change server to serve
app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));
}
catch(error){
throw new Error(error);
}
}
I have controller homeController where i do export let index
import { Request, Response } from "express";
export let index = (req: Request, res: Response) => {
console.log("home");
};
In app.ts
import * as homeController from "../src/modules/home/controllers/home.controller";
const app = express();
...
app.get("/", homeController.index);
tslint passed it but whe i do npm start (npm run serve), i getting the error
internal/modules/cjs/loader.js:589
throw err;
^
Error: Cannot find module '../src/modules/home/controllers/home.controller'
path to /home.controller is right
The problem is that in your HomeController you are exporting a function, the index function. So, when you import that file you are already importing the function so
import * as homeController from "../src/modules/home/controllers/home.controller";
const app = express();
...
app.get("/", homeController);
might work
If you want to do homeController.index
In you homeController module you should do
import { Request, Response } from "express";
let index = (req: Request, res: Response) => {
console.log("home");
};
module.exports = {
index
}
I'm trying to load ExpressJS routes from a Typescript class. The idea is that this should eventually be dynamic routes. But I'm already stuck at defininghard coded routes in a class.
My index.ts js looks like this:
import generic = require('./generic');
import Generic = generic.Generic;
class Test {
private app:any;
private port:number = 3000;
constructor() {
this.app = express();
new Generic();
this.app.listen(this.port, () => {
console.log('Listening on port ' + this.port);
});
}
}
export = Test;
new Test();
Then my generic.ts looks like this. I'm trying to define another route from this class:
module Generic {
export class Generic {
constructor() {
console.log('In generic');
var router:any = express.Router();
router.get('/', function (req, res) {
res.setHeader('Content-Type', 'application/json');
res.status(200).send("AAA");
});
}
}
}
export = Generic;
When I run my application then I see the message In generic appear in console. But when I load my browser it says:
Cannot GET /
So for some reason it's not really registering the route from the Generic class. What am I doing wrong?
Take a look at the sample on Microsoft's github page. Rather than using express.Router, they simply use get and import the function from an external module. It has worked pretty well for me.
app.ts
import * as express from "express";
import * as routes from "./routes/index";
var app = express();
app.get('/', routes.index);
routes/index.ts
import express = require("express")
export function index(req: express.Request, res: express.Response) {
res.render('index', { title: 'ImageBoard'});
};