nodejs combime with typescript - node.js

I try to use typescript with nodejs.
I don't understand why it show me error. Please help me fix it.
Here is my code (assum that I Import all module needed)
This is my index.ts file:
import routes from "./routes/routes";
let app = express();
app.use(routes);
This is my routes/routes.ts:
import * as homeRoute from "../apps/home/home.route";
let app = express();
export default function routes() {
app.use("./home", homeRoute); // It show error in path: "./home"
return app;
}
This is my home.route.ts
let router = express.Router();
router.get("/", HomeController.hello); // it show error that property "hello" not exist
// }
module.exports = "homeRoute";
This is my home.controller.ts:
exports.hello = function (req, res, next) {
console.log("Hello word");
}

In your home.controller.ts use an export statement instead:
export const hello = (req, res, next) => {
console.log("Hello word");
}
Then in your home.route.ts you have two ways of importing your controller method:
import * as HomeController from '../path/to/home.controller';
let router = express.Router();
router.get("/", HomeController.hello);
or
import {hello} from '../path/to/home.controller';
let router = express.Router();
router.get("/", hello);

Related

Express router going inside the same route

I have setup 3 different routes which have their own subroutes
router.use('/items', handleItems(app, router));
router.use('/price', handlePrice(app, router));
router.use('/documents', handleDocuments(app, router));
But when i call http://localhost:3000/api/documents/, it is throwing error as if it is calling functions inside /items routes. How can this happen?
After some more debugging i noticed that call is going inside handleItems handler to the /:id route
function handleItems(app, router) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return router;
}
Please refer to express.Router documentation.
In your example you have:
function handleItems(app, router) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return router;
}
The first parameter is not used, and the second parameter is your router object, so basically, you are registering your routes in the same object and you will end up with something like
/items
/create-one
/:id
/price
What you are looking for is something like:
const { Router } = require("express");
const express = require("express");
const app = express();
app.use("/items", handleItems(Router()));
app.use("/documents", handleDocuments(Router()));
function handleItems(r) {
r.get("/tada", (req, res) => res.json({ msg: `Tada` }));
r.get("/:name", (req, res) => res.json({ msg: `Hello ${req.params.name}` }));
return r;
}
function handleDocuments(r) {
r.get("/", (req, res) => res.json({ msg: "Bonjour" }));
return r;
}
app.listen(3000, () => console.log("🚀 Listening"));
In the example above, I registered a "new router" under each path (/items and /documents)
then, I registered each of these at their root paths:
app.use("/items", handleItems(Router()));
With:
function handleItems(r) {
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
return r;
}
I don't know which architecture or what pattern you are using in your application, but if you are not using any you may want to use the following:
Create a routes.js file for each of items, price and documents. For example:
// items.routes.js
const { Router } = require("express");
const router = Router();
router.post('/create-one', itemController.createItem);
router.get('/:id', itemController.getItem);
module.exports = router;
Then in the app.js
const express = require("express");
const app = express();
// load routers
const itemsRoutes = require('./path/to/items.routes.js');
const documentsRoutes = require('./path/to/documents.routes.js');
app.use("/items", itemsRoutes);
app.use("/documents", documentsRoutes);
app.listen(3000, () => console.log("🚀 Listening"));
This might be a cleaner and more predictable pattern to work with in many cases

Using koa-views with TypeScript

I was trying to use koa-views as my renderer, but I kept getting the message from TS:
Property 'render' does not exist on type 'ParameterizedContext<any, IRouterParamContext<any, {}>>'.ts(2339)
My app.js
import Koa from "Koa";
import views from "koa-views";
import indexRouter from "./routes/index"
const app = new Koa();
app.use(views(`${__dirname}/views`, { autoRender: true, extension: "swig" }));
app.use(indexRouter.routes());
index.js - IndexRouter:
import Router from "koa-router";
const router = new Router();
router.get("/", async (ctx, next) => {
ctx.render(); // not done yet tho
await next();
})
export = router;
This is because argument ctx type doesn't have method render(), but in types lib #types/koa-views declared module (see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/koa-views/index.d.ts#L55).
And you can make this:
import { Context, DefaultState } from "koa";
import * as Router from "koa-router";
const router = new Router<DefaultState, Context>();
router.get("/", async (ctx: Context, next) => {
await ctx.render("/path/");
await next();
});

Pass object to routes file used as middleware in nodejs

