Edit: I solved it. It was pretty simple, I had imported the routes before setting app.use for body-parser, so it didn't knew hot to parse JSON and thus returned an undefined body.
I'm trying to make a REST API following a tutorial. I made everything exactly as it was done in it, and tried every fix I could think of, but I can't make JSON POST requests work.
After sending a request I'm supposed to get a JSON like this:
{
"_id": "a13d1s2bc12as3a2",
"name": "Name",
"desc": "bla bla",
"_v": 0
}
But instead I'm only getting a 201 Resource with an empty body. Not even an empty object, just nothing.
I tried all the possible configurations in Postman and also HTTPie.
I also tried the suggested fixes changing body-parser config, since I got a clue something was deprecated or changed in the last months (json, urlencoded, etc.)
I checked if Mongoose was connected to the DB using the function it has for checking it (it was).
I have no clue where's the problem.
This is the index:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const meals = require("./routes/meals");
const orders = require("./routes/orders");
const app = express();
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true });
app.use("/meals", meals);
app.use("/orders", orders);
app.use(express.json());
app.use(bodyParser.json({ type: 'application/json' }));
module.exports = app;
The routes for Meals:
const express = require("express");
const Meals = require("../models/Meals");
const router = express.Router();
router.get("/", (req, res) => {
Meals.find()
.exec()
.then(x => res.status(200).send(x));
});
router.get("/:id", (req, res) => {
Meals.findById(req.params.id)
.exec()
.then(x => res.status(200).send(x));
router.post("/", (req, res) => {
Meals.create(req.body)
.then(x => res.status(201).send(x));
});
router.put("/:id", (req, res) => {
Meals.findOneAndUpdate(req.params.id, req.body)
.then(x => res.sendStatus(204));
});
router.delete("/:id", (req, res) => {
Meals.findOneAndDelete(req.params.id)
.exec()
.then(() => res.sendStatus(204));
});
module.exports = router;
And the model:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Meals = mongoose.model("Meal", new Schema({
name: String,
desc: String
}));
module.exports = Meals;
Thank you.
To debug try this
const handleError = function() {
console.error(err);
// handle your error
};
router.post("/", (req, res) => {
Meals.create(req.body, function (err, req.body) {
if (err) return handleError(err);
})
});
You can also try to just provide static data for testing and see if that works, like :
const meal = new Meals({ name: 'some meal',desc: 'some desc' });
meal.save(function (err) {
if (err) return handleError(err);
// saved! return 200 or whatever you needs to do here
});
I solved it. It was pretty simple, I had imported the routes before setting app.use for body-parser, so it didn't knew hot to parse JSON and thus returned an undefined body.
app.use(bodyParser.json());
app.use("/meals", meals);
app.use("/orders", orders);
module.exports = app;
Related
Follow the code below, everything is working fine with other collections that I am using exactly the same code and handlerFactory as well with the GET call for this one but when I try to delete and update it is getting a 404 error, it is not a typo because GET is working well and the same in the handlerFactory.js as all CRUD operations are working fine with this same code and other collection:
app.js
var userRouter = require('./routes/userRoutes');
// Routes Definitions
app.use('/', viewRouter);
app.use('/api/v1/aircraft', aircraftRouter);
app.use('/api/v1/users', userRouter);
UserRoutes.js
const express = require('express');
const userController = require('../controllers/userController');
const router = express.Router();
router.get('/', userController.getAllUsers);
router
.get('/:id', userController.getUser)
.delete(userController.deleteUser)
.patch(userController.UpdateUser);
module.exports = router;
UserController.js
const user = require('../models/userModel');
const factory = require('./handlerFactory');
exports.getAllUsers = factory.getAll(user);
exports.getUser = factory.getOne(user);
exports.deleteUser = factory.deleteOne(user)
exports.UpdateUser = factory.updateOne(user);
handlerfactory.js
const catchAsync = require('../utils/catchAsync');
const AppError = require('../utils/appError');
const APIFeatures = require('../utils/apiFeatures');
exports.deleteOne = (Model) =>
catchAsync(async (req, res, next) => {
const doc = await Model.findByIdAndDelete(req.params.id);
if (!doc) {
return next(new AppError('No document found with that ID', 401));
console.log('No document found with that ID');
}
res.status(204).json({
status: 'success',
data: null,
});
});
exports.updateOne = (Model) =>
catchAsync(async (req, res, next) => {
const doc = await Model.findByIdAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true,
});
if (!doc) {
return next(new AppError('No document found with that ID', 404));
}
res.status(200).json({
status: 'success',
data: {
data: doc,
},
});
});
I will answers my own question here because Daniel just come to my attention a silly mistake that I was making but I think it can still be useful to others.
If you just wanna one route per line use:
router.update('/:id', userController.updateOne);
If you are trying to use multiple routes in the same line do not forget to use .route like in the code below:
router.route('/:id').get(userController.getUser)
.delete(userController.deleteUser)
.patch(userController.UpdateUser);
Thank you very much, it may be silly but silly things happen with code every day.
Thank you all!
So I feel foolish for asking this, and I know there are a ton of related posts on this, but I cannot find anything to work. I'm guessing that it has to do with the index.js since Postman is saying it cannot even connect to the route. I have 2 routes, and the clientRoutes works just fine, postman returns and it shows up on my frontend. However, making any call to the contractRoutes gets me nothing.
I'm trying to pull all 'contracts' subdocs for a single client. I'm new to Express/Mongoose and I'm guessing that I've missed something totally obvious.
index.js
const express = require("express")
const mongoose = require("mongoose")
const cors = require('cors')
const clientRoutes = require("./routes/clientRoutes")
const contractRoutes = require("./routes/contractRoutes")
const bodyParser = require('body-parser');
mongoose
.connect("mongodb+srv://admin:oneterra#cluster0.0cajn.mongodb.net/Octotest?retryWrites=true&w=majority", { useNewUrlParser: true })
.then(() => {
const app = express()
app.use(express.json())
app.use(cors())
app.use(bodyParser.json());
app.use("/api", clientRoutes)
app.use("/api", contractRoutes)
app.listen(5000, () => {
console.log("Server has started")
})
})
client model
const mongoose = require("mongoose")
const schema = mongoose.Schema({
clientId: Number,
firstName: String,
lastName: String,
phone: String,
contracts: [{
contractId: Number,
authNumber: String,
contType: String,
contHours: Number,
contStartDate: Date,
contEndDate: Date
}],
})
module.exports = mongoose.model("Client", schema)
clientRoutes - which works as expected
const express = require("express")
const Client = require("../models/Client.js")
const router = express.Router()
//Client routes
router.get("/clients", async (req, res) => {
const clients = await Client.find()
res.send(clients)
})
router.get("/clients/:clientId", async (req, res) => {
try {
const client = await Client.findOne({ clientId: req.params.clientId })
res.send(client)
} catch {
res.status(404)
res.send({ error: "Client not found"})
}
})
contractRoutes which only brings the error "Cannot GET /api/clients/1/contracts" (1 being the clientId, which has contracts in the db). On clientRoutes, from the first tutorial I used, I did not put '' around ({ clientId : req.params.clientId). In the code below I have it there when I was trying to figure this out, but I get the same result, and again seems to show I'm missing something at the top level.
const express = require("express")
const Client = require("../models/Client")
const router = express.Router()
try{
const client = await Client.findOne({ 'clientId': req.params.clientId })
const contracts = client.contracts;
res.send(contracts)
} catch {
res.status(404)
res.send({error: "Contracts not found"})
}
console.log(contracts)
I've tried using populate
Client.findOne({ 'clientId': req.params.clientId })
.populate('contracts')
.exec(
function(err, client) {
if (err) res.status(500).send(err);
res.json(client.contracts);
}
);
and even if I copy the exact same route for a single client from clientRoutes, but with the contracts endpoint, I get the same error as above
const express = require("express")
const Client = require("../models/Client")
const router = express.Router()
//Client routes
router.get("/clients/:clientId/contracts", async (req, res) => {
try {
const client = await Client.findOne({ clientId: req.params.clientId })
res.send(client)
} catch {
res.status(404)
res.send({ error: "Client not found"})
}
})
Any help is greatly appreciated, I've spent hours running in circles on this and trying every type of different way to make the call. But in dialing it down to even using the same route from clientRoutes but with the contracts endpoint and getting the error, I'm assuming it has to due with the index.js server connection.
So in case anyone comes across this with the same issue, as I put in the comment, express wasn't accepting the 2nd route starting with the same path.
By changing
app.use("/api", clientRoutes)
app.use("/api", contractRoutes)
to
app.use("/api", clientRoutes)
app.use("/api/clients", contractRoutes)
then the route worked fine. I did not see anything in the express docs saying that no path can be designated as the same, but making the change made it work, so there's that.
Between my app and Postman I should be able to make an entry into MongoDB, but I get this.
TypeError: Cannot read property 'title' of undefined
This is the app.js file
const express = require('express');
const app = express();
const Mongoose = require('mongoose');
require('dotenv/config')
const postsRoute = require('./routes/posts');
app.use('/posts', postsRoute)
app.use('/posts', () => {
console.log('This is a middleware.')
})
app.get('/', (req, res) => {
res.send('We are at home')
})
//Connect to DB
Mongoose.connect(process.env.DB_CONNECTION)
.then(() => {
console.log('connected to DB')
})
.catch(err => {
console.log(err);
});
app.listen(3000);
This is the posts.js file
const express = require('express');
const router = express.Router();
const Post = require('../models/Post')
var fs = require('fs');
router.get('/', (req, res) => {
res.send('We are at posts')
})
router.post('/', (req, res) => {
const post = new Post({
title: req.body.title,
description: req.body.description
});
post.save()
.then(data => {
res.json(data);
})
.catch(err => {
res.json({message: err });
})
});
module.exports = router;
This file is the Mongoose Schema
const { json } = require('body-parser');
const mongoose = require('mongoose');
const PostSchema = mongoose.Schema({
title: {
type: JSON,
required: true
},
description: {
type: JSON,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Posts', PostSchema);
Then I'm trying to post to MongoDB, through the app with Postman
Hopefully this photo should suffice.
The app is running fine locally on port 3000 and I do believe I am connecting to the database. It seems like a format problem. I don't know if the schema needs to be in JSON somehow or a configuration of Postman needs to change, but I have tried every Postman setting and tried changing the schema to JSON. I also had the type in the Mongoose file as type: String
Add this in app.js for POST Request...
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
Add that middleware on top of your routes.
I have set up my project to display posts from a MongoDB database. My localhost address is http://localhost:5000/api/posts and it displays my two saved posts. How can I add MongoDB _id to localhost adress to only display one post?
MongoDB _id: 6061890d59ec3b6abcb011fb
I have tried this:
http://localhost:5000/api/posts/6061890d59ec3b6abcb011fb
http://localhost:5000/api/posts/id:6061890d59ec3b6abcb011fb
http://localhost:5000/api/posts/_id:6061890d59ec3b6abcb011fb
All of them returns error Cannot GET /api/posts/and_the_above_parameters_for_each_example`
Index.js to connect my backend to my application.
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
//Middleware
app.use(bodyParser.json());
app.use(cors());
const posts = require("./routes/api/posts");
app.use("/api/posts", posts);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
posts.js to connect to MongoDB database. Below Password, MY_DATABASE and TABLE is changed to real values in my code.
const express = require("express");
const mongodb = require("mongodb");
const router = express.Router();
//Get posts
router.get("/", async (req, res) => {
const posts = await loadPostCollection();
res.send(await posts.find({}).toArray());
});
//Add post
router.post("/", async (req, res) => {
const posts = await loadPostCollection();
await posts.insertOne({
text: req.body.text,
createdAt: new Date(),
});
res.status(201).send();
});
router.delete("/:id", async (req, res) => {
const posts = await loadPostCollection();
await posts.deleteOne({
_id: req.params.id,
});
res.status(200).send();
});
async function loadPostCollection() {
const client = await mongodb.MongoClient.connect(
"mongodb+srv://MongoDB:PASSWORD#cluster0.5pnzd.mongodb.net/MY_DATABASE?retryWrites=true&w=majority",
{
useNewUrlParser: true,
useUnifiedTopology: true,
}
);
return client.db("MY_DATABASE").collection("TABLE");
}
module.exports = router;
PostService.js to display posts on localhost and methods to post and delete.
import axios from "axios";
const url = "http://localhost:5000/api/posts/";
class PostService {
// Get posts
static getPosts() {
return new Promise((resolve, reject) => {
axios
.get(url)
.then((res) => {
const data = res.data;
resolve(
data.map((post) => ({
...post, //spread operator
createdAt: new Date(post.createdAt),
}))
);
})
.catch((err) => {
reject(err);
});
});
}
// Create posts
static insertPost(text) {
return axios.post(url, {
text,
});
}
static deletePost(id) {
return axios.delete(`${url}${id}`);
}
}
export default PostService;
router.get("/:id", async (req, res) => {
const posts = await loadPostCollection();
res.send(await posts.findOne({
_id: req.params.id,
}));
});
Number 1: http://localhost:5000/api/posts/6061890d59ec3b6abcb011fb is correct, but you're going to need to create a new route to handle that request.
These are often called 'show' routes.
router.get("/:id", async (req, res) => {
// code to handle the logic of that request
// access the url parameter via: req.params.id
});
I want to get an user by his userId parameter but it doesn't work. The app connects to the database(Atlas), I can create users, retrieve them all in bulk but I can't retrieve them with a specific parameter ( in this case UserId), producing a not found error message.
UserRoutes.js
const express = require('express');
const UserModel = require('../models/UserModel');
const app = express();
app.get('/getusersById/:userId', async (req, res) => {
const user = await UserModel.find(req.params.userId);
try {
res.send(user);
} catch (err) {
res.status(500).send(err);
}
});
UserModel.js
const mongoose = require('mongoose');
// Define Schema
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
age: {
type:Number,
required:true
},
userId: {
type:String,
required:true
},
});
//
const User = mongoose.model('user', UserSchema);
module.exports = User;
Server.js
// Open connection to test database
const express = require('express');
const mongoose = require('mongoose');
const UserRouter = require('./routes/UserRoutes.js');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const uri = "mongodb+srv://XXXXXXXXXXX#XXXXXXXeXXXX.gcp.mongodb.net/dbusers?retryWrites=true&w=majority";
mongoose.connect(uri,{
userNewUrlParser: true,
useUnifiedTopology: true
});
// Notify if connection was succesful
var db = mongoose.connection;
db.on ('error', console.error.bind(console, 'connection error'));
db.once('open', function() {
console.log("Connection Succesful");
});
db.on('close', () => {
db.removeAllListeners();
});
// Router & Port
app.use(UserRouter);
app.listen(3000,
() => {console.log('Server is running...')});
And this is the postman request:
Postman get userId
Your request is wrong. You defined your path as:
app.get('/getusersById/:userId', /* callback */)
So the request URL should be:
/getusersById/3
and not
/getusersById/?userId=3
'/getusersById/:userId'
What you are doing here is parameters of your request, which is userId
the correct usage of this api is /getusersById/3
app.get('/getusersById/:usersId', async (req, res) => {
const user = await UserModel.find(req.params.usersId );
However it seems you want to use ?usersId=3 for query the user id
You need to use req.query.usersId
app.get('/getusersById', async (req, res) => {
const user = await UserModel.find(req.query.usersId );
You can find examples of query usage : https://coderrocketfuel.com/article/handle-get-request-query-string-parameters-in-express-js
I think you are new to API development. From the image that I can see that you are sending userId as a query parameter. But in code, you are doing req.parms.userId which is used for path params. In your code you defined route for path parameter so the request should be like this:
/getusersById/3
And to be handled as below
app.get('/getusersById/:userId', async (req, res) => {
const user = await UserModel.find(req.params.userId );
However, If you want to pass userId in query parameter then do this:
app.get('/getusersById', ...)
request can be made like this:
/getusersById/?userId=3
and query parameter will be accessible as below:
app.get('/getusersById', async (req, res) => {
const user = await UserModel.find(req.query.userId );
Read this: Query vs Path params