'Cannot GET /api/items' - node.js

Working on a MERN application as a way to learn how it all works, but I am stuck trying to get my routes to display. I don't get any other errors, and if I use a simple app.get('/'), I am able to see that just fine; it seems that the routes I have defined are not being recognized for some reason.
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const app = express();
const port = process.env.PORT;
const options = {
origin: 'http://localhost:8081'
}
app.use(cors(options));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const db = require('./models');
db.mongoose
.connect(db.url, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('Successfully connected.');
})
.catch((error) =>{
console.log(`Connection failed. Error: ${error}`);
process.exit();
}
);
require('./routes/items.routes')(app)
app.listen(port, () => {
console.log(`Listening at localhost:${port}`);
});
const multer = require('multer');
const dir = './public/';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, dir);
},
filename: (req, file, cb) => {
const fileName = file.originalname.toLowerCase().split(' ').join('-');
cb(null, fileName + '-' + Date.now());
}
});
var upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (file.mimetype == 'image/png' || file.mimetype == 'image/jpg' || file.mimetype == 'image/jpeg') {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('Invalid file type.'));
}
}
});
module.exports = app => {
const items = require('../controllers/items.controller');
let router = require('express').Router();
router.post('/', upload.single('icon'), items.create);
router.delete('/:id', items.delete);
app.use('/api/items', router);
};
I followed this and this as a start point. Unsure what I am missing or why it is unable to retrieve my POST route.

The error in your title:
Cannot GET /api/items
means it is a GET request to /api/items. But, you don't have a GET handler for that route. You only have a POST handler for that route defined with these two lines of code:
router.post('/', upload.single('icon'), items.create);
app.use('/api/items', router);
So, you apparently need to change your test on that route to a POST, not a GET and the POST will be expecting a body part with the data for an icon in it.
If you want to see exactly what is getting to your router (for debugging/troubleshooting purposes), you can add this right after you declare the router as the first item you register on the router.
router.use((req, res, next) => {
console.log(`In router: ${req.method}:${req.originalUrl}`);
next();
});

Related

Not being able to connect to mongoDB

I am trying to connect my app to MongoDB but i get the following error as i run my app:
DepracationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
[MONGODB DRIVER] Warning: Current Server Discover and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to MongoClient constructor
I know both above are just warnings but they are stopping my app and no letting me run it.
This is how i am setting the connection to mongodb in app.js:
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const csrf = require('csurf');
const flash = require('connect-flash');
const multer = require('multer');
const errorController = require('./controllers/error');
const User = require('./models/user');
const MONGODB_URI =
'mongodb+srv://mateoghidini:<PASSWORD>#cluster0.9dernor.mongodb.net/test';
const app = express();
const store = new MongoDBStore({
uri: MONGODB_URI,
collection: 'sessions'
});
const csrfProtection = csrf();
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'images');
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString() + '-' + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
if (
file.mimetype === 'image/png' ||
file.mimetype === 'image/jpg' ||
file.mimetype === 'image/jpeg'
) {
cb(null, true);
} else {
cb(null, false);
}
};
app.set('view engine', 'ejs');
app.set('views', 'views');
const adminRoutes = require('./routes/admin');
const shopRoutes = require('./routes/shop');
const authRoutes = require('./routes/auth');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(
multer({ storage: fileStorage, fileFilter: fileFilter }).single('image')
);
app.use(express.static(path.join(__dirname, 'public')));
app.use('/images', express.static(path.join(__dirname, 'images')));
app.use(
session({
secret: 'my secret',
resave: false,
saveUninitialized: false,
store: store
})
);
app.use(csrfProtection);
app.use(flash());
app.use((req, res, next) => {
res.locals.isAuthenticated = req.session.isLoggedIn;
res.locals.csrfToken = req.csrfToken();
next();
});
app.use((req, res, next) => {
// throw new Error('Sync Dummy');
if (!req.session.user) {
return next();
}
User.findById(req.session.user._id)
.then(user => {
if (!user) {
return next();
}
req.user = user;
next();
})
.catch(err => {
next(new Error(err));
});
});
app.use('/admin', adminRoutes);
app.use(shopRoutes);
app.use(authRoutes);
app.get('/500', errorController.get500);
app.use(errorController.get404);
app.use((error, req, res, next) => {
// res.status(error.httpStatusCode).render(...);
// res.redirect('/500');
res.status(500).render('500', {
pageTitle: 'Error!',
path: '/500',
isAuthenticated: req.session.isLoggedIn
});
});
mongoose
.connect(MONGODB_URI)
.then(result => {
app.listen(3000);
})
.catch(err => {
console.log(err);
});
Is any of the packages that i am requiring above deprecated? Or should i include new lines of code that i am missing?
I remember having similar issue, so first of all make sure your mongoose version is above 5.7.1, if it is you can pass the options objects suggested from your warning message to your connect function like so:
mongoose
.connect(MONGODB_URI, {useNewUrlParser: true, useUnifiedTopology: true})
.then(result => {
app.listen(3000);
})
.catch(err => {
console.log(err);
});

