Node Express - Cannot POST / error - node.js

I have been following a tutorial on how to build a full stack MEAN application and everything has been functioning fine up until this point (registration, login and authentication.)
I'm now trying to do a post request for the blog page and am receiving the following error:
<pre>Cannot POST /blogs/newBlog</pre>
All that I've done so far is create a schema, a route and then made the relevant changes to index.js. The schema file provided below is the one provided by the author of the tutorial in his respository (unlike the other two files it is in it's completed form.) The problem still persists and so I do not believe it to be the problem.
Blog schema:
/* ===================
Import Node Modules
=================== */
const mongoose = require('mongoose'); // Node Tool for MongoDB
mongoose.Promise = global.Promise; // Configure Mongoose Promises
const Schema = mongoose.Schema; // Import Schema from Mongoose
// Validate Function to check blog title length
let titleLengthChecker = (title) => {
// Check if blog title exists
if (!title) {
return false; // Return error
} else {
// Check the length of title
if (title.length < 5 || title.length > 50) {
return false; // Return error if not within proper length
} else {
return true; // Return as valid title
}
}
};
// Validate Function to check if valid title format
let alphaNumericTitleChecker = (title) => {
// Check if title exists
if (!title) {
return false; // Return error
} else {
// Regular expression to test for a valid title
const regExp = new RegExp(/^[a-zA-Z0-9 ]+$/);
return regExp.test(title); // Return regular expression test results (true or false)
}
};
// Array of Title Validators
const titleValidators = [
// First Title Validator
{
validator: titleLengthChecker,
message: 'Title must be more than 5 characters but no more than 50'
},
// Second Title Validator
{
validator: alphaNumericTitleChecker,
message: 'Title must be alphanumeric'
}
];
// Validate Function to check body length
let bodyLengthChecker = (body) => {
// Check if body exists
if (!body) {
return false; // Return error
} else {
// Check length of body
if (body.length < 5 || body.length > 500) {
return false; // Return error if does not meet length requirement
} else {
return true; // Return as valid body
}
}
};
// Array of Body validators
const bodyValidators = [
// First Body validator
{
validator: bodyLengthChecker,
message: 'Body must be more than 5 characters but no more than 500.'
}
];
// Validate Function to check comment length
let commentLengthChecker = (comment) => {
// Check if comment exists
if (!comment[0]) {
return false; // Return error
} else {
// Check comment length
if (comment[0].length < 1 || comment[0].length > 200) {
return false; // Return error if comment length requirement is not met
} else {
return true; // Return comment as valid
}
}
};
// Array of Comment validators
const commentValidators = [
// First comment validator
{
validator: commentLengthChecker,
message: 'Comments may not exceed 200 characters.'
}
];
// Blog Model Definition
const blogSchema = new Schema({
title: { type: String, required: true, validate: titleValidators },
body: { type: String, required: true, validate: bodyValidators },
createdBy: { type: String },
createdAt: { type: Date, default: Date.now() },
likes: { type: Number, default: 0 },
likedBy: { type: Array },
dislikes: { type: Number, default: 0 },
dislikedBy: { type: Array },
comments: [{
comment: { type: String, validate: commentValidators },
commentator: { type: String }
}]
});
// Export Module/Schema
module.exports = mongoose.model('Blog', blogSchema);
routes/blogs.js
const User = require('../models/user'); // Import User Model Schema
const jwt = require('jsonwebtoken');
const config = require('../config/database');
module.exports = (router) => {
router.post('/newBlog', (req, res) => { // TODO: change URL
res.send('test worked');
});
return router; // Return router object to main index.js
}
index.js
/* ===================
Import Node Modules
=================== */
const env = require('./env');
const express = require('express');
const app = express();
const router = express.Router();
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
const config = require('./config/database');
const path = require('path');
const authentication = require('./routes/authentication')(router);
const blogs = require('./routes/blogs')(router);
const bodyParser = require('body-parser');
const cors = require('cors');
const port = process.env.PORT || 8080;
// Database Connection
mongoose.connect(config.uri, {
useMongoClient: true,
}, (err) => {
// Check if database was able to connect
if (err) {
console.log('Could NOT connect to database: ', err);
message
} else {
console.log('Connected to ' + config.db);
}
});
// Middleware
app.use(cors({ origin: 'http://localhost:4200' }));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(__dirname + '/public'));
app.use('/authentication', authentication);
app.use('/blogs', blogs);
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/public/index.html'));
});
// Start Server: Listen on port 8080
app.listen(port, () => {
console.log('Listening on port ' + port + ' in ' + process.env.NODE_ENV + ' mode');
});
I have been enjoying this course greatly and would appreciate any help (even if it is to simply rule out possible causes.)
Error in full:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /blogs/newBlog</pre>
</body>
</html>

