I have single repository of project and have multiple databases for different clients.
Following types of database naming and URL to connectivity that I am using to connect database based on access URL:
Client's Database
shreyas_db (http://shreyas.locahost:3001)
ajay_db (http://ajay.locahost:3001)
vijay_db (http://vijay.locahost:3001)
Please guide how to implement this structure in NodeJs with Express.
Thanks
Here is the solution I figure out:
Add a router
app.use('/api', await APIRouter())
connect to different DBS in router middleware.
Get the subdomain(If you are using Nginx, you may found req.subdomains or req.host does not return what you expected, try to use req.headers.referer instead), use res.locals to save DB, so you can get it from every API call.
export const APIRouter = async () => {
const router = express.Router()
const client = await connectDatabase()
router.use(async (req, res, next) => {
//const host = req.headers.referer?.replace("https://","");
//const subdomain = host ? host.substring(0, host.indexOf('.')) : 'main'
const subdomain = req.subdomains.length ? req.subdomains.length[0] : 'main'
const db = client.db(subdomain)
res.locals.db = {
clients: db.collection<Client>('clients'),
}
next()
}
)
return router
};
Related
I am writing a small application. You could think of it like this:
Server
-- Security
-- Microservices
-- Others
The server routes to each of those. Each of those routes uses a different MongoDB database (same server).
I don't find the way to make a single, reusable connection to MongoDB server, that could be reused each route.
For example:
//server.js
//import modules etc
async function connectDB(){
const uri = process.env.MONGOURI;
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
return (await client.connect());
}
But I can not pass the returned client to any route, as the routes take no parameters:
//server.js
import security from "./security";
async function run() {
// security
app.use("security", security) //cant pass mongo client
If I write the client in the routes themselves:
//security.js
router.post("/register", async (req, res) => {
const database = await connectDB()
const users = database.collection("users");
I will have to run connectDB() to each route.
Question
Is there a way to create a connection in the server.js that will be reused in other routes?
I'm trying to build a small Node.js API hosted on PaaS service Digital Ocean App Platform. The API is very simple: only having a single route that I know is accessible. However, what I do in this route only works in my machine in development mode. When I deploy it, it ceases to work as I'd want to.
For more context, this route takes a string path and returns a signed URL to my Cloudfront instance. I'm using the aws-cloudfront-sign library and what I'm trying to return is the signed URL directly after calling cfsign.getSignedUrl. In development and on my machine, I indeed receive a valid URL. But deployed and in production I only receive {} after accessing the URL.
I validated by making quick res.send() calls that the signingParams are correctly instantiated and correctly include my private key. Same for the imagePath parameter.
Here's the route:
"use strict";
// Thanks to: https://aws.amazon.com/fr/blogs/developer/creating-amazon-cloudfront-signed-urls-in-node-js/
// And also: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html#private-content-canned-policy-creating-signature
const express = require("express");
const router = express.Router();
const cfsign = require('aws-cloudfront-sign');
const twoMinutes = 2*60*1000;
let key = ""
if (process.env.NODE_ENV == 'development') {
key = 'localOnlyKey';
} else if (process.env.NODE_ENV == 'production') {
key = process.env.CLOUDFRONT_API_SECRET;
}
/*
Convert an image path to a full-fledged Cloudfront URL
*/
router.get("/:imagePath", (req, res, next) => {
const expirationTime = Date.now() + twoMinutes;
const signingParams = {
keypairId: 'K34YSMTSIK8T2F',
privateKeyString: key,
expireTime: expirationTime
};
const imagePath = req.params.imagePath;
// Generating a signed URL
const signedUrl = cfsign.getSignedUrl(
`https://d3dzfh5brk23pn.cloudfront.net/${req.params.imagePath}`,
signingParams
);
res.send(signedUrl);
});
module.exports = router;
What could lead to this route only producing a single {} response?
Thank you in advance.
I have the following route in my express (version 4.17.1) API in a postTimecardCompany.js file:
const mongoose = require('mongoose');
const Timecard = require('./../models/timecard');
function postTimecardCompany(server) {
server.post('/api/v1/timecard/:userId', (req, res) => {
// Insert timecard data into the database
Timecard.create(data, (error, result) => {
// Check for errors
if (error) {
res.status(500).end();
return;
}
// Respond
res.status(200).send({timecardId: result._id});
});
});
}
module.exports = postTimecardCompany;
The route (among other routes) is loaded via the following mechanism by server.js file:
[
'postTimecardCompany',
'anotherRoute',
'someOtherRoute',
'andSoOn...'
].map((route) => {
require('./core/routes/' + route + '.js').call(null, server)
});
I have a middleware (in server.js file) where I check which route is being called.
server.use((req, res, next) => {
// If route is "/api/v1/timecard/:userId" do something
});
I have found various solutions which do nearly what I am looking for, but not exactly.
For example, if I post to the route with a data parameter userId value of "123f9b" then req.originalUrl gives an output of "/api/v1/timecard/123f9b."
What i'm looking to get is the original route path with the parameters in it so for a request of "/api/v1/timecard/123f9b" it would be: "/api/v1/timecard/:userId."
How do I get this functionality in express or extend express to get the original route path with parameters in the request object?
if you want to use from your approach, it's is impossible, after that your approach is not standard in express check the documentation, if you want get routes in a middleware you should try like this:
server.js
const express = require('express')
const server = express()
const postTimecardCompany = require('./routes/postTimecardCompany.js')// don't use map()
server.use("/",postTimecardCompany)//use the routes
server.listen(6565,()=>console.log(`Listening to PORT 6565`))
routes of postTimecardCompany.js
use Router of express and export router, and you can use middleware before each route you want, there are many ways to use middleware in routes, check the documentation
const express = require("express");
const router = express.Router();
const middleware = require('../middleware');//require middlewares
router.post("/api/v1/timecard/:userId", middleware,(req, res) => {
// Insert timecard data into the database
console.log(req.route.path);
});
module.exports = router;
middleware.js
module.exports = ((req, res, next) => {
console.log(req.route.path);
next()
});
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!!
I want to use express to create unique proxy instances using URLs that I am storing in a database. I found an npm module that may help with this called http-express-proxy but open to other solutions that uses express.
And I had a route like this (using http-express-proxy):
user.URL = 'https://www.google.com'
app.post('/', proxy(user.URL))
// and after this, user.URL is updated to a different value. I want the proxy's address to change too.
I did find a solution that dynamically creates a regular express route during runtime, but I cannot get it to work using the proxy() method from http-express-proxy:
https://alexanderzeitler.com/articles/expressjs-dynamic-runtime-routing/
According to that approach, I can require a 2nd file inside the POST route that looks like this: (and includes a call to the database using sequelize)
const express = require('express')
const proxy = require('express-http-proxy')
const User = require('../db/models/user')
const app = express()
module.exports= {
init : init
}
function init(app) {
User.findOne({where: {id: 1}})
.then(user => app.post('/', proxy(user.URL)))
}
And in my main app.js file, I am then doing this:
// ...
app.post('/', function(req, res, next) {
var dynamic = require('./dynamic')
dynamic.init(app)
})
// ...
But I am getting a 503 response when I post using this approach using http-express-proxy, which was not used in his example.
I got it to work like this. I modified the express-http-proxy module and at the top of the SendProxyRequest() method I included this:
if (req.URL) host = req.URL
And then in my app.js file I added a middleware method to set req.URL after a call to the database:
function getProxyURL (req, res, next) {
User.findOne({where: {id: 1}})
.then(user => {
if(user.URL) req.URL = user.URL
next()
})
}
app.post('/', getProxyURL, proxy('www.default.com'))