Cannot remote access to MongoDB Atlas from anywhere except my house

I am working on a full stack MERN app.
When I am working on the project from my home, everything works fine.
However, as soon as I change my location to somewhere else(different IP adress) like a cafe, the connection to mongoDB fails..
This is what I have tried:
1,Add the IP address of the new location to the network access of my DB => doesn’t work
2, Add “allow access from anywhere” to the network access => doesn’t work
3, disabled firewall on my MacBookAir => doesn’t work
4, create a new user in the DB and try to use the new user login => doesn’t work
5, I am not using any proxy.
I am using macOS BigSur(version 11.5.2)
this is my index.js file
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const helmet = require("helmet");
const morgan = require("morgan");
const userRoute = require("./routes/users");
const auth = require("./routes/auth");
const postRoute = require("./routes/posts");
const motorRoute = require("./routes/motors");
const multer = require("multer");
const path = require("path");
var cors = require("cors");
dotenv.config();
//connect to the DB here
mongoose.connect(
process.env.MONGO_URL,
{ useNewUrlParser: true, useUnifiedTopology: true },
() => {
console.log("connected to MongoDB");
}
);
app.use("/images", express.static(path.join(__dirname, "public/images/")));
app.use(express.json());
// app.use(helmet());
app.use(morgan("common"));
// app.use(cors({ origin: true, credentials: true }));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
if (req.path == "/api/upload") {
console.log("yes the path is /api/upload");
cb(null, "public/images/posts/");
}
if (req.path == "/api/upload/profile") {
console.log("yes the path is /api/upload/profile");
cb(null, "public/images/person/");
}
// cb(null, "public/images");
},
filename: (req, file, cb) => {
cb(null, file.originalname);
},
});
//upload post from share
const upload = multer({ storage });
app.post("/api/upload", upload.single("file"), (req, res) => {
try {
return res.status(200).json("File uploaded successfully");
} catch (error) {
console.log(error);
}
});
///upload profile picture
const upload2 = multer({ storage });
app.post("/api/upload/profile", upload2.single("file"), (req, res) => {
try {
return res.status(200).json("File uploaded successfully");
} catch (error) {
console.log(error);
}
});
//get all the users
app.get("/api/allusers", (req, res) => {
try {
console.log("debug: I will try to get all the users");
return res.status(200).json(" will return users here");
} catch (error) {
console.log(error);
}
});
//set some routes in here
app.use("/api/users", userRoute);
app.use("/api/auth", auth);
app.use("/api/posts", postRoute);
app.use("/api/motors", motorRoute);
app.listen(8800, () => {
console.log("Backend server is running");
});
Please let me know if you need more information about it.
Thank you
You are only allowed to connect from whitelisted IPs.
https://www.mongodb.com/docs/atlas/security/ip-access-list/

Why doesn't work the multer as a middleware?

