How to allow users to add in parameter in api? - node.js

How to allow users to add in parameter in api? i.e http://localhost:8080/amk_codes?collada_north=1.361692308&collada_east=103.8527273
I have the following codes:
app.get("/amk_codes", async (req, res) => {
const rows = await readamk_codes();
res.send(JSON.stringify(rows))
})
async function readamk_codes() {
try {
const results = await client.query("select collada_north,collada_south,collada_east from amk_codes");
return results.rows;
}
catch(e){
return [];
}
}

The parameters in url are located in req.query object. In your example you can access to collada_north and collada_south values with req.query.collada_north and req.query.collada_south.
app.get("/amk_codes", async (req, res) => {
var collada_north = req.query.collada_north
var collada_south = req.query.collada_south
const rows = await readamk_codes();
res.send(JSON.stringify(rows))
})
...
Of course you have to check if parameters are present in the request ( they could also be null I think ).

Related

Chaining GET requests to WP REST API in Express

I am struggling to understand callbacks, promises, and async/await.
What I want to do is read a .csv file inside my project folder that contains 150+ post ID's.
For each one of those ID's I want to make a https GET request to fetch a JSON response from my Wordpress website.
Then for each one of those posts that gets returned I want to insert them in my Firestore database.
I'm struggling with how to properly set up the callback functions.
Please help.
Thanks in advance.
const express = require('express');
const router = express.Router();
const https = require("https");
const Recipe = require("../includes/newrecipe");
var admin = require('firebase-admin');
var serviceAccount = require("../service_key.json");
const collectionKey = "recipes"; //name of the collection
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "<MY_FIRESTORE_URL>"
});
const firestore = admin.firestore();
const fs = require('fs');
const parse = require('csv-parser');
function prepareCsvData() {
return new Promise((resolve, reject) => {
//establish empty csvData array and filename to be referenced
var csvData = [];
var filename = 'wprm_recipe_ids.csv';
//read the csv file and push the data object into the array
fs.createReadStream(filename)
.pipe(parse(['ID']))
.on('data', (data) => csvData.push(data))
.on('end', () => { resolve(csvData); });
});
}
function getRecipeFromBlog(recipeId) {
return new Promise((resolve, reject) => {
//make the get request to my website to get the recipe
https.get('<MY_WEBSITE_URL>' + recipeId, (response) => {
var body = "";
response.on('data', function (chunk) { body += chunk; });
response.on('end', () => {
var { recipe } = JSON.parse(body);
//build new recipe to be exported
var newRecipe = new Recipe(recipe);
resolve(newRecipe);
});
});
});
}
/* GET recipes. */
router.get('/', async (req, res, next) => {
//first prepare the csv data
//function returns a promise with the csv data
//that I can then use in the next step
const csvData = await prepareCsvData();
for (var i = 0; csvData.length < i; i++) {
getRecipeFromBlog(csvData[i].ID)
.then((newRecipe) => {
//when I have a recipe for a given recipe ID
//update database in firestore
firestore
.collection(collectionKey)
.doc(""+newRecipe.id)
.set(newRecipe)
.then(function() {
console.log('document written');
});
});
}
res.send('done');
});
You need to do something like below:
Play around this, You'll get it working hopefully!
Let me know if that worked!
router.get("/", async (req, res, next) => {
const csvData = await prepareCsvData();
const recipePromises = [];
// check if data is empty or not
if (!csvData.length) {
return res.send("Empty data");
}
csvData.forEach((element) => {
recipePromises.push(getRecipeFromBlog(element.id));
});
// await for all promises parallelly.
const result = await Promise.all(recipePromises);
// Get a new write batch
const batch = db.batch();
result.forEach((recipe) => {
const ref = db.collection("recipes").doc(`${recipe.id}`);
batch.set(ref, recipe);
});
// Commit the batch
await batch.commit();
res.send("done");
});
The OP code looks pretty close to working. Have the promise-returning functions been tested? Assuming they work, first decorate them as async...
async function prepareCsvData() {...}
async function getRecipeFromBlog(recipeId) {...}
Create another promise-returning function to insert many recipes into firebase...
async function writeRecipesToFB(recipes) {
const collectionRef = collection(collectionKey);
const promises = recipes.map(recipe => {
return collectionRef.doc(`${recipe.id}`).set(recipe);
});
return Promise.all(promises)
}
As another answer suggests, as an alternative, firebase's batch write is a good idea...
async function writeRecipesToFB(recipes) {
// as a set of promises
const collectionRef = collection(collectionKey);
const batch = db.batch();
recipes.forEach(recipe => {
const docRef = collectionRef.doc(`${recipe.id}`)
batch.set(docRef, recipe)
});
return batch.commit();
}
Now the express function is easy to write...
router.get('/', async (req, res, next) => {
const csvData = await prepareCsvData();
let promises = csvData.map(row => {
return getRecipeFromBlog(row.ID);
});
const recipes = await Promise.all(promises);
await writeRecipesToFB(recipes);
res.send('done');
});

