What's the difference between these 2 ways of calling express.router? - node.js

I'm new to Node/Express, and while reading docs and doing some tutorials, I've found that the Express Router is initialized in 2 different ways. Is there any meaningful change between them?
Method 1:
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json({'Title': 'HELLO!'});
});
module.exports = router;
Method 2:
const { Router } = require('express');
const router = Router ();
router.get('/', (req, res) => {
res.json({'Title': 'HELLO!'});
});
module.exports = router;

Method 1:
It requires the express module and then calling the Router() function.
Method 2:
This functionality is called Object destructuring in Javascript. We're basically splitting that Particular function(Router) from the express object.
Refer this for more information.

Method 1: The code first imports the Express application object, uses it to get a Router object.
Method 2: Here ES6 object destruction is used to import multiple child modules(or single in this instance) from one single module.
Method 2 is when you need to import multiple child modules from a single module with a single line of code. Else you'll have to include the whole object or import each child module separately.

Related

require is not define / module.exports is not define with node.js and Express

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);

module exports does not export the variable

I'm trying to creat a rest API, using the file routes.js (code bellow) but the module exports does not wotk, it does not export the const routes, if i put route it try to export, but routes makes it just dont wotk, i dont know why, this is the entire code until now
const routes = express.Router();
routes.get('/', (req, res) => {
return res.json({ message: 'test' });
});
module.exports = routes;
i tried using insominia to debug this, it work on the index file, normaly, but when i use the routes file, it just does not work (code of index bellow)
const express = require('express')
const mongoose = require('mongoose')
const routes = require('./routes')
const app = express()
mongoose.connect(**url mongoose censored**)
app.use(express.json)
app.use(routes)
app.listen(3333)
someone can help me, please
express.json is a function that returns a middleware function when you call it (allowing you to pass arguments that will be used in the middleware function). So, you need to use it like this:
app.use(express.json());
Also check if the require path is correct, you might need to provide first argument to app.use when using router
app.use('/',router)

How to customise get method with different queries in Express.js with Node.js?