I the app.js there is a direct route app.post('/upload', upload.single('image'), (req, res) => { res.json({ message: 'pic uploaded' }); });
When I make a post request to /upload, the image will be uploaded. However, when I use it like app.use('/phones', phoneRoutes); then the image won't be uploaded. Can you tell me where is the mistake? In both files everything is the same, but as I said, it works only in the app.js.
app.js:
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const phoneRoutes = require('./routes/phoneRoute');
const multer = require('multer');
const cors = require('cors');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './images');
},
filename: (req, file, cb) => {
console.log('file: ', file);
cb(null, Date.now() + file.originalname);
},
});
const upload = multer({ storage: storage });
//initialize
const app = express();
//middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
//routes
app.post('/upload', upload.single('image'), (req, res) => {
res.json({ message: 'pic uploaded' });
});
app.use('/phones', phoneRoutes);
//connect to mongodb
mongoose
.connect(process.env.MONGO_URI)
.then(() => {
app.listen(process.env.PORT);
console.log('Connected to mongodb: ', process.env.PORT);
})
.catch((err) => console.log(err));
phoneRoutes.js:
const router = require('express').Router();
const { createPhone } = require('../controllers/phoneController');
const multer = require('multer');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, '../images');
},
filename: (req, file, cb) => {
console.log('file: ', file);
cb(null, Date.now() + file.originalname);
},
});
const upload = multer({ storage: storage });
router.get('/:id', (req, res) => {
res.json({ message: 'get phone: ' + req.params.id });
});
router.get('/', (req, res) => {
res.json({ message: ' all Phones' });
});
router.delete('/:id', (req, res) => {
res.json({ message: 'phone ' + req.params.id + ' is deleted' });
});
router.post('/', upload.single('image'), (req, res) => {
res.json({ message: 'pic is uploaded' });
});
module.exports = router;
Your statement
cb(null, '../images');
contains a relative path, which is probably evaluated relative to the current working directory of the Node.js process. It is safer to make it relative to the directory containing the Javascript file phoneRoutes.js:
cb(null, __dirname + '/../images');

"message": "ENOENT: no such file or directory, open 'E:\\astrology\\utils\\uploads\\1600798534862qf.png'"

