I am new to Javascript and have been following along with a course on NodeJs and am stuck on an exercise.
I am creating a basic CRUD API for 'genres' in a made up video store.
I have created all of the above but when I call any of the endpoint with a specific id I get an error.
TypeError: Cannot read property 'id' of undefined
at genres.find.g (/Users/richardcurteis/Desktop/NodeJsCourse/vidly-node/index.js:60:65)
at Array.find (native)
at findGenre (/Users/richardcurteis/Desktop/NodeJsCourse/vidly-node/index.js:60:26)
The issue is (I think) related to the findGenre method which checks to see if a genre with the matching id exists.
function findGenre(req) {
const genre = genres.find(g => g.id === parseInt(req.params.id));
if(genre) return genre;
false;
}
The json object is currently hardcoded.
This is the request (sent from Postman):
GET > http://localhost:5000/api/genres/1
This returns:
[
{
"id": 1,
"name": "Action"
},
{
"id": 2,
"name": "Horror"
},
{
"id": 3,
"name": "Drama"
}
]
I have checked against the solution and it appears to be the same implementation as mine, save for my refactoring repetitive checks and error codes.
Maybe I'm just going code blind, but maybe someone can see what I'm missing?
I have included the entire class (?) but like I said the error is related to the checking of the json objects id.
const Joi = require('joi');
const express = require('express');
const app = express();
app.use(express.json());
const genres = [
{ id: 1, name: 'Action' },
{ id: 2, name: 'Horror' },
{ id: 3, name: 'Drama' },
];
app.get('/api/genres', (req, res) => {
res.send(genres);
});
app.post('/api/genres', (req, res) => {
const { error } = validateGenreName(req.body);
if(error) return sendValidationError(res, error);
const genre = {
id: genres.length + 1,
name: req.body.name
};
genres.push(genre);
res.send(genre);
});
app.put('/api/genres/:id', (res, req) => {
const genre = findGenre(req);
if(!genre) return genreNotFoundError(res);
const { error } = validateGenreName(req.body);
if(error) return sendValidationError(res, error);
genre.name = req.body.name;
res.send(genre);
});
app.delete('/api/genres/:id', (res, req) => {
const genre = findGenre(req);
if(!genre) genreNotFoundError(res);
const index = genres.indexOf(genre);
genres.splice(index, 1);
res.send(genre);
});
app.get('/api/genres/:id', (res, req) => {
const genre = findGenre(req);
if(!genre) return genreNotFoundError(res);
res.send(genre);
});
// Functions
function findGenre(req) {
const genre = genres.find(g => g.id === parseInt(req.params.id));
if(genre) return genre;
false;
}
function genreNotFoundError(res) {
res.status(404).send('Genre not found');
}
function validateGenreName(genre) {
const schema = {
name: Joi.string().min(3).required()
};
return Joi.validate(genre, schema);
}
function sendValidationError(res, error) {
res.status(400).send(error.details[0].message);
}
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));
Now I see your problem. You have flipped (req, res) for the .get handler.
app.get('/api/genres/:id', (res, req) => {
const genre = findGenre(req);
if(!genre) return genreNotFoundError(res);
res.send(genre);
});
Should be
app.get('/api/genres/:id', (req, res) => {
const genre = findGenre(req);
if(!genre) return genreNotFoundError(res);
res.send(genre);
});
The same goes for .delete and .put.
Related
i have a table:
CREATE TABLE IF NOT EXISTS bands (
id serial PRIMARY KEY,
name VARCHAR UNIQUE NOT NULL,
creationDate DATE not NULL,
years DATE not NULL
);
I only want to pass name and creation date. what i want is that years will return currentdate - creationDate. The problem is that I do not really know where i should correctly change my code, because im using Node project.
My code looks like this:
const express = require("express");
const app = express();
const pool = require("./db");
app.use(express.json());
// Routes
app.post("/bands", async (req, res) => {
try {
const { name, creationDate } = req.body;
const newBand = await pool.query(
"INSERT INTO bands (name, creationDate,years) VALUES ($1, $2) RETURNING *",
[name, creationDate]
);
res.json(newBand);
} catch (err) {
console.error(err.message);
}
});
app.get("/bands", async (req, res) => {
try {
const allBands = await pool.query("SELECT * FROM bands");
res.json(allBands);
console.log(allBands);
} catch (err) {
console.error(err.message);
}
});
app.get("/bands/:bandsName", async (req, res) => {
console.log(req.params);
const { bandsName } = req.params;
try {
const todo = await pool.query("SELECT * FROM bands WHERE name = $1", [
bandsName,
]);
res.json(todo.rows[0]);
} catch (err) {
console.error(err.message);
}
});
app.put("/bands/:id", async (req, res) => {
try {
const { id } = req.params;
const { name, creationDate } = req.body;
const updateTodo = await pool.query(
"UPDATE band SET name = $1, creationDate = $2 WHERE id = $3",
[name, creationDate, id]
);
res.json("Udało się, zaaktualizowane");
} catch (err) {
console.error(err.message);
}
});
app.delete("/bands/:id", async (req, res) => {
try {
const { id } = req.params;
const deleteTodo = await pool.query("DELETE FROM bands WHERE id = $1", [
id,
]);
res.json("Usunięto");
} catch (err) {
console.error(err.message);
}
});
app.listen(3000, () => {
console.log("server is listening on port 3000");
});
Can anyone tell me where should i change my code so "years" will automatically calculate without me having to put the data in postman?
const { name, creationDate } = req.body;
const d = new Date(creationDate );
const year = d.getFullYear();
Create a date object and retrieve the year would do it
I'm writing an endpoint to delete a record from a dummy database. The dummy database right now is written as an array of objects (it will then be changed to a class, then an actual database).
I have a dbHelpers.js file:
module.exports = {
createId(data) {
// ...
},
findById(data, recordId) {
// ...
},
deleteById(data, recordId) {
data.splice(data.findIndex(item => item.id === recordId), 1)
return data;
}
};
And i'm calling it in controllers/envelope.js
exports.deleteEnvelope = async (req, res) => {
try {
const { id } = req.params;
const envelopes = await dbEnvelopes;
const envelope = findById(envelopes, id);
if (!envelope) {
return res.status(404).send({
message: 'Envelope Not Found',
});
}
const updatedEnvelopes = deleteById(envelopes, id);
return res.status(200).send(updatedEnvelopes);
} catch (err) {
res.status(500).send(err)
}
};
However, for some reason, my findIndex function is not working and it's always returning -1. What is wrong with the function?
Below is the dummy db in config/db.js:
const envelopes = [
{
"id": 1,
"title": "Rent",
"budget": 1000
},
{
id: 2,
"title": "Groceries",
"budget": 300
},
{
id: 3,
"title": "Entertainment",
"budget": 400
}
]
module.exports = envelopes;
I think you can use array.filter to remove the object in array by matching the id.
deleteById(data, recordId) {
return data.filter(item => item.id !== recordId)
}
When i call Create Option api it is working fine but when i call list api get error: Cannot read property 'count' of undefined Express (Node + MongoDB) API.here is my Option Controller File code.
i have Log DB.ProductDoption ,getting result but count function not working.
const _ = require('lodash');
const Joi = require('joi');
exports.create = async (req, res, next) => {
try {
const validateSchema = Joi.object().keys({
name: Joi.string().required(),
key: Joi.string().required(),
description: Joi.string().allow(['', null]).optional(),
options: Joi.array().items(Joi.object().keys({
key: Joi.string().required(),
displayText: Joi.string().required()
})).required()
});
const validate = Joi.validate(req.body, validateSchema);
if (validate.error) {
return next(PopulateResponse.validationError(validate.error));
}
const key = Helper.String.createAlias(req.body.key);
console.log(DB.ProductDoption);
const count = await DB.ProductDoption.count({ key });
if (count || validate.value.key === '_custom') {
return next(PopulateResponse.error({
message: 'Please add unique name for key'
}));
}
const option = new DB.ProductDoption(validate.value);
await option.save();
res.locals.option = option;
return next();
} catch (e) {
return next(e);
}
};
exports.list = async (req, res, next) => {
const page = Math.max(0, req.query.page - 1) || 0; // using a zero-based page index for use with skip()
const take = parseInt(req.query.take, 10) || 10;
try {
const query = Helper.App.populateDbQuery(req.query, {
text: ['name', 'key', 'description']
});
const sort = Helper.App.populateDBSort(req.query);
const count = await DB.ProductDoption.count(query);
const items = await DB.ProductDoption.find(query)
.collation({ locale: 'en' })
.sort(sort).skip(page * take)
.limit(take)
.exec();
res.locals.optionList = {
count,
items
};
next();
} catch (e) {
next(e);
}
};
collection.count is deprecated, and will be removed in a future version. Use Collection.countDocuments or Collection.estimatedDocumentCount instead
Currently I have axios and cheerio return data from a webpage. I then setup express to setup a few views. I double checked my index.hbs and it include {{data}} inside the body. This should allow the page to render the text from the index render data: dealss . Am I missing anything ? The dealss obj holds 4 different objects that I can access.
getdeals(result => console.log(result.totaldeals[0].date))
This returns [ 09/04/2019/ ] in the console.
const path = require('path')
const express = require('express')
const hbs = require('hbs')
const axios = require('axios');
const cheerio = require('cheerio');
const app = express()
// Define paths for express config
const publicDirPath = path.join(__dirname, '../public')
const viewsPath = path.join(__dirname, '../templates/views')
const partialsPath = path.join(__dirname, '../templates/partials')
// Setup handlebars engine and views location
app.set('view engine', 'hbs')
app.set('views', viewsPath)
hbs.registerPartials(partialsPath)
// Setup static directory to serve
app.use(express.static(publicDirPath))
// Views
app.get('', (req, res) => {
res.render('index', {
title: 'ClearVision',
data: dealss,
name: 'Chris'
})
})
app.get('/about', (req, res) => {
res.render('about', {
title: 'ClearVision - About Us',
header: 'About Us',
name: 'Chris'
})
})
app.get('/help', (req, res) => {
res.render('help', {
title: 'ClearVision - Help',
helptext: 'Please contact x for help.',
name: 'Chris'
})
})
app.get('/weather', (req, res) => {
res.send({
forecast: 'It is sunny.',
location: 'x, Ca'
})
})
app.listen(1337, () => {
console.log('Server is currently running on port 1337.')
})
const url = 'https://abcdef.com/';
axios.defaults.withCredentials = true
// Get the deals
const getdeals = (callback) => {
axios(url, {
headers: {
Cookie: "x=xx;"
}
})
.then(response => {
const html = response.data;
const $ = cheerio.load(html);
// Deals Page
const statsTable = $('tbody > tr');
const totaldeals = [];
// Loop Table for data in each row
statsTable.each(function () {
const nwline = "\n"
let date = $(this).find('td:nth-child(1)').text()
let bodydeals = $(this).find('td:nth-child(2)').text()
let newdeal = $(this).find('td:nth-child(3)').text()
let revdeal = $(this).find('td:nth-child(4)').text()
let monthlydealrev = $(this).find('td:nth-child(5)').text()
// Clear /n
if (date.includes(nwline)) {
date = date.toString().replace("\n", ""),
date = date.toString().replace("\n", "")
}
// Clear /n
if (bodydeals.includes(nwline)) {
bodydeals = bodydeals.toString().replace("\n", ""),
bodydeals = bodydeals.toString().replace("\n", ""),
bodydeals = bodydeals.toString().replace("\n", "")
}
// Clear /n
if (newdeal.includes(nwline)) {
newdeal = newdeal.toString().replace("\n", ""),
newdeal = newdeal.toString().replace("\n", ""),
newdeal = newdeal.toString().replace("\n", "")
}
// Clear /n
if (revdeal.includes(nwline)) {
revdeal = revdeal.toString().replace("\n", ""),
revdeal = revdeal.toString().replace("\n", ""),
revdeal = revdeal.toString().replace("\n", "")
}
// Clear /n (lookup jquery table functions)
if (monthlydealrev.includes(nwline)) {
monthlydealrev = monthlydealrev.toString().replace("\n", ""),
monthlydealrev = monthlydealrev.toString().replace("\n", ""),
monthlydealrev = monthlydealrev.toString().replace("\n", "")
}
totaldeals.push({
date,
bodydeals,
newdeal,
revdeal,
monthlydealrev
})
})
callback({
totaldeals
})
//console.log(totaldeals[1].date)
})
.catch(console.error);
}
function newFunction() {[getdeals(result => console.log(result.totaldeals))]}
I added a data: dealss under the res.render for the index. I also checked the index.hbs which has {{data}} in there. Shouldn't this just add the text to the screen?
Any ideas on how to print it to the view?
You just need to pass it as a variable to your hbs file:
app.get('', (req, res) => {
getdeals(result => {
res.render('index', {
title: 'ClearVision',
data: result, // or result.totaldeals depending
name: 'Chris' // on what you really mean
})
});
})
Improvements
If you rewrite getdeals() to return a Promise instead of accepting a callback you can use async/await:
const getdeals = () => {
// NOTE THIS CHANGE, return axios promise:
return axios(url, {
/* ... */
})
.then(response => {
/* .. */
statsTable.each(function () {
/* .. */
})
return totaldeals; // NOTE we return the result instead
// of calling a callback. This will
// return a resolved Promise
})
// Don't catch here, your request will hang if an error occurs
}
Now with the change above (return axios and return the result) we can rewrite the route as:
app.get('', async (req, res, next) => { // must have async keyword!
try {
let result = await getdeals();
res.render('index', {
title: 'ClearVision',
data: result, // or result.totaldeals depending
name: 'Chris' // on what you really mean
})
}
catch (err) {
console.log(err);
next(err); // this will close the request socket
}
})
How I want my data to be structured is as follows:
Student -> Reg_num -> someindex (that will start from 1 - like an
unique key) -> course details.
However, the code I wrote gives me an incorrect structure. Can someone help me sort it out.
var db = admin.database();
var ref = db.ref("Students");
var newMessageRef = ref.push();
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
var uniqueID = 97888888888888;
if (req.method !== 'POST') {
return res.status(500).json({
message: 'Not allowed'
})
} else {
return newMessageRef.set({
[uniqueID]: {
course: req.body.course,
credits: req.body.credit,
lecturer : 'Prof. Lee'
}
}).then(() => {
res.status(200).json({
message: "okkkkasss"
});
...
Note: The -LC-lS2HPMbZW9AdT19K is a code that was automatically added from the code. This is because I used ref.push()
Do not use ref.push() or ref.set(), but ref.update() as follows:
const db = admin.database();
const ref = db.ref("Students");
//var newMessageRef = ref.push(); <- Don't do that
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const uniqueID = 97888888888888; <- Student ID
const uniqueCourseKey = 0; <- uniqueCourseKey
if (req.method !== 'POST') {
return res.status(500).json({
message: 'Not allowed'
})
} else {
return ref.child(uniqueID).update({
[uniqueCourseKey]: {
course: req.body.course,
credits: req.body.credit,
lecturer : 'Prof. Lee'
}
}).then(() => {
res.status(200).json({
message: "okkkkasss"
});
....
Then you can call again the Cloud Function with e.g. const uniqueCourseKey = 1; and the new node will be correctly added under the StudentID node.
The doc for the update method is here: https://firebase.google.com/docs/reference/js/firebase.database.Reference#update