I am building out the backend for a flash card app for which you can find the repo here. There is a table called categories. There is an endpoint for adding a category.
router.post("/", protect, createCategory);
The endpoint runs a createCategory function which has logic separated out into a categoryController.js file.
const Categories = require("../models/categoryModel");
const createCategory = async (req, res) => {
const { title } = req.body;
const userId = req.user.id;
if (!title) {
res.status(404).json({ errMsg: "Please provide a title" });
} else {
const category = await Categories.createCategory({ title, userId });
console.log("category: ", category);
res.status(201).json(category);
}
};
The createCategory controller function in turn calls a createCategory function from the categoryModel.js file. The function from the model runs the database operations. Specifically, it inserts a new category into the database and references another function--getCategoryById--to return the newly created category.
const getCategoryById = (id) => {
return db("categories").where({ id }).first();
};
const createCategory = (category) => {
return db("categories")
.insert(category, "id")
.then((ids) => {
const [id] = ids;
return getCategoryById(id);
});
};
The problem is when I make the .post to create a new category, nothing is returned in the response. The database gets updated just fine but nothing is returned. You can see that I put a console.log in the controller and that is coming back undefined. I am not sure why.
Related
I'm trying to make GET request to external API (Rick and Morty API). The objective is setting a GET request for unique character, for example "Character with id=3". At the moment my endpoint is:
Routes file:
import CharacterController from '../controllers/character_controller'
const routes = app.Router()
routes.get('/:id', new CharacterController().get)
export default routes
Controller file:
async get (req, res) {
try {
const { id } = req.params
const oneChar = await axios.get(`https://rickandmortyapi.com/api/character/${id}`)
const filteredOneChar = oneChar.data.results.map((item) => {
return {
name: item.name,
status: item.status,
species: item.species,
origin: item.origin.name
}
})
console.log(filteredOneChar)
return super.Success(res, { message: 'Successfully GET Char request response', data: filteredOneChar })
} catch (err) {
console.log(err)
}
}
The purpose of map function is to retrieve only specific Character data fields.
But the code above doesn't work. Please let me know any suggestions, thanks!
First of all I don't know why your controller is a class. Revert that and export your function like so:
const axios = require('axios');
// getCharacter is more descriptive than "get" I would suggest naming
// your functions with more descriptive text
exports.getCharacter = async (req, res) => {
Then in your routes file you can easily import it and attach it to your route handler:
const { getCharacter } = require('../controllers/character_controller');
index.get('/:id', getCharacter);
Your routes imports also seem off, why are you creating a new Router from app? You should be calling:
const express = require('express');
const routes = express.Router();
next go back to your controller. Your logic was all off, if you checked the api you would notice that the character/:id endpoint responds with 1 character so .results doesnt exist. The following will give you what you're looking for.
exports.getCharacter = async (req, res) => {
try {
const { id } = req.params;
const oneChar = await axios.get(
`https://rickandmortyapi.com/api/character/${id}`
);
console.log(oneChar.data);
// return name, status, species, and origin keys from oneChar
const { name, status, species, origin } = oneChar.data;
const filteredData = Object.assign({}, { name, status, species, origin });
res.send(filteredData);
} catch (err) {
return res.status(400).json({ message: err.message });
}
};
I am trying to build a EJS form with three fields and I need to pass two sets of data to it at the same time: users and books using promises. Unfortunatly books are not getting passed and stay 'undefined'. I cannot figure out why.
Form
Textfield (irrelevant for this example)
Dropdown box with a list of users
Dropdown box with a list of books
For (2) and (3) I query my mysql database to get the data so that I can fill the form drop-down-boxes.
/controller.js
const User = require('../models/user.js');
const Book = require('../models/book.js');
exports.getAddNeueAusleihe = (req, res, next) => {
// storing users and books in this object
let view_data_for_my_view = {};
// fetch users for dropdown-box nr.1
User.fetchAll()
.then(([users_rows]) => {
view_data.users = users_rows;
// fetch books for dropdown-box nr. 2
return Book.fetchAll(([books_rows]) => {
view_data.books = books_rows;
});
})
.then(() => {
// send data to view
res.render('neue-ausleihe.ejs', {
users: view_data.users,
books: view_data.books,
pageTitle: 'Neue Ausleihe'
});
});
}
The User-fetch works fine. But the Book-fetch does return "undefined", although the SQL code in the books model works fine. It actually jumps into the books model, but does not get the values to the view. Here is my SQL-code for models.
/models/user.js
const db = require('../util/database.js');
module.exports = class User {
constructor(id, name) {
this.id = id;
this.name = name;
}
static fetchAll() {
return db.execute('SELECT * from dbuser WHERE UserFlag = "active"');
};
}
/models/books.js
const db = require('../util/database.js');
module.exports = class Book {
constructor(id, name) {
this.id = id;
this.name = name;
}
static fetchAll() {
return db.execute('SELECT * from books WHERE status = "free"');
}
}
Assuming db.execute returns a promise that resolves to an array, for which the first entry is the actual result, your code should look more like this:
exports.getAddNeueAusleihe = (req, res, next) => {
// storing users and books in this object
const view_data = {};
// fetch users for dropdown-box nr.1
User.fetchAll()
.then(([users_rows]) => {
view_data.users = users_rows;
// fetch books for dropdown-box nr. 2
return Book.fetchAll();
})
.then([book_rows] => {
view_data.books = books_rows;
})
.then(() => {
// send data to view
res.render('neue-ausleihe.ejs', {
users: view_data.users,
books: view_data.books,
pageTitle: 'Neue Ausleihe'
});
});
}
Bonus version:
exports.getAddNeueAusleihe = async(req, res, next) =>
// send data to view
res.render('neue-ausleihe.ejs', {
pageTitle: 'Neue Ausleihe',
users: (await User.fetchAll())[0]
books: (await Book.fetchAll())[0]
});
}
Even as the person writing this, it still blows my mind how much more readable async/await-based code is vs promise chains.
I have a firebase function that's supposed to return Items that are sold by a seller. I want to get the seller's profile picture via firebase authentication. But whenever I AWAIT the function
edit: worth noting that mAuth is firebase authentication*
await mAuth.geUser(sellerData.UID);
the application returns me an empty json or []
Here is the full code for the function, the error occurs on line 11 or somewhere around there.
export const getHottestItems = functions.region("asia-east2").https.onRequest(async (data, response) => {
try {
var arrayItem = new Array<Item>();
let itemSeller: Seller;
const sellerSnapshot = await db.collection("users").get();
// this is the list of promises/awaitables for all items
const promises = new Array<Promise<FirebaseFirestore.QuerySnapshot<FirebaseFirestore.DocumentData>>>();
sellerSnapshot.forEach(async (sellerDoc) => {
const sellerData = sellerDoc.data();
// THIS PART CAUSES THE API TO RETURN []
const sellerAuth = await mAuth.getUser(sellerData.UID);
// check for non null / empty strings
if (sellerData.Name as string && sellerData.UID as string) {
// this is all the seller information we need
itemSeller = new Seller(sellerData.Name, sellerData.UID, sellerAuth.photoURL); // placeholder profile picture
const refItem = sellerDoc.ref.collection("Items");
// push all the promises to a list so we can run all our queries in parallel
promises.push(refItem.get());
}
});
// wait for all promises to finish and get a list of snapshots
const itemSnapshots = await Promise.all(promises);
itemSnapshots.forEach((ItemSnapshot) => {
ItemSnapshot.forEach((ItemDoc) => {
// get the data
const itemData = ItemDoc.data();
// if title is not null, the rest of the fields are unlikely to be.
if (itemData.Title as string) {
// the rest of the logic to convert from database to model is in the constructor
arrayItem.push(new Item(ItemDoc.id, itemData.Title, itemSeller, itemData.Likes, itemData.ListedTime, itemData.Rating, itemData.Description, itemData.TransactionInformation, itemData.ProcurementInformation, itemData.Category, itemData.Stock, itemData.Image1, itemData.Image2, itemData.Image3, itemData.Image4, itemData.AdvertisementPoints, itemData.isDiscounted, itemData.isRestocked));
}
});
});
// sort by performance level
arrayItem = arrayItem.sort(x => x.Performance);
if (data.body.userID) {
arrayItem = await markLikedItems(data.body.userID, arrayItem);
}
//send the responseafter all the final modifications
response.send(arrayItem);
} catch (err) {
// log the error
console.log(err);
response.status(500).send(err);
}
});
I want to create a function with node.js but I've got stuck at a point.
Explanation of what I want to do:
First, the function will trigger when a new document added to the path profile/{profileID}/posts/{newDocument}
the function will send a notification to all the following users. the problem comes here.
I've another collection in the profile collection which is followers and contains documents of the field followerID.
I want to take this followerID and use it as a document id to access the tokenID field with I've added to the profile document.
like this:
..(profile/followerID).get(); and then access the field value of tokenID field.
My current Code:- Index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.fcmTester = functions.firestore.document('profile/{profileID}/posts/{postID}').onCreate((snapshot, context) => {
const notificationMessageData = snapshot.data();
var x = firestore.doc('profile/{profileID}/followers/');
var follower;
x.get().then(snapshot => {
follower = snapshot.followerID;
});
return admin.firestore().collection('profile').get()
.then(snapshot => {
var tokens = [];
if (snapshot.empty) {
console.log('No Devices');
throw new Error('No Devices');
} else {
for (var token of snapshot.docs) {
tokens.push(token.data().tokenID);
}
var payload = {
"notification": {
"title": notificationMessageData.title,
"body": notificationMessageData.title,
"sound": "default"
},
"data": {
"sendername": notificationMessageData.title,
"message": notificationMessageData.title
}
}
return admin.messaging().sendToDevice(tokens, payload)
}
})
.catch((err) => {
console.log(err);
return null;
})
});
my firestore database explanation.
profile | profileDocuments | posts & followers | followers collection documents & posts collection documents
I have a parent collection called profile and it contains documents as any collection these documents contain a field called tokenID and that I want to access, but I will not do this for all users only for followers (the users who follwed that profile) so I've created a new collection called followers and it contains all the followers IDs, I want to take every followerID and for each id push tokenID to tokens list.
If I understand correctly your question, you should do as follows. See the explanations below.
exports.fcmTester = functions.firestore.document('profile/{profileID}/posts/{postID}').onCreate((snapshot, context) => {
const notificationMessageData = snapshot.data();
const profileID = context.params.profileID;
// var x = firestore.doc('profile/{profileID}/followers/'); //This does not point to a document since your path is composed of 3 elements
var followersCollecRef = admin.firestore().collection('profile/' + profileID + '/followers/');
//You could also use Template literals `profile/${profileID}/followers/`
return followersCollecRef.get()
.then(querySnapshot => {
var tokens = [];
querySnapshot.forEach(doc => {
// doc.data() is never undefined for query doc snapshots
tokens.push(doc.data().tokenID);
});
var payload = {
"notification": {
"title": notificationMessageData.title,
"body": notificationMessageData.title,
"sound": "default"
},
"data": {
"sendername": notificationMessageData.title,
"message": notificationMessageData.title
}
}
return admin.messaging().sendToDevice(tokens, payload)
});
First by doing var x = firestore.doc('profile/{profileID}/followers/'); you don't declare a DocumentReference because your path is composed of 3 elements (i.e. Collection/Doc/Collection). Note also that,in a Cloud Function, you need to use the Admin SDK in order to read other Firestore documents/collections: So you need to do admin.firestore() (var x = firestore.doc(...) will not work).
Secondly, you cannot get the value of profileID just by doing {profileID}: you need to use the context object, as follows const profileID = context.params.profileID;.
So, applying the above, we declare a CollectionReference followersCollecRef and we call the get() method. Then we loop over all the docs of this Collection with querySnapshot.forEach() to populate the tokens array.
The remaining part is easy and in line with your code.
Finally, note that since v1.0 you should initialize your Cloud Functions simple with admin.initializeApp();, see https://firebase.google.com/docs/functions/beta-v1-diff#new_initialization_syntax_for_firebase-admin
Update following your comments
The following Cloud Function code will lookup the Profile document of each follower and use the value of the tokenID field from this document.
(Note that you could also store the tokenID directly in the Follower document. You would duplicate data but this is quite common in the NoSQL world.)
exports.fcmTester = functions.firestore.document('profile/{profileID}/posts/{postID}').onCreate((snapshot, context) => {
const notificationMessageData = snapshot.data();
const profileID = context.params.profileID;
// var x = firestore.doc('profile/{profileID}/followers/'); //This does not point to a document but to a collectrion since your path is composed of 3 elements
const followersCollecRef = admin.firestore().collection('profile/' + profileID + '/followers/');
//You could also use Template literals `profile/${profileID}/followers/`
return followersCollecRef.get()
.then(querySnapshot => {
//For each Follower document we need to query it's corresponding Profile document. We will use Promise.all()
const promises = [];
querySnapshot.forEach(doc => {
const followerDocID = doc.id;
promises.push(admin.firestore().doc(`profile/${followerDocID}`).get()); //We use the id property of the DocumentSnapshot to build a DocumentReference and we call get() on it.
});
return Promise.all(promises);
})
.then(results => {
//results is an array of DocumentSnapshots
//We will iterate over this array to get the values of tokenID
const tokens = [];
results.forEach(doc => {
if (doc.exists) {
tokens.push(doc.data().tokenID);
} else {
//It's up to you to decide what you want to to do in case a Follower doc doesn't have a corresponding Profile doc
//Ignore it or throw an error
}
});
const payload = {
"notification": {
"title": notificationMessageData.title,
"body": notificationMessageData.title,
"sound": "default"
},
"data": {
"sendername": notificationMessageData.title,
"message": notificationMessageData.title
}
}
return admin.messaging().sendToDevice(tokens, payload)
})
.catch((err) => {
console.log(err);
return null;
});
});
I've got the following function which works as expected on Parse Server cloud code, however it's painfully slow.
The nested for loops which are internally calling queries and save functions are undoubtedly the root cause.
How can I refactor this code so that there is some async processing or even better what methods are there to remove / edit the relations on an object, the documentation around this is very poor.
ClientLabels.applyClientLabels = async (req, res) => {
const { clients, labels } = req.params;
const user = req.user;
const objectIds = clients.map((client) => client.objectId);
const clientSaveList = [];
const clientClass = Parse.Object.extend('Clients');
const query = new Parse.Query(clientClass);
query.containedIn("objectId", objectIds);
const queryResult = await query.find({ sessionToken: user.getSessionToken() })
try {
for (const client of queryResult) {
const labelRelation = client.relation('labels');
const relatedLabels = await labelRelation.query().find({ sessionToken: user.getSessionToken() });
labelRelation.remove(relatedLabels);
for (const label of labels) {
label.className = "ClientLabels";
const labelRelationObj = Parse.Object.fromJSON(label)
labelRelation.add(labelRelationObj);
};
clientSaveList.push(client);
};
const saved = await Parse.Object.saveAll(clientSaveList, { sessionToken: user.getSessionToken() })
res.success(saved);
} catch (e) {
res.error(e);
};
}
Explanation of some weirdness:
I am having to call Parse.Object.fromJSON in order to make the client side label object a ParseObjectSubClass and allow operations on it such as adding relations.
You cannot use include on a relation query as you would with a Pointer, so there needs to be a query for relations all on it's own. An array of pointers was ruled out as there is going to be an unknown amount of labels applied.
There are a few things that can be done: (1) The creation of labels in the inner loop is invariant relative to the outer loop, so that can be done one time, at the start. (2) There's no need to query the relation if you're just going to remove the related objects. Use unset() and add to replace the relations. (3) This won't save much computation, but clientSaveList is superfluous, we can just save the query result...
ClientLabels.applyClientLabels = async (req, res) => {
const { clients, labels } = req.params;
const objectIds = clients.map((client) => client.objectId);
let labelObjects = labels.map(label => {
label.className = "ClientLabels";
return Parse.Object.fromJSON(label)
});
const query = new Parse.Query('Clients');
query.containedIn("objectId", objectIds);
const sessionToken = req.user.getSessionToken;
const queryResult = await query.find({ sessionToken: sessionToken })
try {
for (const client of queryResult) {
client.unset('labels');
client.relation('labels').add(labelObjects);
};
const saved = await Parse.Object.saveAll(queryResult, { sessionToken: sessionToken })
res.success(saved);
} catch (e) {
res.error(e);
};
}