As the title suggests, I am getting error: {"message": "ENOENT: no such file or directory, open 'E:\\astrology\\utils\\uploads\\1600798534862qf.png'"} in my project even after passing every required configs.
Note: I've divided 'app' into two parts: main 'app.js' and 'appRoute.js' for more dynamic and code clarity.
app.js
const express = require("express");
const path = require("path");
const app = express();
const directory = path.join(__dirname, "utils/uploads");
app.use("/uploads", express.static(directory));
require("./config/database/db")();
require("./config/approutes/appRoutes")(app);
module.exports = app;
appRoute.js
require("dotenv").config();
const morgan = require("morgan");
const bodyParser = require("body-parser");
const cors = require("cors");
const productRoutes = require("../../api/routes/products");
module.exports = function (app) {
app.use(morgan("dev"));
app.use(bodyParser.urlencoded({ extended: true, limit: "100mb" }));
app.use(bodyParser.json());
app.use(cors({ credentials: true, origin: true }));
app.use("/products", productRoutes);
app.use((req, res, next) => {
const error = new Error("Not found");
error.status = 404;
next(error);
});
app.use((error, req, res, next) => {
console.log('SHOW ERROR', error);
res.status(error.status || 500);
res.json({
error: {
message: error.message,
},
});
});
};
fileUpload.js
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, __dirname + "/uploads");
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname.replace(/\s+/g, "-"));
},
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({
storage,
limits: {
fileSize: 1024 * 1024 * 5,
},
fileFilter: fileFilter,
});
module.exports = upload;
Product.js (controller)
exports.create_product = async (req, res, next) => {
const { title, min_content, content } = req.body;
console.log("req files", req.files);
try {
const product = new Product({
title,
min_content,
content,
});
const new_product = await product.save();
console.log("error caught", new_product);
if (new_product) {
res.status(201).json({ msg: "New product added", new_product });
} else {
res.status(400).json({ msg: "Unable to create new product" });
}
} catch (error) {
res.status(500).json({ msg: "Internal server error", error });
}
};
Product.js (route)
const express = require("express");
const router = express.Router();
const ProductController = require("../controllers/products");
const uploadMW = require("../middleware/fileUpload");
router.get("/all", ProductController.get_products);
router.post("/new", uploadMW.fields([{ name: "thumbnail" }, { name: "image" }]), ProductController.create_product
);
module.exports = router;
Directory structure
My OS is windows, so I have included the config to replace the (:) from file names, but nothing seems to work for me. Any help to resolve the same is appreciated.
app.js file will be like this
// modules =================================================
var express = require('express');
var app = express();
const logger = require('morgan');
var bodyParser = require('body-parser');
const indexRouter = require("./routes/index");
const cors = require('cors');
const path = require("path");
config = require("./environments/index");
var http = require('http').Server(app);
// configuration ===========================================
var port = config.PORT || 8081; // set our port
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({
limit: '50mb',
extended: true,
parameterLimit: 50000
}));
app.use(cors());
app.use(logger('dev'))
app.use("/api", indexRouter);
app.use(express.static(path.join(__dirname, "/build")));
http.listen(port, () => {
console.log('Magic happens on port ' + port); // shoutout to the user
});
exports = module.exports = app;
create multerhelper.js file in your app nd add below code in it
const multer = require('multer');
// const fs = require('fs');
let fs = require('fs-extra');
let storage = multer.diskStorage({
destination: function (req, file, cb) {
let path = `/uploads`;
fs.mkdirsSync(path);
cb(null, __dirname + path);
},
filename: function (req, file, cb) {
// console.log(file);
cb(null, Date.now() + file.originalname.replace(/\s+/g, "-"));
}
})
var upload = multer({ storage: storage });
let createUserImage = upload.single('images');
let multerHelper = {
createUserImage,
}
module.exports = multerHelper;
In your product.js(route) file import this file
const multerhelper = require("../multerhelper.js");
router.post("/new",multerhelper.createUserImage,ProductController.
create_product);
Your directory structures look messed up.
Your uploads directory in fileUpload.js
is referring to a directory in ./api/middleware/uploads when it should be referencing ./utils/uploads (the paths relative to the module are incorrect).
// fileUpload.js
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.resolve(__dirname, `..${path.sep}..${path.sep}`, `${path.sep}utils${path.sep}uploads`);
},
__dirname returns the directory that the current file is in. So in this case it will be middleware. (I am deducing this based on the require in your Product.js file).
Try fixing the path to the uploads directory in the fileUpload.js by pointing at the same directory that the static files are being served from.
Also, I seen in the chat that Arya started with you that you are now managing to get it working on a *nix OS but not on windows.
Try changing your path separators to use path.sep from the path module. Arya's solution above looks good, i.e. the file paths now all look fixed relative to the static uploads directory. I have updated my answer to use path.sep based on the original code that you published and the directory structure that you provided.
Finally I was able to resolve this. I am posting the solution if anyone comes across the same issue (Windows OS to be specific), this will surely help.
I changed my directory structure by shifting the uploads folder directly outside instead of /utils/uploads(for the sake of easeness) & made a minor change in fileUpload.js:
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, process.cwd() + "/uploads/");
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname.replace(/\s+/g, "-"));
},
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({
storage,
limits: {
fileSize: 1024 * 1024 * 5,
},
fileFilter: fileFilter,
});
module.exports = upload;
Instead of using __dirname, I replaced it with process.cwd(), and appended my target folder :
destination: function (req, file, cb) {
cb(null, process.cwd() + "/uploads/");
},
Reference: What's the difference between process.cwd() vs __dirname?
P.S. Thanks to #jeeves & #Arya for their thorough suggestions :)

increase express post body limit to proxied api