When my application starts, an object is created that is used throughout the app in multiple files. In my app.js file I am requiring several files that contain my routes. These routes need access to the object created in app.js so I'm trying to pass it to each of these files. An example of one of the files is below.
test.js
const express = require('express');
const router = express.Router();
router.use('/api', router);
router.post('/testRoute', (req, res, next) => {
if(globalVariable.abc) { //globalVariable is the variable created in app.js
//do some logic
}
res.json({
message: "Test Route Success"
})
});
module.exports = router;
app.js
const assessmentRoutes = require('./routes/test');
app.use(assessmentRoutes);
One way I've tried to get the variable passed in is wrapping the routes in test.js in a function that takes the variable like this.
module.exports = function(globalVariable) {
router.post('/testRoute', (req, res, next) => {
if(globalVariable.abc) { //globalVariable is the variable created in app.js
//do some logic
}
res.json({
message: "Test Route Success"
})
});
}
Then in app.js the require changes to:
const assessmentRoutes = require('./routes/assessments')(globalVariable);
When I do it this way I get errors starting the application like app.use() requires a middleware function.
How to I pass the object into my routes?
🙅 Using the global object
One approach, which I don't recommend, is using the global object:
app.js
const assessmentRoutes = require('./routes/test');
global.globalVariable = { abc: ... };
app.use(assessmentRoutes);
test.js
const express = require('express');
const router = express.Router();
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
module.exports = router;
✨ Export a function and pass in options
Another approach is to export a function where you can pass in that as a param, as you did. The problem is that you are not returning the router from that function, that you later need to use in app.js:
app.js
const assessmentRoutes = require('./routes/test');
app.use(assessmentRoutes({ abc: ... }));
test.js
const express = require('express');
const router = express.Router();
module.exports = function(globalVariable) {
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
// 👇 This is what you were missing in your code!
return router;
};
✨ Export the router and a setter function
A similar option would be to export the router object as well as a setter function to initialise globalVariable inside test.js:
app.js
const { assessmentRoutes, initAssessmentRoutes } = require('./routes/test');
initAssessmentRoutes({ abc: ... });
app.use(assessmentRoutes);
test.js
const express = require('express');
const router = express.Router();
let globalVariable = { abc: null };
function initAssessmentRoutes(options) {
globalVariable = options;
}
router.post('/testRoute', (req, res, next) => {
if (globalVariable.abc) { ... }
res.json({
message: 'Test Route Success',
});
});
module.exports = {
assessmentRoutes: router,
initAssessmentRoutes,
};
In your test.js get rid of:
router.use('/api', router);
In your app.js change this line:
app.use('/', assessmentRoutes);
This should works.
APP.JS
app.locals.globalVariable= {type:"Fiat", model:"500", color:"white"};
TEST.JS
if (req.app.locals.globalVariable.type =="Fiat" ) {
.....
}

Routing in express

I have structure like this:
server.ts
routes/
index.ts
homeRoute.ts
In server.ts:
let app = Express();
app.use(router);
In routes/index.ts:
const routes = Router();
export default function router() {
routes.use('/home', homeRoute);
}
In routes/homeRoutes.ts
let homeRouter = Express.Router();
export default function homeRoute(req: Request, res: Response, next: NextFunction) {
console.log('home route');
homeRouter.get('/home', function);
next();
}
My problem is when i call http://localhost:3000, it run to index.ts file ( I console.log some things so I know that), however it don't execute my routes.use('/home', homeRoute).
I don't know why it is. Please help me fix it.
Understand how import and export works. Ideally your code should be something like this.
server.ts
import * as express from 'express';
import {routes} from './routes';
const app = express();
routes(app);
routes/index.ts
import {homeRoute} from './homeRoutes';
export const routes = (app) => {
homeRoute(app);
}
routes/homeRoutes.ts
export const homeRoute = (app) => {
app.get('/home', function (req, res, next) {
res.render('home');
});
}

Grouping routes in Express

