Nodejs how convert the rows of postgresql query result to json array? - json

I get below query result from postgreSQL data base, then I want to send the response which should only has part: rows to the client. I need the response is in json format, so could you please tell how can I convert the query result: rows into json array?
Result {
command: 'SELECT',
rowCount: 1,
oid: NaN,
rows:
[ anonymous {
uid: 23,
name: 'Test Human',
email: 'test#example.com',
password: 'test',
created: 2018-08-24T09:44:19.659Z,
terminited: null }
anonymous {
uid: 12,
name: 'Test Animal',
email: 'ani#example.com',
password: 'hello',
created: 2018-08-24T09:44:19.659Z,
terminited: null }
],
fields:
...
_parsers:
[ [Function: parseInteger],
[Function: noParse],
[Function: noParse],
[Function: noParse],
[Function: parseDate],
[Function: parseDate] ],
RowCtor: [Function: anonymous],
rowAsArray: false,
_getTypeParser: [Function: bound ] }
Node.js code:
// router for get friends information
router.get("/getfriends", (req, res) => {
const uid = req.query.uid;
pool.connect((err, client, done) => {
if (err) throw err;
const fetchRow = async() =>{
await client.query('SELECT * FROM friends where follower = $1 ORDER by id ASC', [uid], (err, result) => {
done()
if(err){
console.log(err.stack)
res.status(400).json(err)
} else {
/*here I want to response rows only with JSON format, but result.rows doesn't realise that...*/
console.log(result.rows)
res.status(200).json(result.rows)
}
})
}
fetchRow()
})
})

Related

Sequelize findAll not returning expected output on sequelize-mock

I'm trying to do unit testing on my nodejs-express method with sequelize-mock.
Controller
const getDetailsByUserId = async (id) => {
try {
const userId = id ?? 0;
const details = await Model.findAll(
{
raw: true,
where: { user_id: userId }
}
);
if (details && details .length > 0) {
return {
status: 200,
success: true,
message: 'details found.',
data: details
}
}
return {
status: 404,
success: false,
message: 'details not found',
data: []
}
} catch (error) {
return {
status: 500,
success: false,
message: error.message || "An error occurred while getting details.",
data: null
}
}
}
Test
jest.mock('../models/details', () => () => {
const SequelizeMock = require("sequelize-mock");
const dbMock = new SequelizeMock();
return dbMock.define('users', [
{
id: 1,
user_id: 123
name: 'John Doe 1'
},
{
id: 2,
user_id: 456
name: 'John Doe 2'
},
{
id: 3,
user_id: 789
name: 'John Doe 3'
}
]);
});
test('should return 404 and an empty array', async () => {
const userId = 147;
const details = await controller.getDetailsByUserId(userId);
expect(details.status).toEqual(404);
});
I always get the status of 200 instead of 404 here. I checked the returned data and it's returning the records of the defined mocked model.
Actual Result:
[
fakeModelInstance {
options: {
timestamps: true,
paranoid: undefined,
createdAt: undefined,
updatedAt: undefined,
deletedAt: undefined,
isNewRecord: true
},
_values: {
'0': [Object],
'1': [Object],
'2': [Object],
user_id: 147,
id: 1,
createdAt: 2021-09-18T00:55:25.976Z,
updatedAt: 2021-09-18T00:55:25.976Z
},
dataValues: {
'0': [Object],
'1': [Object],
'2': [Object],
user_id: 147,
id: 1,
createdAt: 2021-09-18T00:55:25.976Z,
updatedAt: 2021-09-18T00:55:25.976Z
},
hasPrimaryKeys: true,
__validationErrors: []
}
]
QUESTIONS:
Is there something I can do to get the expected result (empty array) for this scenario?
the raw: true seems to be not working when it is mocked. Is there a way could log the result on raw object?
NOTE: This only happens on the unit testing. When accessing the endpoint on postman it returns the expected result.
According to the docs, findAll() will always return an array of a single result based on the where query in the options. This is why you will never get an empty array.
See more: https://sequelize-mock.readthedocs.io/en/stable/api/model/#findalloptions-promisearrayinstance

nodejs - passport.use callback return dataValues and _previousDataValues instead of a normal object