I have a seperate frontend and backend, where all requests to http://frontend.com/api are proxied to the backend. However we allow image uploads to be 10mb max, which gets limited by the 1mb internal limit of express on all request bodies.
I have the following config:
const express = require('express');
const consola = require('consola');
const { Nuxt, Builder } = require('nuxt');
const helmet = require('helmet');
// Express
const app = express();
const host = process.env.HOST || '127.0.0.1';
const port = process.env.PORT || 8080;
app.set('port', port);
// Import and Set Nuxt.js options
const config = require('../nuxt.config.js');
config.dev = !(process.env.NODE_ENV === 'production');
async function start() {
// Init Nuxt.js
const nuxt = new Nuxt(config);
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
}
// NOTE: Only in production mode
if (!config.dev) {
// Helmet default security + Referrer + Features
app.use(helmet());
}
// Proxy /api to proper backend
app.use('/api', proxy(process.env.API_ENDPOINT || 'http://localhost:3000'));
// Give nuxt middleware to express
app.use(nuxt.render);
// Listen the server
app.listen(port, host);
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true,
});
}
start();
I have tried adding body-parser, until I found out this only works for non multipart/form type of requests. Considering that this isn't an express backend, but only used to serve SSR (with nuxt), I have no idea how to get this to work with something like multer or busboy.
Can this be done without having to setup nginx as a reverse proxy?
Express itself doesn't impose any limits on body size, because it doesn't process the request body at all.
However, some middleware do impose a limit, like body-parser and express-http-proxy, which is what you're using.
To increase the limit to 10MB:
app.use('/api', proxy(process.env.API_ENDPOINT || 'http://localhost:3000', {
limit: '10mb'
));
The way mine works is I define my api base url in a config file which I reference in an api/init.js file. This file is added to plugins in nuxt.config.js. This is that file:
import axios from 'axios'
import {baseURL} from '~/config'
import cookies from 'js-cookie'
import {setAuthToken, resetAuthToken} from '~/utils/auth'
import { setUser, setCart } from '../utils/auth'
axios.defaults.baseURL = baseURL
const token = cookies.get('x-access-token')
const currentUser = cookies.get('userDetails')
const currentCart = cookies.get('userCart')
if (token) {
setAuthToken(token)
setUser(currentUser)
setCart(currentCart)
} else {
resetAuthToken()
}
The backend runs on it's own server which I launch with node index.js and it is the base url that my init.js looks for. The backend index.js looks like this:
const mysql = require('mysql')
const express = require('express')
const bodyParser = require('body-parser')
const config = require('./config')
const jwt = require('jsonwebtoken')
const bcrypt = require('bcrypt')
const multer = require('multer')
const auth = require('./auth')
const files = require('./files')
const create = require('./create')
const get = require('./get')
const delet = require('./delet')
const blogFiles = require('./blogFiles')
const db = mysql.createConnection(config.db)
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS')
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, x-access-token, userDetails, userCart')
if (req.method === 'OPTIONS') {
res.sendStatus(200)
}
else {
next()
}
})
app.use((err, req, res, next) => {
if (err.code === 'LIMIT_FILE_TYPES') {
res.status(422).json({ error: 'Only images are allowed'})
return
}
if (err.code === 'LIMIT_FILE_SIZE') {
res.status(422).json({ error: `Too Large. Max filesize is ${MAX_SIZE/1000}kb` })
return
}
})
app.use('/auth', auth({db, express, bcrypt, jwt, jwtToken: config.jwtToken}))
app.use('/files', files({db, express, multer}))
app.use('/blogFiles', blogFiles({db, express, multer}))
app.use('/create', create({db, express}))
app.use('/get', get({db, express}))
app.use('/delet', delet({db, express}))
app.get('/test', (req, res) => {
db.query('select 1+1', (error, results) => {
if (error) {
return res.status(500).json({type: 'error', error})
}
res.json({type: 'success', message: 'Test OK', results})
})
})
app.listen(config.port)
console.log('App is running on port ' + config.port)
The files.js handles file uploads and as you can see index.js requires that. It is in there that I use multer to handle the upload limit and such. This is file.js
module.exports = ({db, express, multer }) => {
const routes = express.Router()
const fileFilter = function(req, file, cb) {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
if (!allowedTypes.includes(file.mimetype)) {
const error = new Error('Wrong file type')
error.code = 'LIMIT_FILE_TYPES'
return cb(error, false)
}
cb(null, true)
}
const MAX_SIZE = 250000
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '../frontend/assets/images')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const upload = multer ({
storage: storage,
fileFilter,
limits: {
fileSize: MAX_SIZE
},
})
routes.post('/upload', upload.single('file'), (req, res) => {
res.json({ file: req.file })
})
return routes
}
As you can see I set the MAX_SIZE for my file uploads here so guess you can set any limit and as multer is handling it, it will over ride any limits set by express.

Resources