We can group our routes like this in Laravel:
Route::group("admin", ["middleware" => ["isAdmin"]], function () {
Route::get("/", "AdminController#index");
Route::post("/post", ["middleware" => "csrf", "uses" => "AdminController#index");
});
Basically, all the routes defined in admin group gets the isAdmin middleware and group name automatically. For example, post endpoint listens to admin/post not /post
Is there any way to do the same thing with Express? It would be awesome because my Laravel routes used to be so clean, whereas my Express routes are a bit messy/duplicated.
This is my routes.js on Express at the moment.
app.get("/admin", [passportConfig.isAuthenticated, passportConfig.isAdmin], AdminController.index);
app.post("/admin", [passportConfig.isAuthenticated, passportConfig.isAdmin], AdminController.postIndex);
Thank you.
Since express 4 you can define and compose routers
const app = require('express');
const adminRouter = app.Router();
adminRouter.use(isAdmin);
adminRouter.get('/', admin.index); /* will resolve to /admin */
adminRouter.post('/post', csrf, admin.index); /* will resolve to /admin/post */
app.use('/admin', adminRouter);
Hope that helps!
Just use before of every group you want to do:
app.use('/admin', AdminMiddleware);
app.get('/admin/route1', ...
app.get('/admin/route2', ...
app.use('/user', UserMiddleware);
app.get('/user/route1', ...
app.get('/user/route2', ...
require('express-group-routes');
var app = require('express');
app.group("/api/v1", (router) => {
router.get("/login", loginController.store); // /api/v1/login
});
In case you don't want to add a prefix but still need to group certain routes you can leave the first parameter and go straight for the function:
require('express-group-routes');
var app = require('express');
app.group((router) => {
router.use(middleware);
});
You can use app.use() - https://expressjs.com/en/guide/using-middleware.html#middleware.application
app.use("/admin",[passportConfig.isAuthenticated, passportConfig.isAdmin],AdminController)
// AdminController:
var express = require('express');
var router = express.Router();
router.get('/', AdminController.index);
// etc...
module.exports = router
https://expressjs.com/en/guide/routing.html#express-router
I found some better solution you can follow this method it's working good
Route file route/user.js
var express = require('express')
var router = express.Router()
const authMiddleware = require('../middleware/auth')
express.application.prefix = express.Router.prefix = function(path, middleware, configure) {
configure(router);
this.use(path, middleware, router);
return router;
}
router.prefix('/user', authMiddleware, async function (user) {
user.route('/details').get(function(req, res) {
res.status(201).send('Hello this is my personal details')
}); //also you can use controller method if you have any
});
module.exports = router //make sure you have to import/use this route in main/server js
You can use an npm module
Express group route
here is a code example from npm official module
var app = require('express');
require('express-group-routes');
app.group("/api/v1", (router) => {
router.get("/login", loginController.store); // /api/v1/login
});
Create the group method
export const group = ((callback: (router: Router) => void) => {
const router = express.Router();
callback(router);
return router;
});
Use the group method
import { group } from './relative/path/to/group/method'
const apiRouter = express.Router();
apiRouter.use('/foo', group((router) => {
router.get('/bar', (req, res) => {
res.send('Hello World');
});
}));
This will create a new GET route to "/foo/bar"
I just wrote this module to solve your problem: https://github.com/benjamin658/express-inject-middleware
You can group your middlewares as an array and pass it to the express-inject-middleware...
For example:
import express from 'express';
import { injectMiddleware } from 'express-inject-middleware';
const app = express();
const authMiddleware = (req, res, next) => {
// some auth logic...
};
const fooMiddleware = (req, res, next) => {
// some foo logic
}
const barMiddleware = (req, res, next) => {
// some bar logic
}
app.use(injectMiddleware(
[
authMiddleware,
fooMiddleware,
],
[
// Passing the app.[METHOD] as the parameter.
app.get('/secrets', (req, res, next) => res.send('secrets'));
// Mount barMiddleware itself
app.post('/secrets', barMiddleware, (req, res, next) => res.send('ok'));
],
));
and this is the result:
app.get('/secrets', authMiddleware, fooMiddleware, (req, res, next) => res.send('secrets'));
app.post('/secrets', authMiddleware, fooMiddleware, barMiddleware, (req, res, next) => res.send('ok'));
in express 4 to grouping your routes, you should create some changes :
seperate route files in multiple files like admin and front
pass the router to the divided route files in routes/index.js file
const express = require('express')
require('express-group-routes');
const router = express.Router()
require('./admin/web.router')(router)
require('./front/web.router')(router)
module.exports = router
in each files we can use groups, middlewares and prefixes, like below
const adminPrefix = 'admin'
module.exports = function (router) {
router.use(`\${adminPrefix}`, [ adminMiddleware ]);
router.group(`/${adminPrefix}`, (router) => {
router.get('/', home.index)
});
}

Resources