Your problem has to do with:
app.use('/blogs', blogs);
The blogs function should be a function taking (req, res) but it actually takes (router).
You have two options:
Create a router and pass into blogs, e.g. app.use('/blogs', blogs(router)); or
Add app.post(), e.g.
app.post('/blogs/newBlog', (req, res) => {
res.send('test worked');
});

Check spelling of your route
In my case I was posting from a url that had a missing letter.
I was doing POST /webooks/ but I needed to do POST /webhooks (note the missing h letter).
So it may also be that you misspell your routes.

Just try to change from bodyparser to express means...
instead of
app.use(bodyParser.json())
use app.use(express.json())

Please replace this:
router.post('/newBlog', (req, res) => {
res.send('test worked');
});
With this (on all your methods "get, post, use" etc):
// Make sure to always add a slash both before and after the endpoint!
router.post('/newBlog/', (req, res) => {
res.send('test worked');
});
I encounter this issue mostly if I don't add endpoint slashes properly.

Related

POST going directly to the catch error and not saving data using mongoose, MongoDB, NodeJS, and Express

I already tried some possible solutions and even created and wrote the code again but I am still getting errors. I have created a diminute version of my whole code which connects to the database using Mongoose but after the Schema is created and I import the model in places-controllers my data that I write in POSTMAN goes directly to:
FYI: In this case I want POST request from createPlace to properly work.
Data entry: URL: http://localhost:5000/api/places/
{
"title": "Punta Arena Stfdsfdsfsdfop",
"description": "One stop Stop. Does not have tr12affic lights.",
"busrespect": "12ysdfdsfsfes",
"address": "Avenida Solunna",
"creator": "52peru soiflsdjf36"
}
OUTPUT:
{
"status": "error caught"
}
which is what I told the program to define if the try did not work.
IN app.js I have the following code:
const express= require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const placesRoutes = require("./routes/places-routes");
const HttpError = require ("./models/http-error");
const app = express();
app.use(bodyParser.json());
app.use('/api/places', placesRoutes);
app.use((req, res, next) => {
const error= new HttpError('Route not available. Try something different?', 404);
throw error;
});
app.use((error, req, res, next) =>{
if (res.headerSent) {
return next(error);
}
res.status(error.code || 500)
res.json({message: error.message || "An unknown error occured! Sorry" });
});
url = '<mongo_url>'
mongoose.connect(url, {useNewUrlParser: true}).then(()=>{
console.log("Connected to database")
app.listen(5000);
}).catch(erro => {
console.log(erro)
});
In places-routes.js I have the following code:
const express = require('express');
const {check} = require('express-validator')
const placesControllers=require('../controllers/places-controllers');
const router = express.Router();
router.get('/:pid', placesControllers.getPlaceById );
router.get('/user/:uid',placesControllers.getPlacesByCreatorId );
router.post('/' ,[
check('title')
.not()
.isEmpty(),
check('description').isLength({ min: 5 }),
check('address')
.not()
.isEmpty()
],
placesControllers.createPlace);
router.patch('/:pid', [
check('title')
.not()
.isEmpty(),
check('description').isLength({ min: 5 })
] , placesControllers.updatePlace );
router.delete('/:pid', placesControllers.deletePlace);
module.exports=router;
In places-controllers.js I have the following code:
const HttpError = require('../models/http-error');
const { validationResult } = require('express-validator');
//const getCoordsForAddress= require('../util/location');
const BusStop = require('../models/place');
let INITIAL_DATA = [
{
id: "p1",
title: "Samoa Stop",
description: "My first bus stop in Lima",
//location: {
// lat: 40.1382,
// lng:-23.23
// },
address: "Av. La Molina interseccion con calle Samoa",
busrespect: "yes",
creator: "u1"
}
];
const getPlaceById = (req, res, next) => {
const placeId = req.params.pid // Accessing the p1 in pid URL scrapping {pid:'p1'}
const place= INITIAL_DATA.find(p => { //find method goes over each element in the array, the argument p represents the element where find loop is
return p.id ===placeId
});
if (!place) {
const error= new HttpError('No bus stop found for the provided ID.', 404);
throw error;
}
res.json({place: place});
};
const getPlacesByCreatorId = (req, res, next)=> {
const userId = req.params.uid;
const places = INITIAL_DATA.filter(p=>{ //filter to retrieve multiple places, not only the first one
return p.creator ===userId;
});
if (!places || places.length===0) {
return next(
new HttpError('Could not find bus stops for the provide user id', 404)
);
}
res.json({places});
};
const createPlace = async (req, res,next) => {
const errors = validationResult(req);
if (!errors.isEmpty()){
return next(new HttpError ('Invalid bus stop please check your data', 422));
}
//const { title, description, busrespect, address, creator } = req.body; //erased location for now.
/* let place = new BusStop({
title: req.body.title,
description: req.body.description,
busrespect: req.body.busrespect,
address : req.body.address,
creator: req.body.creator
})
awaitplace.save()
.then(response=>{
res.json({
message : "Employee added sucessfully!"
})
})
.catch(err=>{
res.json({
message : "An error has occured!"
})
})
} */
const { title, description, busrespect, address, creator } = req.body;
try {
await BusStop.create({
title:title,
description: description,
busrespect:busrespect,
address: address,
creator: creator
});
res.send({status: "ok"});
} catch(error) {
res.send({status:"error caught"});
}
};
const updatePlace = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()){
console.log(errors);
throw new HttpError ("Invalid inputs passed, please check your data ", 422);
};
const { title, description } = req.body;
const placeId = req.params.pid;
const updatedPlace = { ...INITIAL_DATA.find(p => p.id === placeId)};
const placeIndex = INITIAL_DATA.findIndex(p => p.id === placeId);
updatedPlace.title = title;
updatedPlace.description = description;
INITIAL_DATA[placeIndex] = updatedPlace;
res.status(200).json({place: updatedPlace});
};
const deletePlace = (req, res, next) => {
const placeId = req.params.pid;
if (!INITIAL_DATA.find(p=> p.id ===placesId))
throw new HttpError('Could not find a bus stop for that ID ')
INITIAL_DATA = INITIAL_DATA.filter(p=> p.id !== placeId)
res.status(200).json({message: 'Deleted Place'});
};
exports.getPlaceById= getPlaceById;
exports.getPlacesByCreatorId = getPlacesByCreatorId;
exports.createPlace = createPlace;
exports.updatePlace = updatePlace;
exports.deletePlace = deletePlace;
Inside models folder I have two files: http-error.js which has this code:
class HttpError extends Error {
constructor(message, errorCode) {
super (message);
this.code = errorCode;
}
}
module.exports = HttpError;
The other file inside is the schema which is place.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const placeSchema = new Schema({
title: {
type: String
},
description: {
type: String
},
address: {
type: String
},
busrespect: {
type: String
},
creator: {
type: String
}
},
)
const BusStop = mongoose.model('BusStop', placeSchema)
module.exports= BusStop
Summary: somewhere in the try catch part from createPlace something is going wrong since my data entry is always going to the error status I indicated in that part.

