I am building an application backend in TypeScript & NodeJS.
I have kept multiple routes logic in separate files(in single folder for each service) as :
> signup.ts // export { router as signupRouter };
> signin.ts // export { router as signinRouter };
> signout.ts // export { router as signoutRouter };
I need to do this for all the services in future.
I need to combine these routes so that I can specify them as a single entity in the index.ts file.
Something like:
import <object_that_combines_all> from './routes/<some_file_for_object>';
app.use('/api/users', require('./routes/users/<object_that_combines_all>'));
.
.
and so on
So that I don't have to mention each route explicitly in the index.ts file.
There is a simpler way using the router.use() method in which we can combine the individual endpoints.
Assuming your file structure looks something like this -
/src
/routes
/users
- signup.ts
- signin.ts
- signout.ts
index.ts (your main app entry point)
You can utilize the router.use() method provided by express.Router().
First, add a new index.ts file to the /routes/users folder to combine the multiple endpoints.
// /routes/users/index.ts
import * as express from 'express';
import { signupRouter } from './signupRouter';
import { signinRouter } from './signinRouter';
import { signoutRouter } from './signoutRouter';
const router = express.Router();
router.use(signupRouter);
router.use(signinRouter);
router.use(signoutRouter);
export {router as usersRouter};
Then in your main app entry point index.ts, do the following -
// /src/index.ts
import * as express from 'express';
import { usersRouter } from './routes/users/index.ts';
const PORT = process.env.PORT || 4000;
const app = express();
...
// Add your single entity entry point like this
app.use('/api/user', usersRouter);
...
app.listen(PORT, () => console.log(`Listening on port ${PORT}`)
To do that, you'll need to dynamically discover the filenames (readdir or similar) and use dynamic import to import them. Dynamic import (import()) returns a promise of the module namespace object for a module.
For instance, using the fs/promises version of readdir, something along these lines (off-the-cuff, some tweaking probably required):
import { readdir } from "fs/promises";
const directoryPath = "/path/to/the/files";
const rexIsRouterFile = /^.+\.ts$/i;
async function loadRoutes(app) {
const names = await readdir(directoryPath);
for (const name of names.filter(name => rexIsRouteFile.test(name))) {
const { router } of await import(`${directoryPath}/${name}`);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^−−−−−−− dynamic import
app.use('/api/users', router);
}
}
Note that this assumes you have a TypeScript loader setup (for instance, as with ts-node). If you don't, you'll need to compile those TypeScript files to JavaScript before this runs, then change .ts to .js in rexIsRouterFile.
Related
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.
After receveing help about using Express, I continued to follow tutorials about Node.js. I'm at a point where i'm building my own routes in controllers to create a REST API. I have two files, app.js and /controllers/account-api.js.
Here's my app.js shortened (i deleted the parts that were not used my my test), and the line that is returning me some issues.
import express from 'express';
import exphbs from 'express-handlebars';
import * as accountApiRoutes from './controllers/account-controller.js';
import * as bodyParser from 'body-parser';
var app = express();
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
app.use(bodyParser.urlencoded({extended:true}));
app.use(accountApiRoutes); // ISSUE HERE, i tried ('/account-api', accountApiRoutes) too
app.get('/', function(req, res)
{
res.redirect('/server-home');
});
app.get('/server-home', function(req, res)
{
res.render('server-home');
});
app.listen(1337, '127.0.0.1');
console.log('Express Server running at http://127.0.0.1:1337/');
And here's my ./controllers/account-api.js shortened again to give the main elements that causes the issue :
import express from 'express';
const apiRouter = express.Router(); //ISSUE HERE
var accounts = [];
accounts.push( { code: 1, name: 'Pierrette', adress: 'Sur la Lune'} );
// =========== API ROUTES =========== //
// GET
apiRouter.route('/produit-api/produit/:code')
.get( function(req, res, next) {
var codeSended = req.params.code;
var account = findAccountInArrayByCode(codeSended);
res.send(account);
});
// =========== METHODS AND FUNCTIONS =========== //
// GET
function findAllAccounts() {
return accounts;
}
function findAccountInArrayByCode(codeSended) {
var accountFound = null;
for(i in accounts)
{
if(accounts[i].code === codeSended)
{
accountFound = accounts[i];
break;
}
}
return accountFound;
}
module.exports = { //ISSUE HERE
getApiRouter: function() {
const apiRouteur = express.Router();
return apiRouter;
}
}
The problem is.. This code returns me "module" is not defined.
I use Node.JS with Express and Handlebars.
For what I saw online, when using "app.use", it requires a function. And module.exports too. I tried various solutions, like this one :
account-api.js
const apiRouter = function() { return express.Router() }
...
module.exports = apiRouteur;
The problem is that it changes the type of apiRouteur, when calling apiRouteur.get from IRouter to () => Router, and the routes break.
I don't know how to arrange the code to make the module.exports returning a function that works, or if the problem is not even about the type of value returned, but if I'm missing dependancies, etc...
Thanks for your help.
EDIT : With the explanations I got, I replaced all my ES6 calls to commonjs imports. But it doesn't solve the problem. Now it's "require" that's not define.
I was stuck firstly by "require is not defined", and the solution I was given by reading old SO threads about it, the answer was regularly to use ES6 imports...
ack to the begining I guess ! Maybe I miss something in my project?
Your problem is this line app.use(accountApiRoutes); and you are using a mix of ES6 and commonjs modules.
To fix the module imports (as you are using .js files not .mjs) change all your ES6 imports i.e import * as xyz imports to commonjs imports const x = require('...');
The accountApiRoutes is an object but not a Router object.
To fix you just need to pass the router object to the app.use function.
So you will need to make a couple of changes based on what you have supplied above.
// ./controllers/account-api.js
const express = require('express');
...
module.exports = { //ISSUE HERE
getApiRouter: function() {
return apiRouter; // you have already defined the router you don't need to recreate it
}
}
Properly pass the Router object to the express app.
const express = require('express');
const exphbs = require('express-handlebars');
const bodyParser = require('body-parser');
const accountApiRoutes = require('./controllers/account-controller.js');
...
app.use(accountApiRoutes.getApiRouter());
You could also just set module.exports to your configured router in your account-api.js and then you could pass it directly to app.use as you have already done in your server above. Either way should work. To can do that as follows:
// ./controllers/account-api.js
const express = require('express');
...
module.exports = apiRouter;
And in your server.js
const accountRouter = require('./controllers/account-controller.js');
app.use(accountRouter);
I use TypeScript for my Express app. I distributed the registration of certain endpoints across several other modules. These modules import main.ts and register them (see depFoo.ts). Unfortunately they don't seem to be registered and because app starts listening before deppFoo.ts executed it's registration!?
What is a proper way in TypeScript to fix this Heg-Enn problem? Can I register a callback that gets executed once TypeScript resolved all modules?
main.ts
depFoo.ts
main.ts
export const app: express.Application = express();
app.listen(3000, function () {
console.log("App is listening on port 3000!");
});
depFoo.ts
import { app } import "./main.ts"
app.get(...);
This tutorial gives a great explanation of the router style system with express TS. One of the cleaner ways to do it is to have a routes directory with files similar to
import * as express from "express";
export const register = ( app: express.Application ) => {
app.get( "/", ( req: any, res ) => {
res.render( "index" );
} );
};
and then in your main app file you can use
import * as routes from './routes'
...
const app = express()
routes.register(app)
Which makes your main file a whole lot cleaner and your endpoints more modular.
I am new in Typescript. I am using express js and I have the folowing problem. When I run the server it appear '...../routes.ts' has no exported member 'router'. I ve been searching but I didnt succeed. These are my files.
In my src/index.ts
import "reflect-metadata"
import * as express from "express"
import { router } from "./routes"
async function main() {
const app = express()
app.get('/api',router)
app.listen(3000, () => console.log("Server running"))
}
main()
In my src/routes.ts
import * as express from "express";
var router = express.Router();
router.route('/user').get((req, res) => res.send("Hello world"));
module.exports = router;
This isn't a Typescript issue, you aren't exporting router as a named export so the { ... } syntax won't work.
Given it appears to be the default export, you can use
import router from './routes.js'
Alternatively, if you are using a transpiler that supports the export syntax, you could export your router as a named export i.e.
export const router
This would allow you to import via { router }.
Folder structure
index.js
modules/users/users.server.routes.js
modules/users/users.server.routes.js
export default (app) => {
app.route('/api', (req, res) => {
res.send('User routes working');
});
};
index.js
import express from 'express';
import mongoose from 'mongoose';
/*
* For iterating through files using a regex
* */
import glob from 'glob';
let files = glob.sync('app/**/*.routes.js');
files = files.map((file) => {
import(file);
});
const app = express();
const config = {
app_port: 8080
};
/*
* Test route
* */
app.get('/', (req, res) => {
res.send('Voila!!');
});
Above code is giving an error!
'import' and 'export' may only appear at the top level
What I want to achieve is, once the server gets started, all the routes should be imported. (Live require(--route file--))
I don't want to declare all the routes in a single routes file like this.
// mount user routes at /users
router.use('/users', userRoutes);
// mount auth routes at /auth
router.use('/auth', authRoutes);
I want each route to be defined from the root of the API.
How to achieve this in ES6 way?
Thank you all!