ExpressJS Middleware Method to make variable available in other (module) files - node.js

I am playing around with making a NodeJS app that combines REST API functionality with MongoDB CRUD persistence. I'm pretty new to NodeJS.
Right now I've managed to connect to the Database and figured out that the rest of my code belongs inside the callback - ie only process REST request after the DB is up and available.
The challenge I'm running into in this case is understanding how to "attach" the 'client' (from mongodb.connect) to the 'request'. I need to somehow make it available in other files because I want to keep my routes separate as a best practice.
The same question applies to any variables in the main server.js file which I need to be able to access in my modules.
Here is the relevant code:
//server.js
const express = require('express')
const mongodb = require('mongodb')
const bodyParser = require('body-parser')
const routes = require('./routes')
const url = 'mongodb://localhost:27017/testDB'
let app = express();
app.use(logger('dev'))
app.use(bodyParser.json())
mongodb.connect(url, {useNewUrlParser:true},(error, dbClient) => {
if (error) {
console.log(`Error: ${error}`)
process.exit(1)
}
//connected
console.log(`Connected to mongoDB`)
//what do I do here to make sure my routes can access dbClient?
app.get('/accounts', routes.getAccounts(req, res) )
app.listen(3000)
})
//./routes/index.js
const bodyParser = require('body-parser')
const errorhandler = require('errorhandler')
const mongodb = require('mongodb')
const logger = require('morgan')
module.exports = {
getAccounts: (req, res) => {
console.log("in getAccounts")
//how can I use dbClient in here?
}
}
Thank you in advance for your help!
My apologies if anything about my post isn't according to the normal standards, I'm brand new here! All critique appreciated, coding and otherwise!

Related

TypeError: Router.use() requires a middleware function but got a Object[using express router] --

The app, I am building with nodeJS expressJS is for connecting to a sqlserver database and retrieving data. Am trying to make the code as modular and
reusable as posssible. So different files for routing and controller. The error I am now facing is-
throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))
^
TypeError: Router.use() requires a middleware function but got a Object
For ease of understanding of the imports, my project structure is as such:
controller
|-- controller.js
db
|-- db.js
query
|-- queries.json
routes
|-- route.js
package.json
server.js
My main server.js file is
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const fs = require('fs')
const path = require('path')
const morgan = require('morgan')
const router=require('./routes/route');
const app = express()
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
app.use(morgan('dev'));
const port = 3200
app.listen(process.env.PORT || port , (err) => {
if(err)
{
console.log('Unable to start the server!');
}
else
console.log('NodeExpress Data API started running on : ' + port);
})
the controller file is
const express=require('express')
const { sql, poolPromise } = require('../db/db')
const fs = require('fs');
class MainController
{
async getAllData(req, resp)
{
try
{
const pool = await poolPromise
const result = await pool.request()
.query("select * from players");
resp.json(result.recordset)
}
catch (error)
{
resp.status(500)
resp.send(error.message)
}
}
}
const controller=new MainController();
module.exports=controller;
and the route file is
const express = require('express');
const controller = require('../controller/controller')
const router = express.Router();
router.get('/getAllData', controller.getAllData);
So when I insert this line
app.use('api/', router) in the server.js to wire all the modules together and make calls to api endpoint to get all data, I am getting that error mentioned.
What is it about, can anyone explain me in simple terms? Is the error being thrown from the controller file, since I am initializing a new instance of the controller type? Which line from which file is throwing this error? What is the code correction needed to remove this error?
under route.js change to router.get('/getAllData', controller.getAllData()); you have passed controller.getAllData as a handler function instead of controller.getAllData() as per the type of method you have used in class. hope this solves the error.
Try out one of these:
1- if your are using express and doing
const router = express.Router();
make sure to
module.exports = router ;
at the end of your page
2- If your are using express above 2.x, you have to declare app.router like below code. Please try to replace your code
app.use('/', routes);
with
app.use(app.router);
routes.initialize(app);

How to structure Express server to use granular API endpoints