I am using passport.js module for Authentication and Sequelize mysql for database management.
error occurs when authenticating user:
{
"errors": {
"message": "WHERE parameter \"id\" has invalid \"undefined\" value",
"error": {}
}
}
When authenticating the user, console.log(jwtPayload) in file passport.js shows below results.
{
dataValues: {
id: '06c19eb0-995f-45f4-81d7-26ec3b401234',
email: 'CCCC#gmail.com',
createdAt: '2021-05-14T01:51:31.000Z',
updatedAt: '2021-05-14T01:51:31.000Z'
},
_previousDataValues: {
id: '06c19eb0-995f-45f4-81d7-26ec3b401234',
email: 'CCCC#gmail.com',
createdAt: '2021-05-14T01:51:31.000Z',
updatedAt: '2021-05-14T01:51:31.000Z'
},
_changed: {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes: [ 'id', 'email', 'createdAt', 'updatedAt' ]
},
isNewRecord: false,
iat: 1620961695
}
instead of
{
id: '06c19eb0-995f-45f4-81d7-26ec3b401234',
email: 'CCCC#gmail.com',
createdAt: '2021-05-14T01:51:31.000Z',
updatedAt: '2021-05-14T01:51:31.000Z'
}
passport.js
var passport = require('passport');
const passportJWT = require("passport-jwt");
const JWTStrategy = passportJWT.Strategy;
var LocalStrategy = require('passport-local').Strategy;
const ExtractJWT = passportJWT.ExtractJwt;
const bcrypt = require('bcryptjs');
var User = require('../models/Users')
module.exports = function(passport) {
passport.use(new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey : 'your_jwt_secret'
},
function (jwtPayload, done) {
console.log(jwtPayload)
return User.findOne({where: {id:jwtPayload.id}})
.then(user => {
return done(null, user);
})
.catch(err => {
return done(err);
});
}
));
};
Sequelize returns dataValues and _previousDataValues properties by design. You can pass the current values with a small modification.
return User.findOne({where: {id:jwtPayload.id}})
.then(user => {
return done(null, user.dataValues);
})

Sequilize query is returning only one row while using include

Context : I am having this problem were I am doing a query using sequilize an it only return's me an array with one position even though I have more than one field that correspond to the query.
This are my two involved models
This is my group.js model
module.exports = (sequelize, DataTypes) => {
const Group = sequelize.define('Group', {
name: DataTypes.STRING,
limit: DataTypes.STRING,
user_id: DataTypes.INTEGER
});
Group.associate = models => {
Group.belongsTo(models.User, { foreignKey: 'user_id' });
};
Group.associate = models => {
Group.hasMany(models.Movement, { foreignKey: 'group_id' });
};
return Group;
}
This is my movement.js model
module.exports = (sequelize, DataTypes) => {
const Mov = sequelize.define('Movement', {
description: DataTypes.STRING,
value: DataTypes.INTEGER,
group_id: DataTypes.INTEGER
});
Mov.associate = models => {
Mov.hasOne(models.Group, { foreignKey: 'group_id' });
};
return Mov;
}
This is my query (where you will see that I am doing an INNER JOIN to SUM the fields of the Movement table)
router.get('/', verify, async (req, res) => {
try {
const group = await Group.findAll({
attributes: [
'id',
'name',
'limit',
[sequelize.fn('SUM', sequelize.col('Movements.value')), 'total_spent'],
],
include: [{
attributes: [], // this is empty because I want to hide the Movement object in this query (if I want to show the object just remove this)
model: Movement,
required: true
}],
where: {
user_id: req.userId
}
});
if (group.length === 0) return res.status(400).json({ error: "This user has no groups" })
res.status(200).json({ groups: group }) //TODO see why this is onyl return one row
} catch (error) {
console.log(error)
res.status(400).json({ Error: "Error while fetching the groups" });
}
});
Problem is that it only return's one position of the expected array :
{
"groups": [
{
"id": 9,
"name": "rgrgrg",
"limit": 3454354,
"total_spent": "2533"
}
]
}
It should return 2 positions
{
"groups": [
{
"id": 9,
"name": "rgrgrg",
"limit": 3454354,
"total_spent": "2533"
},
{
"id": 9,
"name": "rgrgrg",
"limit": 3454354,
"total_spent": "2533"
}
]
}
This is the query sequilize is giving me:
SELECT `Group`.`id`, `Group`.`name`, `Group`.`limit`, SUM(`Movements`.`value`) AS `total_spent` FROM `Groups` AS `Group` INNER JOIN `Movements` AS `Movements` ON `Group`.`id` = `Movements`.`group_id` WHERE `Group`.`user_id` = 1;
I guess you need to add an appropriate group by clause as follows -
const group = await Group.findAll({
attributes: [
'id',
'name',
'limit',
[sequelize.fn('SUM', sequelize.col('Movements.value')), 'total_spent'],
],
include: [{
attributes: [], // this is empty because I want to hide the Movement object in this query (if I want to show the object just remove this)
model: Movement,
required: true
}],
where: {
user_id: req.userId
},
group: '`Movements`.`group_id`'
});
Many-to-many "through" table with multiple rows of identical foreign key pairs only returns one result?
I just ran into this bug and added this options to the main query:
{
raw: true,
plain: false,
nest: true
}
Then you just merge the query.
It's a workaround, but might help someone.

Spring + React fetch data