Not receive any data from react to nodejs

I am trying to send data from front end to back end with react and nodejs. but when I console in front end it appear data but in backend it didn't get anything.
Here is my front end code:
const [imageSelected, setImageSelected] = useState("");
// Form submission
const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData();
formData.append("file", imageSelected);
const payload = { title, mission, isSearchable, userId: currentUser.id };
formData.append("payload", payload);
// Send post request for signup
const res = await axios.post("/api/v1/teams/team", formData, {
headers: { "Content-type": "multipart/form-data" },
});
// If no validation errors were found
if (res.data.validationErrors === undefined) {
// Clear any errors
setErrorsArr([]);
// Hide the errors component
setShowErrors(false);
// Toggle the modal
toggleModal();
// Go to team management page
NextRouter.push(`/team/${res.data}`);
} else {
// Set errors
setErrorsArr(res.data.validationErrors.errors);
// Show the errors component
setShowErrors(true);
}
};
<input
type="file"
className="team--modal_upload_avatar"
ref={inputFile}
onChange={(e) => setImageSelected(e.target.files[0])}
/>
<Grid item xs={12} className={classes.avatarDiv}>
<Avatar
aria-label="team"
className={classes.avatar}
component="div"
onClick={onButtonClick}
>
<AddAPhotoIcon className={classes.avatarIcon} />
</Avatar>
Here is my route:
const express = require("express");
const router = express.Router();
const { catchErrors } = require("../errors/errorHandlers");
const { body, param } = require("express-validator");
const cloudinary = require("cloudinary").v2;
const path = require("path");
const Datauri = require("datauri/parser");
const cloud_name = process.env.CLOUDINARY_NAME;
const cloud_api_key = process.env.CLOUDINARY_API_KEY;
const cloud_api_secret = process.env.CLOUDINARY_API_SECRET;
const cloud_url = process.env.CLOUDINARY_URL;
cloudinary.config({
cloud_name: cloud_name,
api_key: cloud_api_key,
api_secret: cloud_api_secret,
cloudinary_url: cloud_url,
});
// Controller
const {
createTeam,
getUsersTeams,
getManagedTeams,
getTeamCredits,
getTeamData,
updateTeamData,
shutdownTeam,
checkTeamPermissions,
checkTeamPermissionsAndLimits,
addMember,
removeMember,
addMemberBackToTeam,
quitTeam,
} = require("./teamsController");
const {
checkUserVerification,
restrictedRoute,
checkData,
} = require("../helpers/apiHelpers");
router.post(
"/team",
(req, res) => {
console.log(res);
console.log("req body", req.body);
console.log("req files", req.files);
console.log("req user", req.user);
const dUri = new Datauri();
const dataUri = (req) =>
dUri.format(path.extname(req.name).toString(), req.data);
if (req.files !== undefined && req.files !== null) {
const { file, id } = req.files;
const newFile = dataUri(file).content;
cloudinary.uploader
.upload(newFile, {
folder: "TeamAvatar",
})
.then((result) => {
const imageUrl = result.url;
const data = { id: req.body.id, imageUrl };
createTeam(data);
return res
.status(200)
.json({ message: "Success", data: { imageUrl } });
})
.catch((err) =>
res.status(400).json({ message: "Error", data: { err } })
);
} else {
return res.status(400).json({ message: "Error" });
}
},
restrictedRoute,
[
body(
"title",
"Only alphabetical characters, numbers, and spaces are allowed."
)
.not()
.isEmpty()
.isLength({ min: 1, max: 25 })
.trim()
.matches(/^[a-zA-Z0-9 ]+$/)
.blacklist("\\<\\>\\;\\[\\]\\{\\}\\|\\%\\=\\(\\)\\~\\#")
.escape(),
body("mission", "Only alphabetical characters and spaces are allowed.")
.not()
.isEmpty()
.trim()
.blacklist("\\<\\>\\;\\[\\]\\{\\}\\|\\%\\=\\(\\)\\~\\#")
.escape(),
],
checkData,
catchErrors(checkUserVerification),
catchErrors(createTeam)
);
Here is my controller that have create team function:
exports.createTeam = async (req, res) => {
// Get the user id from the session
const userId = req.session.passport.user.id;
console.log("body", req.body);
console.log("files", req.files);
console.log("file", req.file);
// Make sure user has the credits to create a new team
const teamInfo = await models.User.findOne({
where: {
id: userId,
},
attributes: ["teamCredits"],
});
if (teamInfo.dataValues.teamCredits <= 0) {
res.status(200).json({
validationErrors: {
errors: [
{
msg: "You don't have any more team credits.",
},
],
},
});
return;
}
const { title, mission } = req.body;
// const { picture } = req.imageUrl;
// Make sure the user hasn't already created a team with that title.
const existingTeam = await models.Team.findOne({
where: {
title: title,
creatorId: userId,
},
});
if (existingTeam !== null) {
// Response and let the user know.
res.status(200).json({
validationErrors: {
errors: [
{
msg: "You already created a team with that name.",
},
],
},
});
return;
}
// Generator a public team id
const firstLetter = title[0];
const secondLetter = title[1];
const thirdLetter = title[2];
const timePart = Date.now();
const generatedPublicId = `${firstLetter}${secondLetter}${thirdLetter}${timePart}`;
const roomEntry = {
name: title,
status: true,
};
const roomResponse = await models.Room.create({ ...roomEntry });
const defaultTeamValues = {
title: title,
type: "simple team",
mission: mission,
// picture: picture,
agreement: "default",
inputs: "",
outputs: "",
duration_in_months: 12,
status: "Seeking new members",
public_team_id: generatedPublicId,
mergedTo: null,
creatorId: userId,
date_closed: null,
current_members_count: 1,
current_invites_count: 0,
max_team_members_allowed: 10,
max_invites_allowed: 20,
roomID: roomResponse.dataValues.id,
};
// No existing team was found with that title and created by that user.
// Create team.
const team = await models.Team.create(defaultTeamValues);
const defaultRoleValues = {
title: "creator",
duties: "",
rights: "all",
};
// Create role for new team
const role = await models.Role.create(defaultRoleValues);
const defaultMembershipValues = {
interests: "",
contributions: "",
authorization: "creator",
status: "active",
application_letter: "",
date_applied: Sequelize.literal("CURRENT_TIMESTAMP"),
date_joined: Sequelize.literal("CURRENT_TIMESTAMP"),
date_departed: null,
memberId: userId,
teamId: team.dataValues.id,
roleId: role.dataValues.id,
};
// Create membership for team with role and team ids
await models.Membership.create(defaultMembershipValues);
const newCreditValue = teamInfo.dataValues.teamCredits - 1;
// Update team credits the user has.
await models.User.update(
{ teamCredits: newCreditValue },
{
where: {
id: userId,
},
}
);
// Done
res.status(200).json(team.dataValues.public_team_id);
};
Why my back end didn't get any data I sent from front end to back end?
Add files
server.js:
/* eslint-disable no-undef */
const express = require("express");
const next = require("next");
const dotenv = require("dotenv");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const compression = require("compression");
const bodyParser = require("body-parser");
const logger = require("morgan");
const session = require("express-session");
const SequelizeStore = require("connect-session-sequelize")(session.Store);
const passport = require("passport");
const helmet = require("helmet");
const sslRedirect = require("heroku-ssl-redirect");
const socketIo = require("socket.io");
const http = require("http");
const {
saveMessage,
getAllMessagedByRoomID,
} = require("./services/chat/chatController");
// Setup Next.js then run express.
app.prepare().then(() => {
// Setup express
const server = express();
const chatServer = http.createServer(server);
const io = socketIo(chatServer);
// Socket Connection Start
io.on("connection", (socket) => {
chatID = socket.handshake.query.chatID;
socket.join(chatID);
//Send message to only a particular user
socket.on("send_message", (message) => {
saveMessage(message);
io.in(message.roomID).emit("receive_message", {
content: message.content,
roomID: message.roomID,
userID: message.userID,
});
});
socket.on("get_all_messages", async ({ roomID }) => {
const allMessagedByRoomID = await getAllMessagedByRoomID(roomID);
io.in(roomID).emit("send_all_messages", {
allMessagedByRoomID,
});
});
});
// Redirect all traffic to use ssl(https);
server.use(sslRedirect());
// Define PORT
const port = process.env.PORT || 3000;
let serverMode = "development";
// Check if node is setup for production
if (!dev) {
serverMode = "production";
}
if (serverMode === "production") {
server.use(helmet());
}
// Logger
server.use(
logger("dev", {
skip: function (req, res) {
return res.statusCode < 199; // Only log 400 and 500 codes
},
})
);
// Use body parser
server.use(bodyParser.urlencoded({ extended: false }));
server.use(bodyParser.json());
// Compression
server.use(compression());
// Database
const db = require("./models/index");
const sequelize = db.sequelize;
// Test db connection
sequelize
.authenticate()
.then(() => {
console.log("Database successfully connected!");
})
.catch((err) => {
throw new Error(err);
});
// Sessions Setup
const sessionMaxTime = 1000 * 60 * 60 * 24 * 5; // 5 Days
// Session options
const theSession = {
secret: process.env.SECRET,
name: "sessId",
resave: false,
saveUninitialized: false,
cookie: {
maxAge: sessionMaxTime,
sameSite: true,
},
store: new SequelizeStore({
db: sequelize,
table: "Session",
}),
};
// Session production options
if (serverMode === "production") {
server.set("trust proxy", 1); // Trust first proxy
theSession.cookie.secure = true; // Serve cookies on HTTPS only
}
server.use(session(theSession));
// Passport Setup
// require("./config/passport")(passport);
require("./config/passport");
server.use(passport.initialize());
server.use(passport.session());
// API Routes
const userRoutes = require("./services/users/usersAPI");
server.use("/api/v1/users", userRoutes);
const profileRoutes = require("./services/profiles/profilesAPI");
server.use("/api/v1/profiles", profileRoutes);
const teamRoutes = require("./services/teams/teamsAPI");
server.use("/api/v1/teams", teamRoutes);
const searchRoutes = require("./services/searches/searchAPI");
server.use("/api/v1/search", searchRoutes);
const ratingRoutes = require("./services/ratings/ratingsAPI");
server.use("/api/v1/ratings", ratingRoutes);
const inviteRoutes = require("./services/invites/invitesAPI");
server.use("/api/v1/invites", inviteRoutes);
const feedbackRoutes = require("./services/feedback/feedbackAPI");
server.use("/api/v1/feedback", feedbackRoutes);
const couponRoutes = require("./services/coupons/couponsAPI");
server.use("/api/v1/coupons", couponRoutes);
const chatRoutes = require("./services/chat/chatAPI");
server.use("/api/v1/chat", chatRoutes);
// Restricted Pages
const restrictedRoutes = require("./services/restricted/restrictedAPI");
server.use(restrictedRoutes);
// Run server
sequelize.sync({ force: true }).then(() => {
server.listen(port, (err) => {
if (err) throw err;
console.log(`> Ready in ${serverMode} mode.`);
});
});
});
To handle HTTP POST requests in Express.js version 4 and above, you need to install the middleware module called body-parser.
body-parser extracts the entire body portion of an incoming request stream and exposes it on req.body.
const express = require('express')
const app = express()
const bodyParser = require('body-parser');
// support parsing of application/json type post data
app.use(bodyParser.json());
//support parsing of application/x-www-form-urlencoded post data
app.use(bodyParser.urlencoded({ extended: true }));
router.post("/team", (req, res) => {...})
In Addition:
To handle multipart/form-data request that support file upload, you
need to use multer multer module.
Basic usage example:
Don't forget the enctype="multipart/form-data" in your form.
<form action="/profile" method="post" enctype="multipart/form-data">
<input type="file" name="avatar" />
</form>
var express = require('express')
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
var app = express()
app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
})
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
// req.files is array of `photos` files
// req.body will contain the text fields, if there were any
})
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body will contain the text fields, if there were any
})
In your case: route.js
const express = require("express");
const app = express();
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
const router = express.Router();
const { catchErrors } = require("../errors/errorHandlers");
const { body, param } = require("express-validator");
const cloudinary = require("cloudinary").v2;
const path = require("path");
const Datauri = require("datauri/parser");
const cloud_name = process.env.CLOUDINARY_NAME;
const cloud_api_key = process.env.CLOUDINARY_API_KEY;
const cloud_api_secret = process.env.CLOUDINARY_API_SECRET;
const cloud_url = process.env.CLOUDINARY_URL;
cloudinary.config({
cloud_name: cloud_name,
api_key: cloud_api_key,
api_secret: cloud_api_secret,
cloudinary_url: cloud_url,
});
// Controller
const {
createTeam,
getUsersTeams,
getManagedTeams,
getTeamCredits,
getTeamData,
updateTeamData,
shutdownTeam,
checkTeamPermissions,
checkTeamPermissionsAndLimits,
addMember,
removeMember,
addMemberBackToTeam,
quitTeam,
} = require("./teamsController");
const {
checkUserVerification,
restrictedRoute,
checkData,
} = require("../helpers/apiHelpers");
router.post(
"/team", upload.single('file'),
(req, res) => {
console.log(res);
// req.file is the `file` file
// req.body will hold the text fields, if there were any
console.log("req body", req.body);
console.log("req file", req.file);
console.log("req user", req.user);
const dUri = new Datauri();
const dataUri = (req) =>
dUri.format(path.extname(req.name).toString(), req.data);
if (req.files !== undefined && req.files !== null) {
const { file, id } = req.files;
const newFile = dataUri(file).content;
cloudinary.uploader
.upload(newFile, {
folder: "TeamAvatar",
})
.then((result) => {
const imageUrl = result.url;
const data = { id: req.body.id, imageUrl };
createTeam(data);
return res
.status(200)
.json({ message: "Success", data: { imageUrl } });
})
.catch((err) =>
res.status(400).json({ message: "Error", data: { err } })
);
} else {
return res.status(400).json({ message: "Error" });
}
},
restrictedRoute,
[
body(
"title",
"Only alphabetical characters, numbers, and spaces are allowed."
)
.not()
.isEmpty()
.isLength({ min: 1, max: 25 })
.trim()
.matches(/^[a-zA-Z0-9 ]+$/)
.blacklist("\\<\\>\\;\\[\\]\\{\\}\\|\\%\\=\\(\\)\\~\\#")
.escape(),
body("mission", "Only alphabetical characters and spaces are allowed.")
.not()
.isEmpty()
.trim()
.blacklist("\\<\\>\\;\\[\\]\\{\\}\\|\\%\\=\\(\\)\\~\\#")
.escape(),
],
checkData,
catchErrors(checkUserVerification),
catchErrors(createTeam)
);

