I've created multiple REST API projects using the Laravel framework and basing my code structure on the Laracasts tutorial. However we are deciding to move some projects using NodeJs as a backend. I'm beginning to learn node and I'm trying to replicate it in Node. I was able to do it for a singe object response but for multiple objects I can't seem to make it work.
Here is my controller:
index(req,res) {
User
.findAll()
.then(function(users){
res.json(api.respond(transfomer.transformCollection(users)));
})
.catch(function(error){
res.json(api.respondWithError('users not found',error));
});
}
api controller:
module.exports = {
// response w/o error
respond: function(data,msg,status) {
if (msg == null) {
return {
'status': status || true,
'data': data
};
} else {
return {
'status': true,
'message': msg,
'data': data
};
}
},
// response with error
respondWithError: function(msg,error) {
var self = this;
var status = false;
var data = {
'error': error
};
return this.respond(data,msg,status);
},
};
transformer.js
module.exports = {
// single transformation
transform (user) {
return {
'id' : user.id,
'username': user.username,
'firstname': user.firstname,
'lastname': user.lastname,
'address': user.address,
'phone': user.phone,
'mobile': user.mobile,
'status': user.status
};
},
//
transformCollection(users) {
var self = this;
var data = [];
for (var i = 0; i <= users.length; i++) {
data.push(this.transform(users[i]));
}
return data;
}
};
sample output
{
"status": true,
"data": [
{
"id": 1,
"username": "b#email.com",
"firstname": "Jon",
"lastname": "Doe",
"address": "Homes",
"phone": "+966501212121",
"mobile": "+966501212121",
"status": "NOT VERIFIED"
},
{
"id": 1,
"username": "b#email.com",
"firstname": "Jon",
"lastname": "Doe",
"address": "Homes",
"phone": "+966501212121",
"mobile": "+966501212121",
"status": "NOT VERIFIED"
},
{
"id": 1,
"username": "b#email.com",
"firstname": "Jon",
"lastname": "Doe",
"address": "Homes",
"phone": "+966501212121",
"mobile": "+966501212121",
"status": "NOT VERIFIED"
},
{
"id": 1,
"username": "b#email.com",
"firstname": "Jon",
"lastname": "Doe",
"address": "Homes",
"phone": "+966501212121",
"mobile": "+966501212121",
"status": "NOT VERIFIED"
},
]
}
Sorry for asking this as I'm a bit newb with node. Is it possible to achieve that output as I tried different ways but Im still getting errors. Btw I'm using sequelize for the database.
Thanks.
You can use this:
const options = {
raw: true,
attributes: ['id', 'name', 'code', 'createdAt','updatedAt']
};
country.findAndCountAll(options).then(querySnapshot => {
const total = querySnapshot.count;
resolve({
docs: querySnapshot.rows,
total: total
})
}).catch((err) => {
reject(err)
});
I've found the answer to my question since sequelize is returning the results as an object with additional properties aside from the database results I had to modify the controller to set and convert the results to raw in order for me to get the array of objects from the query results from the database.
index(req,res) {
User
.findAll({ raw: true }) // added "raw: true"
.then(function(users){
res.json(api.respond(transfomer.transformCollection(users)));
})
.catch(function(error){
res.json(api.respondWithError('users not found',error));
});
},
This will return the array of objects from the database and from there the data transformer is working properly. Thank you for all the help.
Related
Problem
I’m trying to return only the objects that don’t contain any of the names on the filteredEmployers list, against the employer attribute, from an API I'm retrieving the data from.
What I've tried
I have an alternative piece of code that seems to work fine when I don’t connect to the API (i.e. reading from hardcoded data), but when I connect to the API, even though I get the following response (when immediately logged after retrieval), the code then doesn’t execute…
{
"Pagination": {
"NumberOfPeople": 185,
"PageSize": 200,
"PageNumber": 1,
"NumberOfPages": 1
},
"People": [
{
"name": "TJ",
"job": "Software Engineer",
"organization": {
"company": {
"employer": "amazon",
"department": "IT"
}
},
"location": {
"city": "Boston",
"state": "Massachusetts"
}
},
{
"name": "Dominique",
"job": "CEO",
"organization": {
"company": {
"employer": "IBM",
"department": "IT"
}
},
"city": "Seattle",
"state": "Washington"
},
{
"name": "Enrique",
"job": "Engineer",
"organization": {
"company": {
"employer": "Bellkrieg Megasystems",
"department": "Construction"
}
},
"location": {
"address": {
"state": "New York",
"city": "New York City",
"zip": "11323"
}
}
},
{
"name": "Bob",
"job": "Project Manager",
"organization": {
"company": {
"employer": "Megasystems",
"department": "R&D"
}
},
"address": {
"location": {
"quadrant": {
"block": 1,
"state": "Texas",
"city": "Austin"
}
}
}
}
]
}
The code I’m trying to implement is here:
// constants and variables are defined here, including API credentials and the filteredEmployers array
//FYI const filteredEmployers = ['Megasystems', 'Bellkrieg'];
//the code then fetches the API data is here
.then((response) => {
return response.json();
})
.then((json) => {
//console.log(typeof json);
//console.log(json);
const people = Array.from(json).flatMap(o => o.People);
return people.filter(person => {
const employer = person?.organization?.company?.employer;
if (typeof employer !== 'string') return true;
const employerIsNotFiltered = filteredEmployers.every(
str => !employer.includes(str)
);
console.log("This is the outputted data: " + employerIsNotFiltered);
return employerIsNotFiltered;
});
})
The desired response is:
[
{
name: 'TJ',
job: 'Software Engineer',
organization: { company: [Object] },
location: { city: 'Boston', state: 'Massachusetts' }
},
{
name: 'Dominique',
job: 'CEO',
organization: { company: [Object] },
city: 'Seattle',
state: 'Washington'
}
]
Any recommendations on how to get this to execute, or alternatives to this method appreciated.
Thanks in advance
Reiterating my comment on your question: You just need to change the line
const people = Array.from(json).flatMap(o => o.People);
to
const people = json.People;
The JSON response that you included in the question is an object, and Response.json() returns a promise which resolves to an already parsed representation of the JSON text response, so in order to access the array at the People property, you only need json.People. Here is a runnable snippet based on the code and data that you showed:
// The JSON data, copied and pasted from the first code block of your question:
const json = `{"Pagination":{"NumberOfPeople":185,"PageSize":200,"PageNumber":1,"NumberOfPages":1},"People":[{"name":"TJ","job":"Software Engineer","organization":{"company":{"employer":"amazon","department":"IT"}},"location":{"city":"Boston","state":"Massachusetts"}},{"name":"Dominique","job":"CEO","organization":{"company":{"employer":"IBM","department":"IT"}},"city":"Seattle","state":"Washington"},{"name":"Enrique","job":"Engineer","organization":{"company":{"employer":"Bellkrieg Megasystems","department":"Construction"}},"location":{"address":{"state":"New York","city":"New York City","zip":"11323"}}},{"name":"Bob","job":"Project Manager","organization":{"company":{"employer":"Megasystems","department":"R&D"}},"address":{"location":{"quadrant":{"block":1,"state":"Texas","city":"Austin"}}}}]}`;
function mockFetch () {
return Promise.resolve({
json: () => Promise.resolve(JSON.parse(json)),
});
}
const filteredEmployers = ['Megasystems', 'Bellkrieg'];
mockFetch()
.then(response => response.json())
.then(json => {
// Change this line:
// const people = Array.from(json).flatMap(o => o.People);
// To:
const people = json.People;
return people.filter(person => {
const employer = person?.organization?.company?.employer;
if (typeof employer !== 'string') return true;
const employerIsNotFiltered = filteredEmployers.every(
str => !employer.includes(str)
);
return employerIsNotFiltered;
});
})
.then(console.log);
I'm having hard time on thinking how will I populate hooks with API response(json)
see below codes
cosnt [loginResponse, setloginResponse] = useState({
Token: '',
UserID: '', //from user-id
UserName: '', //from user-userName
firstName: '', //from user-firstName
rcCode: '' //from attributes-rcCode
})
const login = async () => {
await axios.get('/API')
.then(response => {
console.log('response.data', response.data.resp)
});
}
here's the result of console.log(response.data.resp)
{
"data": {
"token": "abcd",
"user": {
"id": "123456",
"userName": "uname",
"firstName": "FNAME",
"lastName": "MNAME",
"email": "email#email.com",
"attributes": {
"favorites": ["FAV"],
"rcCode": ["123"]
},
"requiredActions": [],
"roles": ["ROLE"]
},
"modulePermissions": []
}
}
for console.log(response.data):
"resp": {
"data": {
"token": "abcd",
"user": {
"id": "123456",
"userName": "uname",
"firstName": "FNAME",
"lastName": "MNAME",
"email": "email#email.com",
"attributes": {
"favorites": ["FAV"],
"rcCode": ["123"]
},
"requiredActions": [],
"roles": ["ROLE"]
},
"modulePermissions": []
}
},
"success": true
I want to populate my hooks with those datas for me to utilize it on my page.
I got undefined if I tried to console.log(response.data.resp.data)
On console.log(response), I got:
Thank you.
Don't use async/await and .then() together. Use either of those.
const login = async () => {
const response = await axios.get('/API');
const parsedData = JSON.parse(response.data.resp);
const userData = parsedData.data;
setLoginResponse({
Token: userData.token,
UserID: userData.user.id,
UserName: userData.user.userName,
firstName: userData.user.firstName,
rcCode: userData.user.attributes.rcCode
});
}
In the .then
setLoginResponse({...loginResponse, Token: response.data.resp.data.token, UserId: response.data.resp.data.user.id, ...}
You can destructure your response object first and store values in variables to make the setLoginResponse more easy to read.
https://reactjs.org/docs/hooks-state.html
I've a function that set one JSON to my Cloud Firestore database:
app.post('/api/add/collection/:collection_id/permission/:permission_id', (req, res) => {
(async () => {
try {
await db.collection(req.params.collection_id)
.doc(req.params.permission_id)
.set({
[req.body.id]: {
name: req.body.name,
email: req.body.email,
phone_number: req.body.phone_number
}
}, { merge: true}
);
return res.status(200).send('OK');
} catch (error) {
console.log(error);
return res.status(500).send('ERROR' + error);
}
})();
});
To invoke this function, I pass JSON body to POST request:
https://.../app/api/add/collection/USER_ID/permission/PERMISSION_id
{
"id": 45,
"name": "Stack",
"email": "stack#overflow.com",
"phone_number": "+48 111 222 333"
}
Everything work fine, this request set this JSON as a map with id as a key, and values as a name, email and phone_number. But I want to pass several JSON's on one request to add several map objects to my NoSQL document, like:
[
{
"id": 45,
"name": "Stack",
"email": "stack#overflow.com",
"phone_number": "+48 111 222 333"
},
{
"id": 46,
"name": "Stack2",
"email": "stack2#overflow.com",
"phone_number": "+48 222 222 333"
},
{
"id": 47,
"name": "Stack3",
"email": "stack3#overflow.com",
"phone_number": "+48 333 222 333"
}
]
How to modify my firebase function to achieve this?
Since you have an array of objects, you'll need to loop over that array, and then create a document for each object. And since you need to wait until all documents are created, you can use Promise.all().
Something like this:
try {
await Promise.all(req.body.map((object) => {
return db.collection(req.params.collection_id)
.doc(req.params.permission_id)
.set({
[object.id]: {
name: object.name,
email: object.email,
phone_number: object.phone_number
}
}, { merge: true});
});
return res.status(200).send('OK');
} catch (error) {
console.log(error);
return res.status(500).send('ERROR' + error);
}
If i have an array like:
[
{ "user": "tom",
"email": "ee#co.com"
},
{ "user": "james",
"email": "bb#co.com"
},
{ "user": "ryan",
"email": "rr#co.com"
}
]
But it's not always being returned in that order - how can i check if ryan, for example, exists somewhere in one of these objects?
If you are already using lodash (or want to) then I would use its find:
_.find(array, { user: "ryan" })
Or with a few more characters you can do it without lodash:
array.find(elem => elem.user === "ryan")
Both return undefined if a matching element is not found.
Function return true if array contains ryan.
var input = [
{ "user": "tom",
"email": "ee#co.com"
},
{ "user": "james",
"email": "bb#co.com"
},
{ "user": "ryan",
"email": "rr#co.com"
}
]
var output = input.some(function(eachRow){
return eachRow.user === 'ryan';
});
console.log(output);
My method to solve that is to extract the targeted fields into arrays then check the value.
const chai = require('chai');
const expect = chai.expect;
describe('unit test', function() {
it('runs test', function() {
const users = [
{ "user": "tom",
"email": "ee#co.com"
},
{ "user": "james",
"email": "bb#co.com"
},
{ "user": "ryan",
"email": "ryan#co.com"
}
];
const names = extractField(users, 'user'); // ['tom', 'james', 'ryan']
expect(names).to.include('ryan');
});
function extractField(users, fieldName) {
return users.map(user => user[fieldName]);
}
});
I'm using chai for assertion. In case you want to check in other fields, we just use the extract methods.
const emails = extractField(users, 'email');
expect(emails).to.include('ryan');
Hope it helps
I'm new use nodejs and mongodb, now I build restful API use nodejs and mongodb. I want use response standard for my API with http://jsonapi.org standard.
I need a suggestion from advance how best way to do it, making my API response like the following JSON data:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"links": {
"self": "http://example.com/users"
},
"data": [{
"type": "users",
"id": 5b647bb8998248235a0aab3c,
"attributes": {
"username": "luke",
"email": "luke#mail.com",
"password": "password",
"hashpassword":"",
"oldpassword":"$2a$10$eyt6YV6m2JJrebNxvS0iEuxMCubDXratNJ6/XK797IGvepXBdp9Yq",
"salt":"2q6eN9U0vWFBsIF1MtB5WrgPiB8pldTS",
"usertype":"bisnis",
"userstatus":"not aktif"
}
}, {
"type": "users",
"id": 5b647bdf998248235a0aab3d,
"attributes": {
"username": "ken",
"email": "ken#mail.com",
"password": "password",
"hashpassword":"",
"oldpassword":"$2a$10$eyt6YV6m2JJrebNxvS0iEuxMCubDXratNJ6/XK797IGvepXBdp9Yq",
"salt":"2q6eN9U0vWFBsIF1MtB5WrgPiB8pldTS",
"usertype":"bisnis",
"userstatus":"not aktif"
}
}]
}
I have problem with nest iteration when create output as above. This my code:
const UsersModel = mongoose.model('Users');
//...show list user
exports.listUsers = (req, res) => {
UsersModel.find({}, (err, result) => {
if (err) {
res.send({code: 400, failed: 'Error 400'});
}
res.json(result);
});
};
And this is my result JSON:
[
{
"type": "users",
"userstatus": "not aktif",
"_id": "5b647bb8998248235a0aab3c",
"username": "luke",
"email": "luke#mail.com",
"password": "password",
"usertype": "bisnis",
"hashpassword": "$2a$10$eyt6YV6m2JJrebNxvS0iEuxMCubDXratNJ6/XK797IGvepXBdp9Yq",
"salt": "2q6eN9U0vWFBsIF1MtB5WrgPiB8pldTS",
"__v": 0
},
{
"type": "users",
"userstatus": "tidak aktif",
"_id": "5b647bdf998248235a0aab3d",
"username": "ken",
"email": "ken#mail.com",
"password": "password",
"usertype": "personal",
"hashpassword": "$2a$10$hok988mszyIBiXVNjmfifOiPNzXkBRRRynXJS/0qCkvlaBOQs65MO",
"salt": "IiMvtVYVqTpZFXmYQIM4IlS6PJFVZ3kw",
"__v": 0
}
]
And this is my temporary code for my problem.
//...show list user
exports.listUsers = (req, res) => {
UsersModel.find({}, (err, result) => {
if (err) {
res.send({code: 400, failed: 'Error 400'});
}
let listData = [];
for (let key in result) {
let data = {};
let attr = {};
if (result.hasOwnProperty(key)) {
data.type = result[key].type;
data.id = result[key]._id;
for(let i in result[key]) {
if(result[key].hasOwnProperty(i)) {
attr.username = result[key].username;
attr.email = result[key].email;
attr.password = result[key].password;
attr.hashpassword = result[key].hashpassword;
attr.oldpassword = result[key].oldpassword;
attr.salt = result[key].salt;
attr.usertype = result[key].usertype;
attr.userstatus = result[key].userstatus;
}
}
data.attribute = attr;
listData.push(data);
}
}
let collections = {
"meta": {
"copyright": "Copyright 2018 Kotakku Studio and Lab",
"authors": [
"sw. saputra"
]
},
"link": {
"self": req.protocol + '://' + req.get('host') + req.originalUrl
},
"data": listData
}
res.json(collections);
});
};
Please give me suggestion the elegant and the best way to solve my problem if my temporary code is not correct.
Thanks advance.
You need to create a proper mongoose schema according to your requirement and map the res to the schema.
//Schema for users
var UsersSchema = mongoose.Schema({
links: {
self: String
},
data:[{
type: String,
attributes: {
username: String,
.
.
.
}
}]
})
id for each document in an array will be created automatically by MongoDB.
For mongoose schema docs see http://mongoosejs.com/docs/guide.html
You can also check this simple TODO API for reference https://github.com/mkujaggi/node-course-todo-api
In this condition's statement:
if(result[key].hasOwnProperty(i)) {
You should be accessing the i of result[key]:
if(result[key].hasOwnProperty(i)) {
attr.username = result[key][i].username;
attr.email = result[key][i].email;
attr.password = result[key][i].password;
attr.hashpassword = result[key][i].hashpassword;
attr.oldpassword = result[key][i].oldpassword;
attr.salt = result[key][i].salt;
attr.usertype = result[key][i].usertype;
attr.userstatus = result[key][i].userstatus;
}
And you don't need all those .hasOwnProperty checks unless you're modifying native prototypes, which you probably shouldn't. And especially for the Array, it's not necessary if you'd just loop properly with a for instead of a for-in.
exports.listUsers = (req, res) => {
UsersModel.find({}, (err, result) => {
if (err) {
res.send({
code: 400,
failed: 'Error 400'
});
}
let listData = [];
let data = {};
let attr = {};
data.type = result[key].type;
data.id = result[key]._id;
for (let i = 0; i < result[key].length; i++) {
attr.username = result[key][i].username;
attr.email = result[key][i].email;
attr.password = result[key][i].password;
attr.hashpassword = result[key][i].hashpassword;
attr.oldpassword = result[key][i].oldpassword;
attr.salt = result[key][i].salt;
attr.usertype = result[key][i].usertype;
attr.userstatus = result[key][i].userstatus;
}
data.attribute = attr;
listData.push(data);
}
}
let collections = {
"meta": {
"copyright": "Copyright 2018 Kotakku Studio and Lab",
"authors": [
"sw. saputra"
]
},
"link": {
"self": req.protocol + '://' + req.get('host') + req.originalUrl
},
"data": listData
}
res.json(collections);
});
};
I would do something like this with iterating over the keys (this will work dynamically that is even if we don't know key names)
var test=[
{
"type": "users",
"userstatus": "not aktif",
"_id": "5b647bb8998248235a0aab3c",
"username": "luke",
"email": "luke#mail.com",
"password": "password",
"usertype": "bisnis",
"hashpassword": "$2a$10$eyt6YV6m2JJrebNxvS0iEuxMCubDXratNJ6/XK797IGvepXBdp9Yq",
"salt": "2q6eN9U0vWFBsIF1MtB5WrgPiB8pldTS",
"__v": 0
},
{
"type": "users",
"userstatus": "tidak aktif",
"_id": "5b647bdf998248235a0aab3d",
"username": "ken",
"email": "ken#mail.com",
"password": "password",
"usertype": "personal",
"hashpassword": "$2a$10$hok988mszyIBiXVNjmfifOiPNzXkBRRRynXJS/0qCkvlaBOQs65MO",
"salt": "IiMvtVYVqTpZFXmYQIM4IlS6PJFVZ3kw",
"__v": 0
}
]
var listData=[]
test.forEach((obj)=>{
var tempObj={attributes:{}}; //initialize tempObj
Object.keys(obj).forEach((key)=>{
if(key=="_id" || key=="type"){
tempObj[key]=obj[key];
}else{
tempObj.attributes[key]=obj[key];
}
})
listData.push(tempObj);
})
let collections = {
"meta": {
"copyright": "Copyright 2018 Kotakku Studio and Lab",
"authors": [
"sw. saputra"
]
},
"link": {
"self": 'test url'
},
"data": listData
}
console.log(collections)