i have a problem with fetch data from api exposed by spring.
I want to fetch data by:
componentDidMount() {
this.setState({isLoading: true});
fetch('http://localhost:8080/api/tasks/')
.then(response => { return response.json() })
.then(results => this.setState({
tasks: results,
isLoading: false
}));
The spring api which return data:
#CrossOrigin(origins = "http://localhost:3000")
#GetMapping
public List<Task> findTasksByUserId() {
return taskService.findTasksBelongToUser(idProvider.getCurrentUserId());
}
Returned JSON:
[
{
id: 1,
version: 0,
name: "Task1",
description: "description1",
priority: 1,
finished: true,
category: {
id: 1,
version: 0,
name: "Uncategorized",
user: {
id: 1,
version: 0,
login: "admin",
email: "admin#todo.pl",
enabled: true
}
},
user: {
id: 1,
version: 0,
login: "admin",
email: "admin#todo.pl",
enabled: true
},
position: 0
},
{
id: 2,
version: 0,
name: "Task2",
description: "description2",
priority: 4,
finished: true,
category: {
id: 1,
version: 0,
name: "Uncategorized",
user: {
id: 1,
version: 0,
login: "admin",
email: "admin#todo.pl",
enabled: true
}
},
user: {
id: 1,
version: 0,
login: "admin",
email: "admin#todo.pl",
enabled: true
},
position: 1
}]
And i got a error like this:
Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
I dont know what is wrong but maybe format of json is not correct, because it's start with the sign '['.
I am a beginner in React so please about some hint and help.
regards
fetch() returns a promise which gets resolved into an HTTP response of course, not the actual JSON. To extract the JSON body content from the response, we use the json() method.
Try adding two headers Content-Type and Accept to be equal to application/json.
fetch('http://localhost:8080/api/tasks/', {
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then(response => { return response.json() })
.then(results => this.setState({
tasks: results,
isLoading: false
}));

GraphQL error update mutation "Resolve function for \"User.id\" returned undefined"

I am a newbie to GraphQL and trying to write an update mutation. However, I am receiving Resolve function for \"User.id\" returned undefined" error although the database is actually got updated.
What am I doing wrong?
userSchema.js:
import Sequelize from 'sequelize';
import SqlHelper from '../helpers/sqlhelper';
const config = require('../../config');
const sequelizer = new SqlHelper(config).Init();
const createUser = sequelizer.define(
'createUser',
{
...
}
);
const updateUser = sequelizer.define(
'updateUser',
{
id: {
type: Sequelize.UUID,
field: 'Id',
primaryKey: true,
defaultValue: Sequelize.UUIDV4,
},
username: {
type: Sequelize.STRING,
field: 'Username',
allowNull: true,
},
email: {
type: Sequelize.STRING,
field: 'Email',
allowNull: true,
},
firstname: {
type: Sequelize.STRING,
field: 'FirstName',
allowNull: true,
},
lastname: {
type: Sequelize.STRING,
field: 'LastName',
allowNull: true,
},
....
},
{
// define the table's name
tableName: 'Users',
},
);
module.exports = User;
UserResolver.js:
import User from '../dbschemas/user';
import Sequelize from 'sequelize';
const Op = Sequelize.Op;
export default {
Mutation: {
createUser: async (obj, args) =>
(await User.create(args)),
updateUser: async (obj, args) =>
(await User.update(args,
{
where: {
id: args.id,
},
returning: true
}))
}
};
Although calling updateUser from GraphiQL updates the records (in db), it results in a "Resolve function for \"User.id\" returned undefined" error:
mutation{
updateUser(id: "2ecd38ca-cf12-4e79-ac93-e922f24af9e3",
username: "newUserTesting",
email: "testemail#yahoo.com",
lastname: "TestUserLName",
firstname: "fname1") {
id
}
}
{
"data": null,
"errors": [
{
"message": "Resolve function for \"User.id\" returned undefined",
"locations": [
{
"line": 16,
"column": 4
}
],
"path": [
"updateUser",
"id"
]
}
]
}
The issue is clear, your resolver does not return an object containing id.
The docs say that Model.update returns an array in which the 2nd element is the affected row.
Hence, your resolver should look like:
async updateUser(obj, args) {
const resultArray = await User.update( ... )
return resultArray[1]
}
... To be replaced by whatever you need.
So apparently, update does NOT return affected rows for MSSQL, only the number of records affected.
This is true only for postgres when returning: true:
public static update(values: Object, options: Object): Promise<Array<affectedCount, affectedRows>>
Setting returning: true (for MSSQL) returns undefined (and order of params in the array is not even in the right order... i.e. first affectedRows -> undefined, then affectedCount ->num of affected rows.)
Tho get an object back you would need to do something like this:
Mutation: {
createUser: async (obj, args) =>
(await User.create(args.user)),
updateUser: async (obj, args, context, info) =>{
let user = args.user;
let response = await User.update(user,
{
where: {
[Op.or]: [{ email: user.email }, { id: user.id }, { username: user.username }, { lastname: user.lastname}]
},
//returning: true //not working... only for postgres db :(
}).then(ret => {
console.log('ret', ret);
return ret[0];
}).catch(error => {
console.log('error', error)
});
if (response > 0) return user; //return record
//return response > 0; //return true
}
}