Unable to create or edit new Post (Node & Mongo)

I already solved the problem but now IT is back. I want to save new artist to the website or edit them but now its like the application doesnt connect to the DB but it is! I checked.
const
express = require('express'),
router = express.Router(),
Post = require('../models/post');
// Define which ImageTypes are avaiable to upload
const imageMimeTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'];
// All admin Routes
router.get('/', async (req, res) => {
renderNewPage(res, new Post())
})
// New Route
router.get('/create', (req, res) => {
res.render('admin/createPost/index', {
layout: 'layouts/admin',
// Defined in models/post, gets variables
post: new Post()
});
})
// New Post create
router.post('/', async (req, res) => {
const post = new Post({
// get information from sended new Post (above) and defines variables to use in ejs files
surname: req.body.surname,
name: req.body.name,
bio: req.body.bio,
birthday: req.body.birthday,
technic: req.body.technic,
techPricerangeFrom: req.body.techPricerangeFrom,
techPricerangeTo: req.body.techPricerangeTo
})
// set FilePond saving files to DB
saveProfilpic(post, req.body.profilpic)
try { // await for new post to safe
const newPost = await post.save();
res.redirect('admin');
} catch {
renderNewPage(res, post, true)
}
})
//? Singe Artist Show, Edit, Upate, Delete
// Create single artists page
router.get('/:id', (req, res) => {
res.send('Show Artist' + req.params.id)
})
// Edit artist
router.get('/:id/edit', async (req, res) => {
try {
const findPost = await Post.findById(req.params.id)
res.render('admin/edit', {
layout: 'layouts/admin',
post: findPost
})
} catch {
res.redirect('/admin')
}
})
// Update artist text
router.put('/:id', async (req, res) => {
let post;
try {
post = await Post.findById(req.params.id)
post.surname = req.body.surname,
post.name = req.body.name,
post.bio = req.body.bio,
post.birthday = req.body.birthday,
post.technic = req.body.technic,
post.techPricerangeFrom = req.body.techPricerangeFrom,
post.techPricerangeTo = req.body.techPricerangeTo,
post.profilpic = req.body.profilpic
await post.save();
res.redirect(`/admin`);
} catch {
if (post == null) {
res.redirect('/admin')
}
else {
res.render('admin/edit', {
layout: 'layouts/admin',
post: post,
})
}
}
})
// Define functions
async function renderNewPage(res, post, hasError = false) {
// Implement all Posts (artists) in the DB
try {
const posts = await Post.find({}).collation({ locale: 'en', strength: 2 }).sort({ name: 1 }) // wait and find all post and sort my name
const params = {
layout: 'layouts/admin',
posts: posts, // take all posts from await Post.find({}) and overrides the updates the posts
}
if (hasError) params.errorMessage = 'Ein Fehler ist aufgetreten'
res.render('admin/index', params);
} catch (err) {
res.redirect('/');
}
}
// func for dave files via filepond
function saveProfilpic(post, profilpictureEncoded) {
if (profilpictureEncoded == null) return
const profpic = JSON.parse(profilpictureEncoded);
if (profpic != null && imageMimeTypes.includes(profpic.type)) { // If the file is a json obj & from the type image (jpg, png)
post.profilpic = new Buffer.from(profpic.data, 'base64') // Buffer.from(where, how)
post.profilpicType = profpic.type
}
}
module.exports = router;
all types in the model/post file are Strings.If I want to change something it redirects me to /admin, what means its a error. I got all latest packages (express, mongoose, ...)
I found your answer as I was working on the same tutorial, so I'm still quite new to this, but in my case the problem was related to having a missing '=' in the index page view.
Do you have the '=' character within the <% %> part so that the id is actually included in the link? If not, the link won't include the actual id, since the '=' is needed to not just access a variable but also display it.
<% artists.forEach(Artist=> { %>
<div><%= Artist.last_name %></div>
View
<% }) %>
Hope this helps, and feel free to correct me, as I'm also just starting to learn the basics.

