im trying to setup an API using node.js and in my app.js class im handling request errors where i return a 404 in case something goes wrong, now thats my problem, i can't see how am i requesting anything wrong, i am still receiving 404 error, im trying to send a post request to my API exactly like this:
{
"name":"Harry Potter 5",
"price":"12.99"
}
then i get this
Here's my app.js
const express = require('express');
const app = express();
const morgan = require('morgan');
const productRoutes = require('./api/routes/product');
const orderRoutes = require('./api/routes/order');
const bodyParser = require('body-parser');
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({
extended:false
}));
app.use(bodyParser.json());
app.use((req, res, next) => {
const error = new Error("Not found");
error.status = 404;
next(error);
});
app.use((error, req, res, next) => {
res.status(error.status || 500);
res.json({
error: {
message: error.message
}
});
});
app.use('/products', productRoutes);
app.use('/orders', orderRoutes);
module.exports = app;
Here's my product.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res, next) => {
res.status(200).json({
message: 'Handling GET requests to /products'
});
});
router.post('/', (req, res, next) => {
const product = {
name: req.body.name,
price: req.body.price
};
res.status(201).json({
message: 'Handling POST requests to /products',
createdProduct: product
});
});
router.get('/:productId', (req, res, next) => {
const id = req.params.productId;
if (id === 'special') {
res.status(200).json({
message: 'You discovered the special ID',
id: id
});
} else {
res.status(200).json({
message: 'You passed an ID'
});
}
});
router.patch('/:productId', (req, res, next) => {
res.status(200).json({
message: 'Updated product!'
});
});
router.delete('/:productId', (req, res, next) => {
res.status(200).json({
message: 'Deleted product!'
});
});
module.exports = router;
It's because you are setting everything to error out :)
See the documentation from here - from the provided link:
Writing error handlers Define error-handling middleware functions in
the same way as other middleware functions, except error-handling
functions have four arguments instead of three: (err, req, res, next).
For example:
// pay attention to err param
app.use(function (err, req, res, next) {
console.error(err.stack)`
res.status(500).send('Something broke!')
})
In your code you have this bit:
app.use((req, res, next) => {
const error = new Error("Not found");
error.status = 404;
next(error);
});
which tells express that every request should be responded with a 404. You should either make it a proper error handler, or remove it.
This is because any request execute the 404 handler.
Look at this shorten version of your code:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({
extended:false
}));
app.use(bodyParser.json());
app.use((req, res, next) => {
console.log("Got into 404 handler");
const error = new Error("Not found");
error.status = 404;
next(error);
});
app.use((error, req, res, next) => {
console.log("Got into 500 handler");
res.status(error.status || 500);
res.json({
error: {
message: error.message
}
});
});
app.use('/products', (req, res, next) => {
console.log("Got into 200 handler");
res.status(200).end();
});
app.listen(8080);
It prints "Got into 404 handler" at each request. Now, if you comment out the 404 callback this way: all requests go through the 500 and 200 callbacks:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({
extended:false
}));
app.use(bodyParser.json());
/* there used to be the 404 callback here */
app.use((error, req, res, next) => {
console.log("Got into 500 handler");
res.status(error.status || 500);
res.json({
error: {
message: error.message
}
});
});
app.use('/products', (req, res, next) => {
console.log("Got into 200 handler");
res.status(200).end();
});
app.listen(8080);
Now in your specific problem, the code below would work (I just swapped the order of the handlers):
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({
extended:false
}));
app.use(bodyParser.json());
app.use('/products', (req, res, next) => {
console.log("Got into 200 handler");
res.status(200).end();
});
app.use((req, res, next) => {
console.log("Got into 404 handler");
const error = new Error("Not found");
error.status = 404;
next(error);
});
app.use((error, req, res, next) => {
console.log("Got into 500 handler");
res.status(error.status || 500);
res.json({
error: {
message: error.message
}
});
});
app.listen(8080);
Hope this helps.
Related
I am new to javascript and I have a a bit of a clarification question.I managed to set up a middleware function to upload image files in node.js on my App.js file. However, The route I want my images to show is the '/post' but I am stuck on how to implement my function in my Post controller 'CREATE' method as currently on my app.js file the route post is already being used in app.use("/posts", tokenChecker, postsRouter);. And I know you can't use one route in 2 separate places. Any clarification would be appreciated.
//controller file
const Post = require("../models/post");
const TokenGenerator = require("../models/token_generator");
const PostsController = {
Index: (req, res) => {
Post.find(async (err, posts) => {
if (err) {
throw err;
}
const token = await TokenGenerator.jsonwebtoken(req.user_id)
res.status(200).json({ posts: posts, token: token });
});
},
Create: (req, res) => {
const post = new Post(req.body);
post.save(async (err) => {
if (err) {
throw err;
}
const token = await TokenGenerator.jsonwebtoken(req.user_id)
res.status(201).json({ message: 'OK', token: token });
});
},
};
module.exports = PostsController;
//app.js file
const createError = require("http-errors");
const express = require("express");
const path = require("path");
const logger = require("morgan");
const JWT = require("jsonwebtoken");
const postsRouter = require("./routes/posts");
const tokensRouter = require("./routes/tokens");
const usersRouter = require("./routes/users");
const multer = require('multer');
const app = express();
// setup for receiving JSON
app.use(express.json())
app.use(logger("dev"));
app.use(express.json());
app.use(express.static(path.join(__dirname, "public")));
// middleware function to check for valid tokens
const tokenChecker = (req, res, next) => {
let token;
const authHeader = req.get("Authorization")
if(authHeader) {
token = authHeader.slice(7)
}
JWT.verify(token, process.env.JWT_SECRET, (err, payload) => {
if(err) {
console.log(err)
res.status(401).json({message: "auth error"});
} else {
req.user_id = payload.user_id;
next();
}
});
};
//middleware function for storing images
const fileStorageEngine = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, '../public/images/')
},
filename:(req, file, cb) => {
}
})
const upload = multer({storage: fileStorageEngine});
app.post('/posts', upload.array('images', 3),(req,res) =>{
console.log(req.files);
res.send("multiple Files Upload success");
});
// route setup
app.use("/posts", tokenChecker, postsRouter);
app.use("/tokens", tokensRouter);
app.use("/users", usersRouter);
// catch 404 and forward to error handler
app.use((req, res, next) => {
next(createError(404));
});
// error handler
app.use((err, req, res) => {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// respond with details of the error
res.status(err.status || 500).json({message: 'server error'})
});
module.exports = app;
// route setup
app.use("/posts", tokenChecker, postsRouter);
app.use("/tokens", tokensRouter);
app.use("/users", usersRouter);
// catch 404 and forward to error handler
app.use((req, res, next) => {
next(createError(404));
});
// error handler
app.use((err, req, res) => {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// respond with details of the error
res.status(err.status || 500).json({message: 'server error'})
});
module.exports = app;
When I make a post request to the /login endpoint in postman it works fine and returns all the information. However when I try to navigate to the end point in the url the route returns unfound. In the console I get GET http://localhost:5000/login 404 (Not Found). Why is the console returning for a get request? If I try to call the post request in axios I get xhr.js:177 POST http://localhost:3000/login 404 (Not Found).
app.js
require("dotenv").config();
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const router = express.Router();
const cors = require('cors');
const app = express();
app.use(cors())
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
const mongoose = require('mongoose');
const connection = "password"
mongoose.connect(connection, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
})
const clientRoutes = require('./routes/clientRoutes');
const traderRoutes = require('./routes/traderRoutes');
const loginRoute = require('./routes/loginRoute')
app.use('/', clientRoutes, traderRoutes, loginRoute);
// setup a friendly greeting for the root route
app.get('/', (req, res) => {
res.json({
message: 'Welcome to the REST API for Pave!',
});
});
// send 404 if no other route matched
app.use((req, res) => {
res.status(404).json({
message: 'Route Not Found',
});
});
// setup a global error handler
app.use((err, req, res, next) => {
if (enableGlobalErrorLogging) {
console.error(`Global error handler: ${JSON.stringify(err.stack)}`);
}
res.status(err.status || 500).json({
message: err.message,
error: {},
});
});
app.listen(5000, () => console.log('Listening on port 5000!'))
loginRoute.js
require("dotenv").config();
const express = require("express");
const router = express.Router();
const jwt = require("jsonwebtoken");
const bcryptjs = require("bcryptjs");
const Client = require("../models/clientSchema");
const Trader = require("../models/traderSchema");
function asyncHandler(callback) {
return async (req, res, next) => {
try {
await callback(req, res, next);
} catch (error) {
next(error);
console.log(error);
}
};
}
router.post('/login', asyncHandler(async (req, res, next) => {
let user = req.body;
const trader = await Trader.findOne({ emailAddress: req.body.emailAddress })
if (user && trader) {
console.log(trader)
let traderAuthenticated = await bcryptjs.compareSync(user.password, trader.password);
console.log(traderAuthenticated)
if (traderAuthenticated) {
console.log('Trader match')
const accessToken = jwt.sign(trader.toJSON(), process.env.ACCESS_TOKEN_SECRET)
res.location('/trader');
res.json({
trader: trader,
accessToken: accessToken
}).end();
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
} else if (user && !trader) {
const client = await Client.findOne({emailAddress: req.body.emailAddress})
console.log(client)
let clientAuthenticated = await bcryptjs.compareSync(user.password, client.password);
console.log(clientAuthenticated)
if (clientAuthenticated) {
console.log('Client match')
const accessToken = jwt.sign(client.toJSON(), process.env.ACCESS_TOKEN_SECRET)
res.location('/client');
res.json({
client: client,
accessToken: accessToken
});
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
})
);
module.exports = router;
You set POSTMAN to make a POST request, right? When you enter a url in the browser, that causes a GET request - and you have no route to manage this that I can see, but for the default Not found.
you are calling with axios with wrong port no. it should, POST method http://localhost:5000/login as your application is running on port 5000.
but you are calling, POST http://localhost:3000/login
I'm getting started in node.js and trying to create a user authentication system for my web application. I am watching a tutorial on YouTube and have followed every step through. When it comes to actually registering my user. the username entry in the collecting comes out as null. I'm not sure why this is happening, and I'm a rookie to javascript and node js. I am using passport, express and mongodb modules.
this is the video i am watching 'https://www.youtube.com/watch?v=m2ZzRZemc98'
If anyone knows how to fix my issue, please respond
Thanks
This is my main app.js
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
const MongoClient = require("mongodb").MongoClient;
const passport = require("passport");
const Strategy = require("passport-local").Strategy;
const session = require("express-session");
const flash = require("connect-flash");
const authUtils = require("./utils/auth");
const hbs = require("hbs");
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
const authRouter = require("./routes/auth");
var app = express();
//Connet to MongoDB database
MongoClient.connect("mongodb://localhost", (err, client) => {
if (err) {
throw err;
}
const db = client.db("user-profiles");
const users = db.collection("users");
app.locals.users = users;
});
//Passport streategy
passport.use(
new Strategy((username, passport, done) => {
app.locals.users.findOne({ username }, (err, user) => {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (user.password != authUtils.hashPassword(password)) {
return done(null, false);
}
return done(null, user);
});
})
);
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
done(null, { id });
});
// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "hbs");
hbs.registerPartials(path.join(__dirname, "views/partials"));
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use(
session({
secret: "session secret",
resave: false,
saveUninitialized: false
})
);
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use((req, res, next) => {
res.locals.loggedIn = req.isAuthenticated();
next();
});
app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use("/auth", authRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render("error");
});
module.exports = app;
This is my auth.js
const router = express.Router();
const authUtils = require("../utils/auth");
const passport = require("passport");
router.get("/login", (req, res, next) => {
const messages = req.flash();
res.render("login", { messages });
});
router.post(
"/login",
passport.authenticate("local", {
failureRedirect: "/auth/login",
failureFlash: "Wrong username or password"
}),
(req, res, next) => {
res.redirect("/users");
}
);
router.get("/register", (req, res, next) => {
const messages = req.flash();
res.render("register", { messages });
});
router.post("/register", (req, res, next) => {
const registrationParams = req.body;
const users = req.app.locals.users;
const payload = {
username: registrationParams.username,
password: authUtils.hashPassword(registrationParams.password)
};
users.insertOne(payload, err => {
if (err) {
req.flash("error", "User account already exists");
} else {
req.flash("success", "User account was registered succesfully");
}
res.redirect("/auth/register");
});
});
router.get("/logout", (req, res, next) => {
req.session.destroy();
res.redirect("/");
});
module.exports = router;
I fixed my error, it was a mistake i made in the handlebars file, i put the input name as none the class of form-control and then named it again. Obviously the js looked at the first name and not the second name
I can't figure out how to query the MySQL database from the promise in my route file. I'm writing a RESTful API to query a MySQL database with GET methods. I'm using Express and Axios for Javascript promises.
I want to get back the list of books from a SQL table and the count of how many listings in the returned JSON.
server.js
const http = require('http');
const app = require('./app');
const port = process.env.PORT || 3000;
const server = http.createServer(app);
server.listen(port);
app.js
const express = require('express');
const app = express();
const morgan = require('morgan');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const bookRoutes = require('./api/routes/books');
const entryRoutes = require('./api/routes/entries');
const connection = mysql.createConnection({
host: 'localhost',
user: 'rlreader',
password: process.env.MYSQL_DB_PW,
database: 'books'
});
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
if (req.method === 'OPTIONS') {
res.header('Access-Control-Allow-Methods', 'GET');
return res.status(200).json({});
}
next();
});
// Routes which should handle requests
app.use('/books', bookRoutes);
app.use('/entries', entryRoutes);
app.use((req, res, next) => { //request, response, next
const error = new Error('Not found');
error.status = 404;
next(error);
});
app.use((error, req, res, next) => {
res.status(error.status || 500);
res.json({
error: {
message: error.message
}
});
});
module.exports = app;
books.js
const express = require('express');
const router = express.Router();
const axios = require('axios');
//do I import something for mysql here?
router.get('/', (req, res, next) => {
axios.get('/').then(docs => {
res.status(200).json({
"hello": "hi" //want to query MySQL database here
})
}).catch(err => {
res.status(500).json({
error: err
});
})
});
module.exports = router;
Any help would be appreciated. For starters, how do I get const connection from app.js to books.js?
I moved the code connecting to the MySQL database to a separate file and included that:
const con = require('../../db');
Next, I had to properly return the response:
router.get('/', (req, res, next) => {
let responseData = axios.get('/').then(docs => {
const sql = "SELECT title, id FROM books";
con.query(sql, function (err, result) {
if (err) {
console.log("error happened");
}
return res.status(200).json(result);
});
}).catch(err => {
res.status(500).json({
error: err
});
});
});
I've been following a tutorial online, but modifying it for my own project. Get requests to api/posts work fine, but Post requests lead to 404 and 'Error: Can't set headers after they are sent.'. I can't work out why this is.
'use strict'
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const Post = require('./models/post_schema');
const app = express();
const router = express.Router();
const port = process.env.API_PORT || 3000;
mongoose.connect('mongodb://REDACTED');
app.use(bodyParser.urlencoded({extend: true}));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers');
res.setHeader('Cache-Control', 'no-cache');
next();
});
router.get('/', function(req, res) {
res.json({ message: 'API Initlialised'});
});
router.route('/posts')
.get(function(req, res) {
Post.find(function(err, posts) {
if (err) {res.send(err)}
res.json(posts)
})
})
.post(function(req, res) {
const post = new Post();
post.title = req.body.title;
post.text = req.body.text;
post.save(function(err) {
if (err) {res.send(err)};
res.json({ message: 'Post added!'});
});
});
app.use('/api', router);
app.listen(port, function() {
console.log(`api running on port ${port}`)
})
I've gone through the express documentation, but can't find a solution. Any help would be appreciated.
This might do the trick!
router.route('/posts')
.get(function(req, res) {
Post.find(function(err, posts) {
if (err) return res.send(err);
return res.json(posts)
})
})
.post(function(req, res) {
const post = new Post();
post.title = req.body.title;
post.text = req.body.text;
post.save(function(err) {
if (err) return res.send(err);
return res.json({ message: 'Post added!'});
});
});
Your server could send 2 http response, if an error occurred on the save method, I think that's why you get Can't set headers after they are sent., try to use return statement to stop the remaining code from executing or use else clause:
post.save(function(err) {
if (err) {
return res.json({ success: false, error: err.message });
}
res.json({ message: 'Post added!'});
});