Make multiple firestore queries in a cloud function

I want to create a scheduled cloud function that is generating employees bonus at the end of each month.
To do that, i need a list of all employees, of all invoices of that user and of all existing bonus, all contained in firestore collections.
So i need 3 firestore collections but can't find any solution on how to do query that in a cloud function.
i tried this for now :
exports.generateBonus = functions.https.onRequest(async (req, res) => {
var listEmployee = [];
var listInvoice = [];
const employeeRef = admin.firestore().collection('employee');
const invoiceRef = admin.firestore().collection('invoice');
const promiseFacture = new Promise((resolve,reject)=>{
return factureRef.get();
})
.then(list_invoice => {
listInvoice = list_invoice.docs.map(doc => {
return doc.data();
});
})
.catch(error => {
console.log("got an error",error);
});
const promiseEmployee = new Promise((resolve,reject)=>{
return employeeRef.get();
})
.then(list_employee => {
listEmployee = list_user.docs.map(doc => {
return doc.data();
});
})
.catch(error => {
console.log("got an error",error);
});
Promise.all([promiseInvoice, promiseEmployee])
.then((values) => {
console.log(values);
return res.send('ok');
})
.catch(error => {
console.log(error);
})
});
But it return me two empty arrays in 1 sec
Does anyone know how to do this ? Thank you
The following, using destructuring assignment syntax, should do the trick:
exports.generateBonus = functions.https.onRequest(async (req, res) => {
const employeesRef = admin.firestore().collection('employee');
const invoicesRef = admin.firestore().collection('invoice');
const [employeesSnapshot, invoicesSnapshot] = await Promise.all([employeesRef.get(), invoicesRef.get()]);
const listEmployees = employeesSnapshot.docs;
const listInvoices = invoicesSnapshot.docs;
//Logging
listEmployees.forEach(snap => {
console.log(snap.data());
});
listInvoices.forEach(snap => {
console.log(snap.data());
});
//...
res.status(200).send(...); //Adapt the ... to a meaningful value
});
Note that the get() method returns a Promise, so you don't need to wrap it in another Promise.
(note that I have added an s to all the collections/snapshots variables names).

How should I parse URL parameters using Node JS express?

I would like to pass the lat lon from this URL http://localhost:8080/fp?lon=103.742463567216646&lat=1.336711421273283. Where should I place the "req.query.lon" and "req.query.lat" in the following code?
I am hoping to parse the users input in the URL link so that I can dynamically retrieve from the database.
const {Client} = require("pg")
const express = require ("express")
const url=require('url')
const fs= require('')
const app = express();
app.use(express.json())
const client = new Client({
"user": "xxx",
"password" : "xxx",
"host" : "xxx",
"port" : xxx,
"database" : "xxx"
})
//app.get("/", (req, res) => res.sendFile(`${__dirname}/index.html`))
app.get("/fp", async (req, res) => {
//const lon = 103.742463567216646;
//const lat = 1.336711421273283;
//const lon = req.query.lon;
//const lat = req.query.lat;
const rows = await readTodos ();
res.send(JSON.stringify(rows))
})
app.listen(8080, () => console.log("Web server is listening.. on port 8080"))
start()
async function start() {
await connect();
}
async function connect() {
try {
await client.connect();
}
catch(e) {
console.error(`Failed to connect ${e}`)
}
}
async function readTodos() {
try {
const results = await client.query("SELECT ST_AsText(ST_Force2D(geom)) FROM fp ORDER BY geom <-> ST_SetSRID(ST_MakePoint("+lon+ ","+lat+ "), 3993) LIMIT 1;");
return results.rows;
}
catch(e){
return [];
}
}
Express parses it by default and store them in req.query.
Accessing req.query.<key> will give you the value you are looking for.
For instance, in your example, express will store it inside req.query.lon and req.query.lat.
So, actually you access them right in the request handler of /fp, just comment out the access to these variables and pass them as parameters to readTodos():
app.get("/fp", async (req, res) => {
const lon = req.query.lon;
const lat = req.query.lat;
const rows = await readTodos(lon, lat);
res.send(JSON.stringify(rows))
})
async function readTodos(lon, lat) {
try {
const results = await client.query("SELECT ST_AsText(ST_Force2D(geom)) FROM fp ORDER BY geom <-> ST_SetSRID(ST_MakePoint("+lon+ ","+lat+ "), 3993) LIMIT 1;");
return results.rows;
}
catch(e){
return [];
}
}

