Create a new field inside a JSON - javascript

I'm using the combo Express (Node.js) and Mongoose to make a REST API. I'm trying to make the login using a JWT token but I've got a problem. When I execute the following code
const mongoose = require('mongoose');
const User = mongoose.model('User');
// other code
_api.post('/login', function (req, res) {
const data = req.body;
// some data control
User.findOne({ username: data.username}, function(err, doc) {
if (hash(password) == doc.password) { // password check
myToken = generateToken(); // generating the token
doc.jwtToken = myToken; // including the generated token to the response
res.status(200).json(doc); // return the final JSON to client
}
}
}
the final JSON returned by the API doesn't have the field "jwtToken":"mygeneratedtoken" and this is strange. I included other times new fields inside a JSON with the same syntax and it worked. I tried to use a tmp variable to which I assigned the doc content (that is a javascript object) and then I added the jwtToken filed and return the tmp variable. But nothing.
Can someone explain me if there is something wrong with my code or if there is something that I need to know?

Documents returned by mongoose are immutable, and thus assignment to doc.jwtToken does not modify the object. You can either use the lean method to modify the query, or toObject to convert the document to a regular javascript object. Try:
var docObject = doc.toObject();
docObject.jwtToken = myToken;
res.status(200).json(docObject);

Related

Parsing Body from request to Class in express

I got doubt, im coming from .net with c# and i want to parse my body request as .net does for my automatically, how can i set a class or an interface as a request body in express, i have found many options but all of them just destruct the body into the properties that they need, i need a way or a method that allows me to get only the properties that i specified in my class.
In .Net it will be something like this.
[HttpGet("someName")
Public IActionResult GetSomething(Myclass instance){
// the instance with only the properties that i specified in my class and not the hole body with useless props that i don’t request
instance.someProperty
Return Ok()
}
ASP.net is actually smart enough to understand that when a class is declared as an argument, it must map from the request POST body to the class.
Nodejs and express do not always come with batteries included.
You need to add a middleware that can read the raw request and get the json object you want. If you are only receiving JSON then you need the JSON middleware. If you expect to have URL encoded posts (for file uploading or html s) then you also need to add the urlencoded middleware
const app: Application = express();
(...)
app.use(express.json());
app.use(express.urlencoded());
At this point, you can declare your route, and express will corectly fill the req.body object with your post data.
interface MyPostBody {
foo: string;
bar: string;
}
app.post("/api/someName", (req, res) => {
const instance = req.body as MyPostBody;
console.log(instance.foo);
console.log(instance.bar);
const result = doSomething(instance);
res.send(result);
});
Please be aware that we are just casting the type here, so if your client sends an object that does not conform to the MyPostBody interface, things will break. You probably need to add some validation to ensure the data conforms to you api contract. You can use some validation library like yup for that. To keep it simple I will do something very basic here.
app.post("/api/someName", (req, res) => {
if(req.body.foo === null || req.body.foo === undefined) {
res.status(400).send("foo is required");
return;
}
if(req.body.bar === null || req.body.bar === undefined) {
res.status(400).send("bar is required");
return;
}
const instance = req.body as MyPostBody;
console.log(instance.foo);
console.log(instance.bar);
const result = doSomething(instance);
res.send(result);
});

delete operator not functioning as expected in node.js

In my node.js express application I am retrieving a user from my database
const newUser = await User.create({
username,
password,
email,
avatar,
})
but before sending the response with the user object I want to remove the password.
delete newUser.password;
return res.status(200).json({ token, user: newUser });
but in my response the password is returned.
console.log(JSON.stringify(newUser))
returns:
{"_id":"11111111","username":"dylan","email":"dylan#email.com","admin":true,"password":"******"}
query return value is document not javascript object
Documents have a toObject method which converts the mongoose document into a plain JavaScript object.
first convert it to object and then use delete on each property you want
also with mongoose it can done more properly and automatically
User.methods.toJSON = function () {
const user = this;
const userObj = user.toObject();
delete userObj.password;
return userObj;
};
every time you send this document as response it convert to JSON and every time that JSON.strigify() is call on a document it call this toJSON() method
Maybe you can also do
delete newUser._doc.password

Nodejs controller is being messy

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.

Running query from inside Cloud Function using request parameters

I am having troubles running queries from Cloud Functions using the request parameters to build the query form HTTP calls. In the past, I have ran queries from cloud functions fine with no error. My problem arises when I try to run the query using parameters gotten from the request.
When I hardcode the location of the document in the function, it works fine but when I try to build a query, it returns status code of 200. I have also logged the the built query and it is logging out the right thing but no data is being returned. It only returns data when the document path is hardcoded. See code below.
Query looks like this
https://us-central1-<project-id>.cloudfunctions.net/getData/CollectionName/DocumentName
export const getData = functions.https.onRequest((request, response) => {
const params = request.url.split("/");
console.log("the params 0 "+params[0]);
console.log("the params 1 "+params[1]);
console.log("the params 2 "+params[2]);
//Build up the document path
const theQuery = "\'"+params[1]+"\/"+params[2]+"\'";
console.log("the query "+theQuery); <-- logs out right result in the form 'Collection/Document'
//Fetch the document
const promise = admin.firestore().doc("\'"+params[1]+"\/"+params[2]+"\'").get() <---- This doesnt work, building the query
//const promise = admin.firestore().doc('collectionName/DocID').get() <---- This hard coded and it works
promise.then(snapshot => {
const data = snapshot.data()
response.send(data)
}).catch(error => {
console.log(error)
response.status(500).send(error);
})
});
I tried using a different approach and giving the datafields a names as seen below
Query looks like this
https://us-central1-<project-id>.cloudfunctions.net/getData?CollectionName=CName&DocumentID=Dname
export const getData = functions.https.onRequest((request, response) => {
const collectName = request.query.CollectionName;
const DocId = request.query.DocumentName;
//Build up the document path
const theQuery = "'"+collectName+"\/"+collectName+"'";
console.log("the query "+theQuery); <---Logs out correct result
//Fetch the document
const promise = admin.firestore().doc(theQuery).get() <-- Building the query does not work
//const promise = admin.firestore().doc('collectionName/DocID').get() <---- This hard coded and it works
promise.then(snapshot => {
const data = snapshot.data()
response.send(data)
}).catch(error => {
console.log(error)
response.status(500).send(error);
})
});
In both cases, when the request is build from the URL, it does not return any data and it does not return any errors. And I am sure the documents I am trying to fetch exsist in the database. Am I missing anything ?
Try request.path. Then you can obtain the path components, e.g. request.path.split("/")[1]
The syntax for request.query is valid when using Express. This is referenced in some of the docs, but not made explicit that Express is required. It's confusing.
To properly handle the dynamic inputs, you may have more luck working with Express and creating routes and handlers. This Firebase page has links to some projects using it.
Walkthough set-up using Express on Firebase.

MongoDB Error: Cannot create property '_id' on string

I'm using Node.js and Express on Heroku, with the MongoDB addon.
My database connection works fine and I can successfully push some data in, but not other.
Here is the database connection:
mongodb.MongoClient.connect(mongoURI, function (err, database) {
if (err) {
console.log(err);
process.exit(1);
}
// Save database object from the callback for reuse.
db = database;
console.log("Database connection ready");
// Initialize the app.
var server = app.listen(process.env.PORT || dbport, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
});
I can successfully push my Twitter API response into the database like this:
db.collection(TWEETS_COLLECTION).insert(data);
('data' is just a JSON variable)
But when I try to push another JSON variable into the database in the same method, I get an error. Code:
var jsonHash = '{"hashtag":"","popularity":1}';
var objHash = JSON.parse(jsonHash);
objHash.hashtag = req.body.hashtag;
JSON.stringify(objHash);
collection(HASHTAG_COLLECTION).insert(jsonHash);
And the error:
TypeError: Cannot create property '_id' on string '{"hashtag":"myhash","popularity":1}'
at Collection.insertMany...
...
Any ideas what I'm doing wrong?
I don't know where you are getting the jsonHash variable from but I think you are doing unecessary JSON-handling here. You are also inserting the wrong variable, you want to insert objHash which is a valid object to insert, now you are inserting jsonHash which is just a string. JSON.stringify(objHash); is not doing anything as you are not saving the JSON returned from the function. I think you want something like this?
var objHash = {
hashtag: "",
popularity:1
};
objHash.hashtag = req.body.hashtag;
collection(HASHTAG_COLLECTION).insert(objHash);
jsonHash is still a string. May be you want to save objHash instead without JSON.stringify ?

Categories

Resources