How to validate array of objects using Joi?

I am getting an array of objects to backend, where each object contains a service name.
The structure looks like below
[{"serviceName":"service1"},
{"serviceName":"service2"},..]
when I get the array at backend, I want to validate that every object in the array has serviceName property.
I had written the following code, but even though I pass valid array, I am getting validation error.
var Joi = require('joi');
var service = Joi.object().keys({
serviceName: Joi.string().required()
});
var services = Joi.array().ordered(service);
var test = Joi.validate([{serviceName:'service1'},{serviceName:'service2'}],services)
For the above code, I am always getting the validation error with message
"value" at position 1 fails because array must contain at most 1 items
replacing ordered with items will work.
let Joi = require('joi')
let service = Joi.object().keys({
serviceName: Joi.string().required(),
})
let services = Joi.array().items(service)
let test = Joi.validate(
[{ serviceName: 'service1' }, { serviceName: 'service2' }],
services,
)
For reference click here
A basic/ clearer example is as follows.
To validate a JSON request like this:
{
"data": [
{
"keyword":"test",
"country_code":"de",
"language":"de",
"depth":1
}
]
}
Here is the Joi validation:
seoPostBody: {
body: {
data: Joi.array()
.items({
keyword: Joi.string()
.required(),
country_code: Joi.string()
.required(),
language: Joi.string()
.required(),
depth: Joi.number()
.required(),
}),
},
};
This is what I am doing in NodeJs, might need some slight changes for other platforms
const test = {
body: Joi.array()
.items({
x: Joi.string().required(),
y: Joi.string().required(),
z: Joi.string().required(),
date: Joi.string().required(),
})
};
Just want to make it more clear. I'm currently using "#hapi/joi:16.1.7".
Let's say you want your schema to validate this array of objects.
const example = [
{
"foo": "bar",
"num": 1,
"is_active": true,
}
];
Then schema's rules should be:
var validator = require('#hapi/joi');
const rules = validator.array().items(
validator.object(
foo: validator.string().required(),
num: validator.number().required(),
is_active: validator.boolean().required(),
),
);
const { error } = rules.validate(example);
For Joi you can use below which is working fine for me, this will validate that array must have at-least on object with key serviceName-
const Joi = require('joi');
const itemsArray = Joi.array().items(
Joi.object({
serviceName: Joi.string().required(),
})
).min(1).required();
const itemSchema = Joi.array().items(itemsArray).when('checkout_type', {
is: 'guest',
then: Joi.array().required(),
}).required();
let schema = Joi.object().keys({
items: Joi.alternatives().try(itemsArray, itemSchema).required(),
});
Libraries like these are great but wouldn’t it be even better if we could use them in a more seamless way, like in a Request pipeline? Let’s have a look firstly how we would use Joi in an Express app in Node.js:
const Joi = require('joi');
app.post('/blog', async (req, res, next) => {
const { body } = req; const
blogSchema = Joi.object().keys({
title: Joi.string().required
description: Joi.string().required(),
authorId: Joi.number().required()
});
const result = Joi.validate(body, blogShema);
const { value, error } = result;
const valid = error == null;
if (!valid) {
res.status(422).json({
message: 'Invalid request',
data: body
})
} else {
const createdPost = await api.createPost(data);
res.json({ message: 'Resource created', data: createdPost })
}
});
The above works. But we have to, for each route:
create a schema
call validate()
It’s, for lack of better word, lacking in elegance. We want something slick looking
Building a middleware
Let’s see if we can’t rebuild it a bit to a middleware. Middlewares in Express is simply something we can stick into the request pipeline whenever we need it. In our case we would want to try and verify our request and early on determine whether it is worth proceeding with it or abort it.
So let’s look at a middleware. It’s just a function right:
const handler = (req, res, next) = { // handle our request }
const middleware = (req, res, next) => { // to be defined }
app.post( '/blog', middleware, handler )
It would be neat if we could provide a schema to our middleware so all we had to do in the middleware function was something like this:
(req, res, next) => {
const result = Joi.validate(schema, data)
}
We could create a module with a factory function and module for all our schemas. Let’s have a look at our factory function module first:
const Joi = require('joi');
const middleware = (schema, property) => {
return (req, res, next) => {
const { error } = Joi.validate(req.body, schema);
const valid = error == null;
if (valid) {
next();
} else {
const { details } = error;
const message = details.map(i => i.message).join(',');
console.log("error", message);
res.status(422).json({ error: message }) }
}
}
module.exports = middleware;
Let’s thereafter create a module for all our schemas, like so:
// schemas.js
const Joi = require('joi')
const schemas = {
blogPOST: Joi.object().keys({
title: Joi.string().required
description: Joi.string().required()
})
// define all the other schemas below
};
module.exports = schemas;
Ok then, let’s head back to our application file:
// app.js
const express = require('express')
const cors = require('cors');
const app = express()
const port = 3000
const schemas = require('./schemas');
const middleware = require('./middleware');
var bodyParser = require("body-parser");
app.use(cors());
app.use(bodyParser.json());
app.get('/', (req, res) => res.send('Hello World!'))
app.post('/blog', middleware(schemas.blogPOST) , (req, res) => {
console.log('/update');
res.json(req.body);
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

Having trouble saving to mongoDB using node

I'm attempting to save data from an API response and keep getting an empty object. I placed a console.log in my code and it shows that I'm getting a response from the api. I seem to be missing something after the fetch request.
From index.js:
const express = require('express')
const bodyParser = require('body-parser')
const path = require('path')
const fetch = require('node-fetch')
const exphbs = require('express-handlebars')
const db = require('./src/models/movie')
require('./src/db/mongoose')
const Movie = require('./src/models/movie')
const app = express()
const port = process.env.PORT || 3000
// APP CONFIG
app.use(express.json())
app.use(bodyParser.urlencoded({extended: true}))
// ROUTES - ADD
app.post('/movies/:imdbID', (req, res) => {
const imdb = req.params.imdbID
const url = `**api link and key**${imdb}`
const movie = fetch(url).then((res) => {
return res.json()
}).then((response) => {
console.log(response)
const addMovie = new Movie(response)
db.Movie.create(addMovie, (err, newMovie) => {
if(err){
res.render('movie404')
} else {
res.redirect('/')
}
})
}).catch((e) => {
res.status(500).send(e)
})
})
From mongoose.js:
const mongoose = require('mongoose')
mongoose.connect('mongodb://127.0.0.1:27017/movietime-api', {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false
})
From details.handlebars:
<h2>{{details.Title}}</h2>
<img src="{{details.Poster}}" alt="{{details.Title}}">
<p><em>{{details.Plot}}</em></p>
<p>Starring: {{details.Actors}}</p>
<p>Director: {{details.Director}}</p>
<form action="/movies/{{details.imdbID}}" method="POST">
<button type="submit">Add</button>
</form>
From movie.js:
const mongoose = require("mongoose");
// MONGOOSE/MODEL CONFIG
const Movie = mongoose.model('Movie',{
imdbID: String,
Title: String,
Poster: String,
Director: String,
Year: String,
Plot: String,
Ratings: String,
Rated: String,
Genre: String,
Writer: String,
Actors: String
});
module.exports = Movie;
I would expect a redirect to the index page then a new database entry using the above model.
you are almost there just few thing needs to be taken care of:
app.post('/movies/:imdbID', (req, res) => {
const imdb = req.params.imdbID
const url = `**api link and key**${imdb}`
const movie = fetch(url).then((res) => {
return res.json()
}).then((response) => {
console.log(response)
const addMovie = new Movie(response)
addMovie.save((err, newMovie) => {
if(err){
res.render('movie404',newMovie) // to render the details
} else {
res.redirect('/')
}
})
}).catch((e) => {
res.status(500).send(e)
ref

Resources