I've made some API (NodeJS, db: PostgreSQL) for my Android app and now I'm facing problem with PUT. Ofc, API isn't anything special and it's made with express template, without ORM. I'm not backend dev, but I had to do that. But what is my problem... I've tried multiple times to make route, that allows me to pass 3 arguments. First one is ID (protege_patron), second is firstname (from input) and last one is lastname (from input).
router.put('/:firstname/:lastname', (req, res, next) => {
const protege_firstname = req.params.firstname
const protege_lastname = req.params.lastname
const { protege_patron } = req.body
pool.query(
'UPDATE proteges SET protege_patron = $1 WHERE (protege_firstname = $2 AND protege_lastname = $3)',
[protege_patron, protege_firstname, protege_lastname],
(error, results) => {
if (error) {
throw error
}
res.status(200).send(`User modified with ID: ${id}`)
}
)
})
I guess my problem is router.put('/:firstname/:lastname', but I just can't find any solution or anything that would help me. Most of examples use ORM or other stuff. Is there any solution, link to some hidden knowledge, anything? I know my code is... bad, still.
Related
I need a function that takes the request body and conditionally creates the validators that will be used on the request. I figured the best way to do this is by creating middleware but I'm running into a problem with express-validator. For express-validator to work the middleware has to return an array of my validators but if I do this I can't call next() and the request doesn't get handled.
// This will fail because I can't call next
const validatorMiddleware = (req, res, next) => {
const {
isFooRequired,
isBarRequired,
} = req.body;
return concat(
isFooRequired && createValidator('foo'),
isBarRequired && createValidator('bar'),
)
}
With the way I'm going about this I basically need a function that calls next() AND returns the array of validators.
I understand express-validator has a way to conditionally add validation rules but there doesn't seem to be an easy way to do this in bulk. I'd have to do this individually with each validation chain which is a huge pain and kind of gross if you ask me. I have several optional fields which are dependent on a lot of validation and I want to do something like this:
const addressValidators = (service) => [
body(`${service}.address.lineOne`).notEmpty().withMessage('Address cant be empty'),
body(`${service}.address.city`).notEmpty().withMessage('city cant be empty'),
body(`${service}.address.state`).isIn(allowedStates).withMessage('invalid state'),
body(`${service}.address.zipCode`).isPostalCode('US').withMessage('invalid zip code'),
];
In case anyone has this same problem I do, here's the solution I found. With express-validator you can run validations imperatively so I created the middleware you see below. Inside my constructValidators function I use the request body to see which fields are present and create my validation chain accordingly.
const validateMiddleware = async (req, res, next) => {
const validationList = constructValidators(req.body);
await Promise.all(validationList.map((validation) => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
res.status(400).json({ errors: errors.array() });
return null;
};
Remember, 3 hours of troubleshooting could save you 10 minutes of reading documentation!
I'm trying to make a simple login page.I have to do it for university project.The problem is pretty simple, i'm trying to use redis to be the database for the users and passwords. The problem is that i can't extract the values out of the response that i get from redis.I got a docker running the redis image and every thing connect.In the example below im trying to make a simple boolean change from true to false according to the data inside the key (im using "a" as the key) but no matter what i do the value doesn't seem to change, it is note worthy that im very new to this and to js in particular, i read in the redis api that all of the funtions are asyncs i tried changing the code but it didnt help.
app.get('/enter', (req, res) => {
var username =req.query.user;
var password = req.query.pass;
ans = false
redis.get(username,function(err, reply) {
if(reply != null ) ans = true;
})
console.log(ans);
})
I just trying to verify that the key has a value, i tried to make a variable before and after the request but it isn't adjusting thanks for your time
I see you dont understand the very very basics of callbacks and asynchronouse behaviour of javascript.
Well you can code like this:
app.get('/enter', async (req, res) => {
var username =req.query.user;
var password = req.query.pass;
ans = false
let reply = await getUsername(username)
console.log(reply)
console.log(ans);
})
function getUsername(username) {
return new Promise((res, rej) => {
redis.get(username, function(err, reply) {
if(err) rej(err)
res(reply)
})
})
}
You can just promisfy your redis code with new Promise and then you can use async / await style on it
Otherwise you need to write your code in the callback witch leads to an callback hell if you have more code
I am making a backend api with node/express and am running into an error. I've tried many different approaches as well as looked at many different questions on here, but none seem to fix my problem. As far as I can tell my routes are structured properly, but they simply don't work. Perhaps I'm missing something super obvious, but I'm at a loss.
Bellow is a simplified version of my index.js where I start my node server:
const movies = require("./routes/movies");
app.use("/api/movies/", movies);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}...`));
And then in my movies.js I have several endpoints:
// DOES WORK - returns movies with the specifed object
router.get("/genre/:id", async (req, res) => {
const movies = await Movie.find({ genres: req.params.id });
if (!movies)
return res
.status(400)
.send(`No movies with genre of id ${req.params.id} could be found.`);
res.send(movies);
});
// DOESN'T WORK - returns main html page
router.get("/tmdbid/:id", async (req, res) => {
const movie = await Movie.findOne({
tmdbID: req.params.id,
});
if (!movie)
return res
.status(400)
.send(`No movies with tmdbID of ${req.params.id} could be found.`);
if (res.status(404)) res.send("Route not found");
res.send(movie);
});
// DOESN'T WORK - returns Cannot GET /api/movies/title/{title}
router.get("/title/:title", async (req, res) => {
const movie = await Movie.find({
title: { $regex: new RegExp(req.params.title, "i")
},});
if (!movie)
return res
.status(400)
.send(`The movie with name of ${req.params.title} could not be found.`);
res.send(movie);
});
I'm not sure what needs to be done here. All of these share similar syntax, but each result in a different response. None of them log any errors to the terminal either. I know for sure general refactoring should be done, but as far as the problem at hand goes, I can't wrap my head around why the last two aren't working. Lastly, I have several other endpoints in the this file, all of which are working fine. Just these two have issues.
Add a console.log statement to check whether the endpoint is hit or not. The code logic in your code sample above sounds right. However, I would try formatting the code batter.
if (!movie){
return res.status(400).send(`No movies with genre of id ${req.params.id} could be found.`);
}
Also, make sure you do not forget to restart your node js server.
What are those return statements doing there? and you could improve your if statements as well like such ->
try {
const movie = await Movie.findOne({
tmdbID: req.params.id,
});
if (!movie) {
res
.status(400)
.send(`No movie(s) could be found.`);
} else {
res
.status(200)
.send(movie);
}
} catch(err) {
res.send(500);
}
I eventually found my answer and it was simply a mistake I had in the url when calling the endpoint, not in the endpoint's code itself. Rather than calling to localhost:5000, I was calling to localhost:3000, which is probably why it was returning the index.html of my react app.
So, my code works as is. Perhaps not the best structure ever, but as long as it works, I'm happy.
Still not sure why the /title/:title endpoint wasn't working and then suddenly was. That remains a mystery.
I'm new to javascript, node.js (or backend at all). I am trying to create a controller for the login page requests and I am confused about getting data from the MYSQL table and User Authentication and working with JWT package !
In my Controller, I first check if the user input is available in the user table (with a simple stored procedure), then I compare the database password and the user input, after this I want to create a token and with limited time. (I have watched some tutorial videos about JWT and there is no problem with it), my main problem is to figure out how to write a proper controller with this functions?
I have 2 other questions:
1.Is it the right and secure way to get data from MySQL table inside the route? Or should I create a JS class for my controller? (I'm a bit confused and doubtful here)
2.Assuming that comparePassword() returns true, how can I continue coding outside of the db.query callback function scope? Because I have to execute comparePasssword() inside db.query callback
loginController.js :
const { validationResult } = require('express-validator');
const bcrypt = require('bcrypt');
const db = require('../../sqlConnection')
let comparePassword = (dbPass, inputPass) => {
bcrypt.compare(inputPass, dbPass, function(err, result) {
console.log(result)
});
}
// for get request
exports.getController = (req, res) => {
res.send('login')
}
// for post request
exports.postController = (req, res) => {
let errors = validationResult(req)
if(!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() })
}
// find data from MYSQL table
let sql = `CALL findUser(?)`
db.query(sql, [req.body.username], (err, res) => {
if(err) console.log(err)
//console.log(Object.values(JSON.parse(JSON.stringify(res[0]))))
var data = JSON.stringify(res[0])
data = JSON.parse(data).find(x => x)
data ? comparePassword(data.password, req.body.password) : res.status(400).send('cannot find
user')
})
res.send('post login')
}
login.js :
const express = require('express')
const router = express.Router()
const { check } = require('express-validator');
const loginCont = require('../api/controllers/loginController')
router.route('/')
.get(
loginCont.getController
)
.post(
[
check('username').isLength({min: 3}).notEmpty(),
check('password').isLength({min: 4}).notEmpty()
],
loginCont.postController
)
module.exports = router
In my point of view, looks like there is no easy answer for your question so I will try to give you some directions so you can figure out which are the gaps in your code.
First question: MySQL and business logic on controller
In a design pattern like MVC or ADR (please take a look in the links for the flow details) The Controllers(MVC) Or Actions(ADR) are the entry point for the call, and a good practice is to use these entry points to basically:
Instantiate a service/class/domain-class that supports the request;
Call the necessary method/function to resolve what you want;
Send out the response;
This sample project can help you on how to structure your project following a design pattern: https://riptutorial.com/node-js/example/30554/a-simple-nodejs-application-with-mvc-and-api
Second question: db and continue the process
For authentication, I strongly suggest you to take a look on the OAuth or OAuth2 authentication flow. The OAuth(2) has a process where you generate a token and with that token you can always check in your Controllers, making the service a lot easier.
Also consider that you may need to create some external resources/services to solve if the token is right and valid, but it would facilitate your job.
This sample project should give you an example about how to scope your functions in files: https://github.com/cbroberg/node-mvc-api
Summary
You may have to think in splitting your functions into scoped domains so you can work with them in separate instead of having all the logic inside the controllers, then you will get closer to classes/services like: authenticantion, user, product, etc, that could be used and reused amount your controllers.
I hope that this answer could guide you closer to your achievements.
Hi fellow developers!
I've got this error showing up in my console when I try to save two identical documents in a collection in MongoDB that has nothing to do with the index shown in the error.
Here's the error: E11000 duplicate key error collection: Bohemian.orders index: user.email_1 dup key: { user.email: null }
Now this makes no sense, because I'm trying to save an Order document in a separate collection, which has nothing to do with the user router I had set up previously.
Here is the schema and model code:
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema({
amountToPay: Number,
});
const Order = mongoose.model("Order", orderSchema);
module.exports.Order = Order;
As shown here, I am only trying to save the amount to be payed into the database in a separate collection.
Here is the router file:
const express = require('express');
const router = express.Router();
const { Order } = require('../models/Order');
router.get("/", async (req, res, next) => {
try {
const orders = await Order.find();
if (orders.length === 0) return res.status(404).send("There are currently no orders");
res.send(orders);
} catch (ex) {
console.error(ex);
next();
}
});
router.post("/", async (req, res, next) => {
try {
const order = new Order({
amountToPay: req.body.amountToPay
});
await order.save();
res.send(order);
} catch (ex) {
console.error(ex);
next();
}
});
module.exports = router;
As you can see there is nothing relative to the error that I'm getting and I have no clue why I'm getting a duplicate user.email = null key , when I haven't made any reference to the User model or router.
Here is the POST call I'm making from POSTMAN to test:
Pretty straight forward, nothing extreme, nothing tangled, right? Well the first ever POST call saves the document in the Database, but from then on I keep getting the same error. The only thing I can take from that is that when I save the first document, Mongo looks for the user.email property when I'm creating the new instance of Order and when it doesnt find it, it creates it with a value of null and then the next document would naturally be a duplicate, hence the error. But I'm confused, because this model and router should not absolutely nothing to do with the user ones.
Here is the error:
So please if anyone can help me understand why MongoDB is screwing with me or where I'm making a mistake, I would really appreciate it.
I found out what the problems was, thanks to Molda:
At some point I had created these indexes, which I'm still unsure when and how, but I did. Which essentially lead to this error, when I tried to save the second document.
A simple quick console log of the indexes in the collection showed that I had that index in there.
const indexes = await Order.collection.getIndexes();
console.log(indexes);
Then I removed them using this method:
await Order.collection.dropIndexes("user.email_1");
And everything worked flawlessly from there.
I hope this helps anyone in this situation in the future and thanks Molda! :)