Mongo/Express: How to return all documents in collection if no query params are passed?

I'm trying to return all documents from my Mongo collection if no query parameters are passed. Currently I have 3 optional query parameters that could be passed by the user.
localhost:3000/api/projects
//should return all projects. Currently this is returning []
localhost:3000/api/projects?id=1
//should return projects with id of "1". Working properly.
localhost:3000/api/projects?name=myproject
//should return projects with name of "myproject". Working properly.
localhost:3000/api/projects?created_by=John
//should return projects created by "John". Working properly.
Within my route, I'm trying to determine my request has any query values. If it does not, then I want to return all documents in the collection. As stated above, this is not returning anything.
router.get('/', async (req, res) => {
if (req.query !== '') {
const project = await Projects.find({
$or: [
{ _id: req.query.id },
{ name: req.query.name },
{ created_by: req.query.created_by }]
});
res.json(project);
}
else {
const project = await Projects.find();
res.json(project);
}
});
Try as below:
router.get('/', async (req, res) => {
let searchQuery = {}
if(req.query.id){
searchQuery._id = req.query.id
}
if(req.query.name){
searchQuery.name = req.query.name
}
if(req.query.created_by){
searchQuery.created_by = req.query.created_by
}
const project = await Projects.find(searchQuery);
res.json(project);
});
You can write your api handler like this:
router.get('/', async (req, res)=>{
let options = {...req.query};
try{
const project = await Projects.find(options);
res.json(project);
}catch(e){
console.log(e);
}
});
This will fetch the documents on the basis of your query. If there is no query params req.query will be empty object and hence it will find all documents.
Hope this helps!!
router.get('/', async (req, res) => {
const id = req.query.id || null;
const name = req.query.name || null;
const created_by = req.query.created_by || null;
const query = { id, name, created_by };
const project = await Projects.find(query);
res.json(project);
});
I didn't test it, but I would solve your problem this way.

Adding custom middleware

I have a route with a req.param of :groupIndex in it. I would like to process this index as middleware in order to get a specific id.
Node and express are new to me, so I'm possibly missing something simple but reading docs and looking at other implementations hasn't seemed to work.
Any idea where I might be going wrong?
// routes.js
const express = require("express");
const router = express.Router();
const customMiddleware = ('./customMiddleware.js');
const db = require('./mysqlCon.js');
router.get('/person/:groupIndex(\\d+)', customMiddleware(), function (req, res) {
let id = req.params.id;
let query = `
SELECT
*
FROM
data
WHERE
id = ?
`;
let query_params = [id];
db.query(
query,
query_params,
function(error, result, fields) {
if ( result.length == 1 ) {
res.status(200).json( result[0] );
} else {
res.status(401).json({});
}
}
);
});
// customMiddleware.js
const db = require('./mysqlCon.js');
module.exports = (req, res, next) => {
let groupIndex = parseInt(req.params.groupIndex);
let query = `
SELECT
id
FROM
listofids
LIMIT 1
OFFSET ?
`;
let query_params = [groupIndex];
db.query(
query,
query_params,
function(error, result, fields) {
if ( result.length == 1 ) {
req.params.id = result[0].id;
} else {
res.status(401).json({});
}
}
);
next();
}
I would review the middleware guide, but generally all middleware functions have the following signature:
function myMiddleware(req, res, next)
Where req, res, next are passed in from Express itself. You do not invoke the function, you simply pass it in as an argument (higher-order functions) to your route definition:
router.get('/person/:groupIndex(\\d+)', myMiddleware, ...)

Resources