I implemented protected API routes in Express using the Auth0 quick start. Protected routes are handled with a middleware function called checkJwt, provided by Auth0. It runs whenever one of the GET methods is called. This process works well if I manage all my routes in server.js.
How can I separate out the route handling and still preserve the protected API routes?
server.js working code with protected routes.
import colors from 'colors'
import cors from 'cors'
import express from 'express'
import morgan from 'morgan'
import dotenv from 'dotenv'
import connectDB from './db.js'
import checkJwt from './middleware/auth.middleware.js'
import { getStudents, getStudent } from './controllers/students.controller.js'
dotenv.config()
connectDB()
export const app = express()
app.use(cors())
app.use(express.json({ limit: '50mb' }))
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'))
}
//handle routing internally
app.get('/api/students/:id', checkJwt, getStudent)
app.get('/api/students', checkJwt, getStudents)
const PORT = process.env.PORT || 6000
app.listen(PORT, () =>
console.log(
`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold
)
)
I want to divorce out the route handling as my code grows. I can't figure out where to put checkJwt once I separate out the routing.
server.js desired code structure is:
import colors from 'colors'
import cors from 'cors'
import express from 'express'
import morgan from 'morgan'
import dotenv from 'dotenv'
import connectDB from './db.js'
import studentsRouter from './routes/students.routes.js'
dotenv.config()
connectDB()
const app = express()
app.use(cors())
app.use(express.json({ limit: '50mb' }))
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'))
}
// handle routing externally
const apiRouter = express.Router()
app.use('/api', apiRouter)
apiRouter.use('/students', studentsRouter)
const PORT = process.env.PORT || 6000
app.listen(PORT, () =>
console.log(
`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold
)
)
Routes are implemented in the students.routes.js Route handler.
import express from 'express'
import { getStudent, getStudents } from '../controllers/students.controller.js'
const router = express.Router()
// where can I implement checkJwt?
router.route('/').get(getStudents)
router.route('/:id').get(getStudent)
export default router
Is it possible to simplify the code by moving the routing, and still protect my routes?
you can use any middleware like this in your router:
const router = express.Router();
router.route('/').get([checkJwt, secondMiddleware, ...] , getStudents);
based on documentation:
express-routing
You can provide multiple callback functions that behave like middleware to handle a request. The only exception is that these callbacks might invoke next('route') to bypass the remaining route callbacks. You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there’s no reason to proceed with the current route.
Related
I am learning express js.
Here I am creating a crud project in student data. I have created routes and controllers but when I include it in my main app.js , my router seems not working.
Please guide me.
P.S:I am working on ES6.
My app.js
import express from 'express';
import connectDB from './db/connectdb.js'
import web from "./routes/web.js"
import{join} from 'path'
const app = express();
const port = process.env.PORT || "8080"
const DATABASE_URL ="mongodb://127.0.0.1:27017"
//loading static files
app.use('/student',express.static(join(process.cwd(),"public")))
//loading routes
app.get('/student',web)
//set template engine
app.set("view engine","ejs")
app.listen(port,()=>{
console.log("Server listening at port no:"+port)
})
connectDB(DATABASE_URL)
My router file saved as web.js
import express from 'express';
import StudentController from "../controllers/studentController.js"
const router = express.Router();
router.route('/',StudentController.getAllDoc)
export default router
My studentController.js
class StudentController{
static getAllDoc = (req,res)=>{
res.render('index')
}
}
export default StudentController
I expected my ejs file named as index.ejs to run which is in views folder, but the result is like cannot get /student/
.use is used to register middleware, serve group of route,...
.get, .post is a single route.
Please change your code to something like this:
app.js
//loading static files
app.use(express.static(join(process.cwd(),"public")))
//loading routes
app.use('/student',web)
web.js
const router = express.Router();
router.get('/',StudentController.getAllDoc)
export default router
I have written crud routes
`
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const db = require('./user_crud_queries')
const port = 3000
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
extended: true,
})
)
app.get('/', (request, response) => {
response.json({
info: 'Node.js, Express, and Postgres API'
})
})
app.get('/users', db.getUsers)
app.get('/users/:id', db.getUserById)
app.post('/users', db.createUser)
app.put('/users/:id', db.updateUser)
app.delete('/users/:id', db.deleteUser)
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})
`
if i have 100 tables do i have to include 100 files? Can it be imported dynamically based on call?
I did create generic crud but i dont want to give that to client because they expect it to be detailed. So 100 tables means 100 separate file for each table. if sever include all 100 tables , will the memory is enough?
So what you can instead do is create a file called users.routes.js/ts and include all of it like this :
import { Router } from "express";
router
.route("/users")
.get(db.getUsers)
.post(db.createUser)
router
.route("/users/:id")
.get('/users/:id', db.getUserById)
.delete('/users/:id', db.deleteUser)
export default router;
and then in the app.js/ts you can user it like this :
import userRouter from "./routes/user.routes";
app.use("/api", userRouter);
So now you can use the following endpoint :
/api/users or /api.users/:id
But yes, if there are 100 tables you will have to use 100 different files for all the CRUD operation that is there, only this will make the code readability easy. Also, you can use different microservice for the same job and reduce the tables for a single server to handle.
I was working on a personal express project. I've created two files one server file and other restaurants.route.js to sepearate routes and server code. But I'm not able trigger the home route from local host. Moreover no console error is showing only the 404 page error.
server.js code:
import express from "express";
import cors from "cors";
import restaurants from "./api/restaurants.route.js";
// initialising express app
const app = express();
// applying middlewears
app.use(cors());
app.use(express.json());
app.use("api/v1/restaurants", restaurants);
app.use("*", (req, res) => res.status(404).json({error: "not found"}));
export default app;
restaurants.route.js:
import express from "express";
// express router
const router = express.Router();
router.route("/")
.get((req, res) => {
res.send("Hello World");
});`enter code here`
export default router;
It looks like you might be missing the leading forward slash (/) in the path you're using to mount the restaurants router in your server.js file. When you use the app.use() method to mount a router, the path you specify should start with a forward slash.
So instead of this:
app.use("api/v1/restaurants", restaurants);
Try this:
app.use("/api/v1/restaurants", restaurants);
is there a better way of prefixing all routes with /something than this?
app.use('/api/users', require('./routes/users'));
app.use('/api/books', require('./routes/books'));
app.use('/api/authors', require('./routes/authors'));
That would also work when using the routes in my server.js with the app.get()/post()/...
If all your routes are prefixed by /api, I suppose you could use function.
function api(route) {
return `/api/${route}`;
}
app.use(api('users'), require('./routes/users'));
app.use(api('books'), require('./routes/books'));
app.use(api('authors'), require('./routes/authors'));
That being said, the /api prefix may be unnecessary. You could just omit it:
app.use('/users', require('./routes/users'));
app.use('/books', require('./routes/books'));
app.use('/authors', require('./routes/authors'));
You need to add { mergeParams: true } into the child router.
// app.js
import routes from './routes'
const API_PREFIX = '/api'
app.use(API_PREFIX, routes)
// routes.js
import { Router } from 'express'
import users from './users'
import books from './books'
import authors from './authors'
const router = Router({ mergeParams: true })
router.use(users)
router.use(books)
router.use(authors)
export default router
I'm using express 4.16.3 and trying to make sense of why one request to a controller works and a request doesn't.
in my server.js i've got the following. There's no semi-colons because used prettier beforehand.
import express from 'express'
import bodyParser from 'body-parser'
import cors from 'cors'
import PriceCheckerController from './controllers/PriceChecker'
import PersonalLibraryController from './controllers/PersonalLibrary'
const app = express()
app.set('port', process.env.PORT || 5000)
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}))
app.use(cors())
// routes definition
app.use('/api/books',PersonalLibraryController) // this does not
app.use('/api/stock-prices', PriceCheckerController) // this works
//
app.listen(app.get('port'), error => {
if (error) {
logger.error(`error fcc-isqa:${error}`)
} else {
logger.info(`fcc-isqa is running on port ${app.get('port')}`)
}
})
export default app
In PriceCheckerController i've implemented it like so.
import 'babel-polyfill' // mocha required
import express from 'express'
import logger from '../logger'
const PriceCheckerController = express.Router()
PriceCheckerController.use((req, res, next) => {
logger.info(
`date=>${new Date()}\n method=>${req.method}nsender:${req.ip}`
)
})
PriceCheckerController.get('/', async (req, res) => {
return res.status(200).json({message:'soon'})
})
export default PriceCheckerController
In PersonalLibraryController i've implemented it like so
import 'babel-polyfill'
import express from 'express'
import logger from '../logger'
const PersonalLibraryController = express.Router()
PersonalLibraryController.use((req,res,next)=>{
logger.info(
`library date=>${
new Date()}method=>${req.method}url=>${req.baseUrl}${req.path}`
)
})
PersonalLibraryController.route('/test')
.get(async (req, res) => {
return res.status(200).json({message: 'get soon'})
})
.post(async (req,res)=>{
return res.status(200).json({message: 'post soon'})
})
export default PersonalLibraryController
A request to /api/stock-prices returns ok with message soon.
A request to /api/books/test is logged by the middleware but a response is not sent back. It eventually gives a timeout
Can anyone give me any insights/ help in understanding what is the problem and how to fix it?
Thanks in advance.
Your middleware functions need to call next() to carry on the route execution.
e.g
PriceCheckerController.use((req, res, next) => {
logger.info(
`date=>${new Date()}\n method=>${req.method}nsender:${req.ip}`
)
next();
})
https://expressjs.com/en/guide/using-middleware.html
"If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging."