express and knex are beating me a little; I can't make this endpoint work using req.querys (response from express), even though I made one with req.params and it was ok.
Express:
app.get(`/actor`, async (req: Request, res: Response) => {
try {
// const { gender } = req.query;
const count = await getActorsByGender(req.query.gender as string);
console.log({ count });
res.status(200).send({ quantity: count, });
} catch (error) {
res.status(200).send({ message: error.sqlMessage || error.message });
}
});
Knex requisition:
const getActorsByGender = async (gender: string): Promise<any> => {
try {
const result = await connection.raw(`
SELECT COUNT(*) as count FROM Actor
WHERE gender = "${gender}"
`);
// console.log(`Temos: ${result[0][0].count} ocorrĂȘncias`);
return result;
} catch (error) {
console.log(error);
}
};
This might be because of the count(), but I'm not sure. The knex part is ok; I can console.log() the result.
The express part showed a empty object on insomnia
Using 'male' as parameter it was expected to return "2" as result.
Your sending male as a route/path parameter since you use http://localhost:3000/actor/male.
If you want to access it as a query-param, you can leave your code as it is, but you need to change your request-url to http://localhost:3000/actor?gender=male
Note that ff you wanted to define gender as a route-parameter, you'd need to change your route-handler to app.get("/actor/:gender") and access it using req.params.gender.
You are using path param in insomnia, not query param
If you want to use query params, you have to remove /male from the URL, and add query param key and value below the URL.
You could also change the URL to locahost:3000/actor?gender=male
Related
Recently I start using MongoDB with Mongoose on Nodejs.
This code works as it should, and returns me all data i need :
const getAllPosts = async () => {
try {
return (await PostModel.find().populate('user')).reverse();
} catch (error) {
console.log(error);
throw Error('Error while getting all posts');
}
};
But now I only need individual posts, which in the tags (represented as an array in the PostModel) contain the data that I will pass in the request.
For example, I will make a GET request to /posts/tag111 and should get all posts that have "tag111" in the tags array.
Any ways to do this?
If you are using expressjs, your route should be something like:
whatever.get('/:tag', postController.getAllPostTagged)
The function you use in your route (called getAllPostTagged above) should be similar to this one, in which you get the path param tag from req:
const postController = {
getAllPostTagged = async(req, res) => {
try {
return (await PostModel.find({tags: req.params.tag}));
} catch (error) {
console.log(error);
throw Error('Error while getting all posts');
}
}
}
The key here is to know where the params are obtained (from req.params) .
I was trying to send put request using axios to update information about a book in mongodb, the code is working fine when using postman but it is not working using axios.put used inside react component when submitting, while axios.delete is working fine. I think the problem is that sending the query object in that way is not the right way but I am not able to find the solution.
This is the function that handleSubmit,
the 'id' is the id of the book
'updates' is a state object that contains all the changes that should happen in the book data.
And the second function handleChange is the function that setupdates according to changes in the inputs
const handleSubmit = async (e) => {
e.preventDefault();
try {
const res = await axios.put("http://localhost:8000/book/edit/" + id, {
params: {
updates,
},
});
setNewBook(res.data)
} catch (err) {
console.log(err);
}
};
const handleChange = (e) => {
e.preventDefault();
const value = e.target.value;
setUpdates({
...updates,
[e.target.name]: value,
});
};
maybe you misunderstood the axios put method. as per the axios docs provided (https://github.com/axios/axios#axiosputurl-data-config) the correct structure is axios.put(url[, data[, config]])
The first parameter is URL
The second parameter is data (body request).
3rd parameter is config (you leave params in this parameter)
It should be like this
const res = await axios.put("http://localhost:8000/book/edit/" + id, null, {
params: {
updates,
},
});
I hope this can help you
Building a NodeJS REST API.
Trying to send load data from FireBase collection, then sending it to the user (as API response).
Looks like the problem is that it's not waits for the firebase fetch to resolve, but send back a response without the collection data. (tried to use ASYNC-AWAIT but its not working)
exports.getChatMessages = async (req, res, next) => {
const chatId = req.params.chatId
const getChatData = () => {
db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot((snapshot) => {
snapshot.docs.forEach(msg => {
console.log(msg.data().messageContent)
return {
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
}
})
})
}
try {
const chatData = await getChatData()
console.log(chatData)
res.status(200).json({
message: 'Chat Has Found',
chatData: chatData
})
} catch (err) {
if (!err.statusCode) {
err.statusCode(500)
}
next(err)
}
}
As you can see, I've used 2 console.logs to realize what the problem, Terminal logs looks like:
[] (from console.logs(chatData))
All messages (from console.log(msg.data().messageContent))
Is there any way to block the code unti the firebase data realy fetched?
If I correctly understand, you want to send back an array of all the documents present in the messages subcollection. The following should do the trick.
exports.getChatMessages = async (req, res, next) => {
const chatId = req.params.chatId;
const collectionRef = db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc');
try {
const chatsQuerySnapshot = await collectionRef.get();
const chatData = [];
chatsQuerySnapshot.forEach((msg) => {
console.log(msg.data().messageContent);
chatData.push({
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
});
});
console.log(chatData);
res.status(200).json({
message: 'Chat Has Found',
chatData: chatData,
});
} catch (err) {
if (!err.statusCode) {
err.statusCode(500);
}
next(err);
}
};
The asynchronous get() method returns a QuerySnapshot on which you can call forEach() for enumerating all of the documents in the QuerySnapshot.
You can only await a Promise. Currently, getChatData() does not return a Promise, so awaiting it is pointless. You are trying to await a fixed value, so it resolves immediately and jumps to the next line. console.log(chatData) happens. Then, later, your (snapshot) => callback happens, but too late.
const getChatData = () => new Promise(resolve => { // Return a Promise, so it can be awaited
db.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot(resolve) // Equivalent to .onSnapshot((snapshot) => resolve(snapshot))
})
const snapshot = await getChatData();
console.log(snapshot)
// Put your transform logic out of the function that calls the DB. A function should only do one thing if possible : call or transform, not both.
const chatData = snapshot.map(msg => ({
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
}));
res.status(200).json({
message: 'Chat Has Found',
chatData
})
Right now, getChatData is this (short version):
const getChatData = () => {
db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot((snapshot) => {}) // some things inside
}
What that means is that the getChatData function calls some db query, and then returns void (nothing). I bet you'd want to return the db call (hopefully it's a Promise), so that your await does some work for you. Something along the lines of:
const getChatData = async () =>
db
.collection('chats')
// ...
Which is the same as const getChatData = async() => { return db... }
Update: Now that I've reviewed the docs once again, I see that you use onSnapshot, which is meant for updates and can fire multiple times. The first call actually makes a request, but then continues to listen on those updates. Since that seems like a regular request-response, and you want it to happen only once - use .get() docs instead of .onSnapshot(). Otherwise those listeners would stay there and cause troubles. .get() returns a Promise, so the sample fix that I've mentioned above would work perfectly and you don't need to change other pieces of the code.
My issue is different than others, when I passed /:id then I return JSON yeah its ok, but issue is that when I give wrong objectId it return null value with statusCode 200 instead of error that this id is wrong. according to my perception it call .catch block instead of .then block because id is not available on database.
const get_id_docs = async (req, res) => {
await models
.findById(req.params.id)
.then(result => {
res.send(result)
})
.catch(err => {
res.sendStatus(404).send("Link Not Found")
})
};
There are two cases, one an invalid id, and the other a valid id but doesn't exists in db.
If you want to differentiate an invalid id, you can validate it before querying, and return 404.
Also you mixed async await and Promise, one of them must be used in this case.
const mongoose = require("mongoose");
const get_id_docs = async (req, res) => {
const isValidId = mongoose.Types.ObjectId.isValid(req.params.id);
if (!isValidId) {
res.status(404).send("Link Not Found - invalid id");
}
try {
const result = await models.findById(req.params.id);
if (result) {
res.send(result);
}
res.status(404).send("Link Not Found - does not exists");
} catch (err) {
res.status(500).send(err.message);
}
};
And if you prefer then catch
const mongoose = require("mongoose");
const get_id_docs = (req, res) => {
const isValidId = mongoose.Types.ObjectId.isValid(req.params.id);
if (!isValidId) {
res.status(404).send("Link Not Found - invalid id");
}
models.findById(req.params.id).then(result => {
if (result) {
res.send(result);
}
res.status(404).send("Link Not Found - does not exists");
})
.catch (err) {
res.status(500).send(err.message);
}
};
You are composing various general-purpose libraries that fill a variety of use cases. In particular, database abstraction frameworks typically try to percent a collection like facade over the underlying data stores. In JavaScript, methods like Array.prototype.find return undefined rather than throwing errors. And I think the authors of mongoose are trying to write analogously behaving APIs.
In addition to providing intuitive default Behavior, by not throwing errors, this enable a wider range of use cases, such as checking for existence, to be handled without boilerplate.
Given that, you want something like the following
const get_id_docs = async (req, res) => {
const result = await models.findById(req.params.id);
if (result) {
res.send(result);
}
res.sendStatus(404).send("Link Not Found");
};
Note that the above has other advantages including that it does not propagate other kinds of errors as 404s arbitrarily.
I'm working on a project that requires me to:
GET IDs from API1, push the IDs into an array, then map over those IDs, using them for a second GET request, where IDs are used as params for API2 GET request, populates an array with IDs or N for "Not existing" -- this array is then called in:
A POST request. This post maps over the returned array from the GET request. IF the item is not "N", it POSTS to API1 with checked: true. IF the item is "N", it emails us telling us API2 is missing this project.
I want this system to automatically do a GET and POST every 2 hours, so I'm using setInterval (not sure this is the best idea). EDIT: Cron job would be a better solution.
I'm working with NodeJS, Express, Request-Promise, Async / Await.
Here is some of my pseudo code so far:
// Dependencies
const express = require('express');
const axios = require('axios');
const mailgun = require('mailgun-js')({ apiKey, domain });
// Static
const app = express();
app.get('/', (req, res, next) => {
// Replace setInterval with Cron job in deployment
// Get All Ids
const orders = await getGCloud();
// Check if IDs exist in other API
const validations = await getProjectManagementSystem(orders);
// If they exist, POST update to check, else, mailer
validations.map(id => {
if (id !== 'n') {
postGCloud(id);
} else {
mailer(id);
}
});
}
// Method gets all IDs
const getGCloud = async () => {
try {
let orders = [];
const response = await axios.get('gCloudURL');
for (let key in response) {
orders.push(response.key);
}
return orders;
} catch (error) {
console.log('Error: ', error);
}
}
// Method does a GET requst for each ID
const getProjectManagementSystem = async orders => {
try {
let idArr = [];
orders.map(id => {
let response = await axios.get(`projectManagementSystemURL/${id}`);
response === '404' ? idArr.push('n') : idArr.push(response)
})
return idArr;
} catch (error) {
console.log('Error: ', error);
}
}
const postGCloud = id => {
axios.post('/gcloudURL', {
id,
checked: true
})
.then(res => console.log(res))
.catch(err => console.log(err))
}
const mailer = id => {
const data = {
from: 'TESTER <test#test.com>',
to: 'customerSuppoer#test.com',
subject: `Missing Order: ${id}`,
text: `Our Project Management System is missing ${id}. Please contact client.`
}
mailgun.messages().send(data, (err, body) => {
if (err) {
console.log('Error: ', err)
} else {
console.log('Body: ', body);
}
});
}
app.listen(6000, () => console.log('LISTENING ON 6000'));
The TL;DR: Need to do a GET request to API 1, then another GET request to API 2 following it (using IDs from API 1 as params), then send data from second GET to a POST request that then either updates API 1's data or emails Customer support. This is an automatic system that runs every two hours.
Main Questions:
1. Is it okay to have a setInterval in a get req?
2. Can I have a GET request automatically call a POST request?
3. If so, how can I pass GET request data onto a POST request?
To make it work for both of your calls one post and one get you have to do an Ajax call to get post processed information in another method.
I hope this works.