I'm currently building a REST API using Node.js with Express.js and I'm quite new to this technology. The following code shows the get method to a list of councils stored in MongoDB.
const { Council } = require('../mongoose-models/council');
const express = require('express');
const router = express.Router();
router.get('/', async (req, res) => {
const query = req.query;
const councilsList = await Council.find(query);
if (!councilsList) return res.status(404).send('No councils found.');
res.send(councilsList);
});
module.exports = router;
From my previous experience when developing REST API using java, I can customise different queries by implementing different methods with their own paths. For example:
#Path("findByCouncilName/{councilName}")
#Path("findCouncilsNotInMyArea/{longitude}/{latitude}")
And within each method, I can then write different logics. However, in Express.js, it seems that I have to implement all these different logics into one block. It seems not flexible and how can actually implement it? Furthermore, does the query must be same as the key name in MongoDB? What if I want to filter the results based on a specified index element in a nested array in a document?
For your routes:
#Path("findByCouncilName/{councilName}")
#Path("findCouncilsNotInMyArea/{longitude}/{latitude}")
If you are to implement them in express, you can split them into different blocks actually.
Instead of listening to '/' and try to handle everything inside, you can try this.
const express = require('express');
const router = express.Router();
router.get('/findByCouncilName/:councilName', async (req, res) => {
const councilName = req.params.councilName;
// Your logic goes here
res.send();
});
router.get('/findCouncilsNotInMyArea/:longitude/:latitude', async (req, res) => {
const longitude = req.params.longitude;
const latitude = req.params.latitude;
// Your logic goes here
res.send();
});
module.exports = router;
You can use it like lets say:
router.get('/:councilName', async (req, res) => {
Then use the parameter in the route with :
req.params.councilName
Express doc is your friend
https://expressjs.com/en/guide/routing.html
Here is everything you should know about express routing.
You can specify individual logic for every pat-method pair, and again, use general as needed.
You need to be aware of path order in which Express resolves them, eg. first path to match will will be executed.

Express Router - different routes with same suffixes

I have 2 routes:
/api/system/list - list my systems
/api/system/:systemId/books/list - list books of current system
And different file for each API:
systemAPI.js:
const list = router.get('/list', Validate(Validation.list), listHandler)
return {
routes: [list]
}
bookAPI.js:
const list = router.get('/list', Validate(Validation.list), listHandler)
return {
routes: [list]
}
Finally, use the above routes:
express.use('/api/system', systemAPI.routes)
express.use('/api/system/:systemId/book', bookAPI.routes)
The problem is, when I'm entering list-books API (/api/system/:systemId/books/list), its actually calling the list-systems API (/api/system/list)
Update: Solved!
I had 2 main problems:
routes order (in app.use(..))
use different instance of Express.Router() on each API
Refer the answer below for more information.
Try reversing the order of the routes and this will probably solve your problem:
express.use('/api/system/:systemId/book', bookAPI.routes);
express.use('/api/system', systemAPI.routes);
The reason is that express evaluates routes 'greedily' and it will even resolve to partial matches. So, it is important to keep the more specific routes first before the more general ones.
Also, I think you are using the express router wrong according to the documentation the systemAPI:
const express = require('express');
const router = express.Router();
router.get('/list', Validate(Validation.list), listHandler)
module.exports = router;
The bookAPI route:
const express = require('express');
const router = express.Router();
router.get('/list', Validate(Validation.list), listHandler)
module.exports = router;
Finally import the routers and use them:
const express = require('express');
const bookRouter = require('./bookAPI'); //set the file paths based on your file structure
const systemRouter = require('./systemAPI');
const app = express();
app.use('/api/system/:systemId/book', bookRouter);
app.use('/api/system', systemRouter);

NodeJS Construct a custom object without requiring the file

Is there a way in nodejs to define a custom object type in it's own file, then instantiate it anywhere else in the codebase without needing to require in the file?
For example, every time I create a new map somewhere, I don't have to require in the map definition. I want to be able to do
var user = new User();
from anywhere without requiring in the definition.
If you don't want to require a dependency everywhere then you could use Dependency Injection. Dependency Injection is a pattern for passing dependencies or common information from one context to another.
The power of Dependency Injection is decoupling the Dependency itself with resolving and/or initializing that Dependency. In JavaScript, closures can be used to hide configuration/initialization details of a Dependency by locking in their context to the scope that created them. This means that your files implementing the Dependency don't need to be concerned with how to get it initialized (ie. a config) which, probably also means more thing it needs to require.
Below is an example of how a User can be required in one file, injected into a separate file and then be used as if it were required within the file it was injected to. I chose to use an Express Server and Dependency Injection into a Router as the example because a popular reason to use Node.js is to build a Web API. For another example of Dependency Injection, see this blog post from RisingStack Engineering (you'll need to scroll down to the Dependency Injection section).
The example has 3 files
lib/models/User.js
This is the module that is Injected into our UserRouter
/routes/user.js
This is the UserRouter and it returns a function that accepts a models object which, contains all Models the UserRouter needs. Once the router is created it is returned for use within an Express Server
server.js.
The Express Server that is going to be responsible for injecting the User Model into the UserRouter so it can create new users in a cache.
lib/models/User
class User {
constructor(name) {
this.name = name
}
}
module.exports = User
routes/user.js
const Router = require('express').Router
const UserRouter = models => {
// Use Destructuring to get the User model from the models object passed into the Router
// You can use regular ES5 for this as well: var User = models.User
const {User} = models
const router = new Router()
const users = []
// Return all users in cache
router.get('/', (req, res) => res.status(200).json({users}))
router.post('/', (req, res) => {
const {name} = (req.body || {})
if (!name) {
return res.status(400).send('A User must have a name')
}
// Create the new User
let newUser = new User(name)
// add it to the users cache
users.push(newUser)
// return 201 CREATED and send the user as the message body
return res.status(201).json(newUser)
})
// Return the router instance
return router
}
module.exports = UserRouter
server.js
const express = require('express')
const port = process.env.PORT || 1337
// require our UserRouter
const UserRouter = require('./routes/user')
// import the User model
const User = require('./lib/models/User')
// create an object called Models with the User now as a property
const Models = {User}
const server = express()
// Inject the Models directly into the UserRouter
server.use('/users', UserRouter(Models))
server.listen(port, () => console.log(`Listening on ${port}`))

Resources