I am preparing a project for an interview where I need to build an app to display videos to logged in users. I've started working on the back-end (in Node.js / Express / TypeScript / Mongoose / MongoDB). Everything worked until I tested posting data in Thunder Client; I got this error message:
TypeError: Cannot destructure property 'title' of 'req.body' as it is undefined.
at createVideo (/home/wilder/Bureau/video-streaming-server/src/controllers/Video.ts:8:5)
at Layer.handle [as handle_request] (/home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/layer.js:95:5)
at next (/home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/route.js:144:13)
at Route.dispatch (/home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/route.js:114:3)
at Layer.handle [as handle_request] (/home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/layer.js:95:5)
at /home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/index.js:284:15
at Function.process_params (/home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/index.js:346:12)
at next (/home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/index.js:280:10)
at Function.handle (/home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/index.js:175:3)
at router (/home/wilder/Bureau/video-streaming-server/node_modules/express/lib/router/index.js:47:12)
I know very similar questions have been asked (and answered here) but I cannot make it work still. As I gathered from said questions and answers, the content of req.body is, by default, undefined, hence I tried using
app.use(express.json());
but it doesn't change anything.
Here is my code so far:
src/Server.ts
import express from "express";
import http from "http";
import mongoose from "mongoose";
import { config } from "./config/config";
import videoRoutes from "./routes/Video";
const router = express();
// Connect to MongoDB
mongoose
.connect(config.mongo.url, { retryWrites: true, w: "majority" })
.then(() => {
console.log("Connected to MongoDB.");
startServer();
})
.catch((err) => {
console.log(err);
});
// Start server (only if MongoDB connects)
const startServer = () => {
// Routes
router.use("/videos", videoRoutes);
// Error handling
router.use((req, res) => {
const err = new Error("not found");
console.error(err);
return res.status(404).json({ message: err.message });
});
// Server running
http
.createServer(router)
.listen(config.server.port, () =>
console.log(`Server is running on port ${config.server.port}.`)
);
};
src/models/Video.ts
import mongoose, { Document, Schema } from "mongoose";
export interface IVideo {
title: string;
description: string;
video_path: string;
tags: string[];
duration: string;
views: string;
creator: string;
created_at: string;
like: number;
dislike: number;
comments: [
{
user: string;
text: string;
date: string;
}
];
}
const VideoSchema: Schema = new Schema(
{
title: { type: String, required: true },
description: { type: String, required: true },
video_path: { type: String, required: true },
tags: { type: Array, required: true },
duration: { type: String, required: true },
views: { type: String, required: true },
creator: { type: String, required: true },
created_at: { type: String, required: true },
like: { type: Number, required: true },
dislike: { type: Number, required: true },
comments: [
{
user: { type: String, required: true },
text: { type: String, required: true },
date: { type: String, required: true },
},
],
},
{ versionKey: false }
);
export default mongoose.model<IVideo>("Video", VideoSchema);
src/controllers/Video.ts
import { NextFunction, Request, Response } from "express";
import mongoose, { Mongoose } from "mongoose";
import Video from "../models/Video";
// Create video
const createVideo = (req: Request, res: Response, next: NextFunction) => {
const {
title,
description,
video_path,
tags,
duration,
views,
creator,
created_at,
like,
dislike,
comments,
user,
text,
date,
} = req.body;
const video = new Video({
_id: new mongoose.Types.ObjectId(),
title,
description,
video_path,
tags,
duration,
views,
creator,
created_at,
like,
dislike,
comments,
user,
text,
date,
});
return video
.save()
.then((video) => res.status(201).json({ video }))
.catch((err) => res.status(500).json({ err }));
};
// Get a single video
const readVideo = (req: Request, res: Response, next: NextFunction) => {
const videoId = req.params.videoId;
return Video.findById(videoId)
.then((video) =>
video
? res.status(200).json({ video })
: res.status(404).json({ message: "Not found." })
)
.catch((err) => res.status(500).json({ err }));
};
// Get all videos
const readAll = (req: Request, res: Response, next: NextFunction) => {
return Video.find()
.then((videos) => res.status(200).json({ videos }))
.catch((err) => res.status(500).json({ err }));
};
// Update video
const updateVideo = (req: Request, res: Response, next: NextFunction) => {
const videoId = req.params.videoId;
return Video.findById(videoId)
.then((video) => {
if (video) {
video.set(req.body);
return video
.save()
.then((video) => res.status(201).json({ video }))
.catch((err) => res.status(500).json({ err }));
} else {
res.status(404).json({ message: "Not found." });
}
})
.catch((err) => res.status(500).json({ err }));
};
// Delete video
const deleteVideo = (req: Request, res: Response, next: NextFunction) => {
const videoId = req.params.videoId;
return Video.findByIdAndDelete(videoId)
.then((video) =>
video
? res.status(201).json({ message: "Deleted." })
: res.status(404).json({ message: "Not found." })
)
.catch((err) => res.status(500).json({ err }));
};
export default {
createVideo,
readVideo,
readAll,
updateVideo,
deleteVideo,
};
src/routes/Video.ts
import express from "express";
import controller from "../controllers/Video";
const app = express();
const router = express.Router();
app.use(express.json());
router.post("/create", controller.createVideo);
router.get("/get/:videoId", controller.readVideo);
router.get("/get/", controller.readAll);
router.patch("/update/:videoId", controller.updateVideo);
router.delete("/delete/:videoId", controller.deleteVideo);
export = router;
And here is the format of data I need to add in my database
{
"title": "Rotating planet",
"description": "Vidéo de la planète en train de tourner",
"video": "/video_example.mp4",
"tags": ["planet", "earth", "science", "space"],
"duration": "00:31",
"views": 19876,
"creator": "ThePlanetGuy",
"created_at": "2021-05-01T08:00:00.00Z",
"like": 10,
"dislike": 2,
"comments": [{
"user": "Anonymous",
"text": "Beautiful",
"date": "2021-05-10T08:00:00.00Z"
}, {
"user": "Flatter",
"text": "Fake ! The earth is flat !",
"date": "2021-05-05T10:00:00.00Z"
}]
}
If anyone could help me understand what I did wrong, I would gladly appreciate it. I am still very new to programming, especially back-end.
Thank you :)
CodePudding user response:
In src/routes/Video.ts
, you're creating a new Express app for adding the JSON parser middleware to, but you don't do anything with that app.
Instead, either add the middleware to the router instance:
router.use(express.json());
Or add it to the main Express app that is created in src/Server.ts