I currently have an Express server I'm using for a mobile app which is structured as follows (server.js):
const PostRouter = require('./api/production/Post');
const UserRouter = require('./api/production/User');
...
app.use('/posts', PostRouter)
app.use('/users', UserRouter)
and then in api/production/Post I have:
router.get('/fetch', (req, res) => {
...
}
router.get('/delete', (req, res) => {
...
}
etc..
However, I would really like to rebuild the server to match the structure of my corresponding NextJS app and its API structure, which would be something like:
/api/posts/
add-post/
index.js
fetch-all/
index.js
edit-post/
index.js
Where each index.js file contains just one endpoint/query instead of the current structure where each file has multiple queries with the router.get thing.
It looks like this is possible by creating a Router for each endpoint with something like:
const PostFetchAllRouter = require('./api/posts/fetch-all');
const PostEditPostRouter = require('./api/posts/edit-post');
...
app.use('posts/fetch-all', PostFetchAllRouter)
app.use('posts/edit-post', PostEditPostRouter)
What would be the best way to do this, please? Is there an easier way to do this without all the boilerplate in the server.js file? I'm very new to Express - please excuse if it's a naive question
You could move the "boilerplate" code to the different router files and build a router chain. But you have to write a little bit more.
server.js
|-api/
|--posts/
|---PostsRouter.js
|---fetchAll.js
|--users/
|---UserRouter.js
fetchAll.js
const express = require("express");
const FetchAll = express.Router();
FetchAll.get("/fetch", (req, res) => { res.send("/posts/fetch") });
module.exports = FetchAll;
PostsRouter.js
const express = require("express");
const FetchAll = require("./fetchAll");
const PostsRouter = express.Router();
PostsRouter.use(FetchAll);
module.exports = PostsRouter;
server.js
const express = require('express');
const PostsRouter = require("./api/posts/PostsRouter");
let app = express();
app.use("/posts", PostsRouter);
app.listen(80, () => {});
If you build it like that you would plug the small routers into the next bigger one and then use them in the server.js.
GET localhost/posts/fetch HTTP/1.1
// returns in my example the string "/posts/fetch"
Is that what you were looking for?

Unable to get values in NodeJs Express

I am trying to make post request but unable to get values in Postman; it's sending undefined. I am using Express version 4.17.1. Below is my code:
const express = require('express');
const router = express.Router();
const MongoClient = require('mongodb').MongoClient;
const dburl = process.env.URL;
router.use(express.json());
router.use(express.urlencoded({extended:true}));
router.post('/register',(req,res) => {
var data = {
name:req.body.name,
email:req.body.email,
};
res.send(data);
});
module.exports = router;
What am I doing wrong?
Your code snippet works, tested with express 4.17.1 on node 12.19.0. The issue must be from elsewhere.
Possible issues are:
The parser for router could have been overridden by the parser for app (assuming you're calling app.use(router) somewhere).
Can you move this to app level?
router.use(express.json());
router.use(express.urlencoded({extended:true}));

swagger-ui-express Multiple Routes for Different API Documentation

I have 2 separate swagger API documentations which I want to run via swagger-ui-express NPM package, and my express server is starting fine on port 5000, but when I am trying to access any of the URL always getting the 404 error, Here is my app.js file and URL's for your reference:
Route 1: http://localhost:5000/edi
Route 2: http://localhost:5000/ecom
const express = require('express');
const router = require('express').Router();
const swaggerUi = require('swagger-ui-express');
const ediSwaggerDocument = require('./edi-openapi.json');
const ecomSwaggerDocument = require('./ecom-openapi.json');
const SWAGGER_APP_PORT = process.env.SWAGGER_APP_PORT || 5000;
const app = express();
// Route Middleware to be called before serving Any Route
router.use('/', swaggerUi.serve);
// Route - EDI RESTful API Documentaion
router.get('/edi', swaggerUi.setup(ediSwaggerDocument));
// Route - eCommerce RESTful API Documentaion
router.get('/ecom', swaggerUi.setup(ecomSwaggerDocument));
app.listen(SWAGGER_APP_PORT, () => console.log(`RESTful API Up and Running on Port ${SWAGGER_APP_PORT}`));
Try the following configurations to hook swaggerUi with express-app
app.use("/edi", swaggerUi.serve, (...args) => swaggerUi.setup(ediSwaggerDocument)(...args));
app.use("/ecom", swaggerUi.serve, (...args) => swaggerUi.setup(ecomSwaggerDocument)(...args));
I haven't dig enough into the swagger-ui-express but i think the problem comes from the function generateHTML (called in swaggerUi.setup). A global module variable (swaggerInit) is updated when called.
So the last call to generateHTML has side effects on every routes that use swaggerUi.setup middleware.
A quick fix is to generate HTML each time the route is called. According to the code snippet you provide, it should looks like :
let swaggerDocEdi = require('./edi-openapi.json');
let swaggerDocEcom= require('./ecom-openapi.json');
let router = express.Router();
router.use('/api/edi', swagger.serve, (req, res) => {
let html = swagger.generateHTML(swaggerDocEdi);
res.send(html);
});
router.use('/api/ecom', swagger.serve, (req, res) => {
let html = swagger.generateHTML(swaggerDocEcom);
res.send(html);
});
Note that the global variable is still updated.
router.use('/jobs/api/:id',swaggerUi.serve,(req,res)=>{
console.log("here")
let a = req.params.id
if(a==='all'){ res.status(200).send(swaggerUi.generateHTML(swaggerDocument))}
if(a==='google'){res.status(200).send(swaggerUi.generateHTML(GoogleAds)) }
});
I was also looking for a solution for this issue, and found a better solution recommended by Swagger UI Express. check the link below
https://github.com/scottie1984/swagger-ui-express#two-swagger-documents
const express = require('express');
const app = express();
const swaggerUi = require('swagger-ui-express');
const swaggerDocumentOne = require('./swagger-one.json');
const swaggerDocumentTwo = require('./swagger-two.json');
var options = {}
app.use('/api-docs-one', swaggerUi.serveFiles(swaggerDocumentOne, options), swaggerUi.setup(swaggerDocumentOne));
app.use('/api-docs-two', swaggerUi.serveFiles(swaggerDocumentTwo, options), swaggerUi.setup(swaggerDocumentTwo));
Hope this will help others as well.
It looks like the Router is being used incorrectly. For this simple use case I would recommend adding your routes directly to the app instance. See:
const express = require('express');
// xxxx const router = require('express').Router();
const swaggerUi = require('swagger-ui-express');
const ediSwaggerDocument = require('./edi-openapi.json');
const ecomSwaggerDocument = require('./ecom-openapi.json');
const SWAGGER_APP_PORT = process.env.SWAGGER_APP_PORT || 5000;
const app = express();
// Route Middleware to be called before serving Any Route
app.use('/', swaggerUi.serve); // replaced router with app
// Route - EDI RESTful API Documentaion
// REPLACED "router" with "app"
app.get('/edi', swaggerUi.setup(ediSwaggerDocument));
// Route - eCommerce RESTful API Documentaion
// REPLACED "router" with "app"
app.get('/ecom', swaggerUi.setup(ecomSwaggerDocument));
app.listen(SWAGGER_APP_PORT, () => console.log(`RESTful API Up and Running on Port ${SWAGGER_APP_PORT}`));
Now, you could use the Router by adding it to the app instance with app.use(). See:
// Route - EDI
router.get('/edi', swaggerUi.setup(ediSwaggerDocument));
// Route - eCommerce
router.get('/ecom', swaggerUi.setup(ecomSwaggerDocument));
// Adding it to App instance
app.use('/swagger', router)
// End Point => localhost:5000/swagger/edi
Hope this helps!!

How to modularize routing with Node.js Express

I'm building a web app with Express and Node and am trying to factor my routing so that I don't have hundreds of routes in the same file. This site serves different files within the projects directory, so I made a file in routes/ called projectRoutes.jsto handle the routing for project files:
var express = require('express');
module.exports = function() {
var functions = {}
functions.routeProject = function(req, res) {
res.render('pages/projects/' + req.params.string, function(err, html) {
if (err) {
res.send("Sorry! Page not found!");
} else {
res.send(html);
}
});
};
return functions;
}
Then, in my routes.js, I have this...
var projectRoutes = require("./projectRoutes");
router.get('/projects/:string', function(req, res) {
projectRoutes().routeProject(req, res);
});
Is there a better way to structure this functionality within projectRoutes.js? In other words, how can I configure projectRoutes.js so that I can write the follow line of code in index.js:
router.get('/projects/:string', projectRoutes.routeProject);
The above seems like the normal way to handle something like this, but currently the above line throws an error in Node that says the function is undefined.
Thanks for your help!
You should use the native express router, it was made to solve this exact problem! It essentially lets you create simplified nested routes in a modular way.
For each of your resources, you should separate out your routes into several modules named <yourResource>.js. Those modules would contain all of the routing code as well as any other configuration or necessary functions. Then you would attach them in index.js with:
var apiRoute = router.route('/api')
apiRoute.use('/< yourResource >', yourResourceRouter)
For example, if you had a resource bikes:
In index.js:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes')
apiRoute.use('/bikes', bikeRoutes)
Then in bike.js:
var express = require('express')
, router = express.Router()
, bikeRoutes = router.route('/')
bikeRoutes.get(function (req, res) {
res.send('api GET request received')
});
module.exports = bikeRoutes
From there its easy to see that you can build many different resources and continually nest them.
A larger of example of connecting the routes in index.js would be:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes')
, carRoutes = require('./cars')
, skateboardRoutes = require('./skateboards')
, rollerskateRoutes = require('./rollerskates')
// routes
apiRoute.use('/bikes', bikeRoutes)
apiRoute.use('/cars', carRoutes)
apiRoute.use('/skateboards', skateboardRoutes)
apiRoute.use('/rollerskates', rollerskateRoutes)
Each router would contain code similar to bikes.js. With this example its easy to see using express's router modularizes and makes your code base more manageable.
Another option is to use the Router object itself, instead of the Route object.
In Index.js:
//Load Routes
BikeRoutes = require('./routes/Bike.js');
CarRoutes = require('./routes/Car.js');
//Routers
var express = require('express');
var ApiRouter = express.Router();
var BikeRouter = express.Router();
var CarRouter = express.Router();
//Express App
var app = express();
//App Routes
ApiRouter.get('/Api', function(req, res){...});
ApiRouter.use('/', BikeRouter);
ApiRouter.use('/', CarRouter);
In Bike.js:
var express = require('express');
var router = express.Router();
router.get('/Bikes', function(req, res){...});
module.exports = router;
Similarly in Car.js

Resources