I'm using nuxtjs/axios and Mongoose to write to MongoDB. The POST always works but it takes a few seconds for the insert to get into MongoDB. Problem is that I'm trying to call a GET immediately after a new POST so i can get all the latest records. That doesn't always happen because it takes a few seconds for the data to get into the DB. Here's my index.js file for the server:
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
const Post = require('./models/post');
const express = require('express');
const { Nuxt, Builder } = require('nuxt');
const app = express();
const mongoose = require('mongoose');
const xss = require('xss-clean');
app.use(
express.urlencoded({
extended: true
})
)
app.use(express.json())
app.use(xss());
const config = require('../nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
const nuxt = new Nuxt(config);
const { host, port } = nuxt.options.server;
const username = process.env.username;
const pwd = process.env.pwd;
const server = process.env.server;
const db = process.env.db;
const dbURI = `mongodb+srv://${username}:${pwd}#${server}/${db}?
retryWrites=true&w=majority`;
async function start() {
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
app.use(nuxt.render);
}
start();
mongoose
.connect(dbURI, {useNewUrlParser: true, useUnifiedTopology: true})
.then((result) => {
app.listen(port, host); // listen
}
)
.catch(err => console.log(err));
app.get('/posts', (req, res) => {
Post
.find()
.sort({createdAt: -1})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
})
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save()
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);
I feel like in app.post the .save() isn't waiting for the insert to complete. Is my implementation wrong? Here's my Store:
export const actions = {
async getPosts() {
let res = await this.$axios.get(`/posts`);
return res;
}
}
export const mutations = {
async savePost(state, data) {
let res = await this.$axios.post('/posts', {post: data});
return res;
}
}
And here's my index.vue file:
export default {
components: {},
data: () => ({
posts:[],
confession: ""
}),
mounted(){
this.getPosts();
},
methods: {
async getPosts() {
let res = await this.$store.dispatch('getPosts');
this.posts = res;
},
async savePost(payload) {
let res = await this.$store.commit('savePost', payload);
return res;
},
clear(){
this.confession = "";
},
focusInput() {
this.$refs.confession.focus();
},
onSubmit() {
this
.savePost(this.confession.trim())
.then((result) => {
this.playSound();
this.getPosts();
this.clear();
this.focusInput();
});
},
playSound: function(){
// sound code
}
}
}
}
Maybe you can try to add w: "majority" option in save method.
Mongoose Documentation of save options
MongoDB Documentation for further explanation of 'writeConcern'
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save({w: "majority"})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);
Related
i try to return data in node.js from a APIs , but I'm having problems, because I need an asynchronous function, I couldn't understand for sure the correct use of the promise, I've tried everything and I couldn't put the result in the return, only in the console.log, somebody help me?
const express = require('express')
const MFA = require('mangadex-full-api')
module.exports = {
async indexManga(req, res) {
const mangalist = MFA.login('DarksGol', 'R#ul1605', './md_cache/').then(async () => {
manga = []
await MFA.Manga.search('Kiss').then(results => {
results.forEach((elem, i) => {
let obj = {}
obj.status = elem.status
obj.title = elem.title
manga.push(obj)
})
}).catch(console.error)
return manga
}).catch(console.error)
console.log(await mangalist)
return mangalist
}
}
no error occurred, only infinite loading on request
const express = require('express')
const routes = express.Router()
const searchManga = require('../src/controllers/searchManga')
routes.get('/searchManga', searchManga.indexManga)
module.exports = routes
Looks like indexManga is an endpoint. Each endpoint function must end the request-response cycle by sending a response ( res.send(), res.json(), res.end(), etc). If indexManga s an endpoint, the solution would be:
...
//return mangalist
res.send(mangalist)
or
//return mangalist
res.json({ status: "success", message: "logged in successfully" })
If it is a meddleware:
async indexManga(req, res, next) {
...
//return mangalist
return next()
}
EDIT: you are using async/await with .then() improperly in some places.
Try this way:
module.exports = {
indexManga(req, res) {
MFA.login('DarksGol', 'R#ul1605', './md_cache/').then(() => {
manga = []
MFA.Manga.search('Kiss').then(results => {
results.forEach((elem, i) => {
let obj = {}
obj.status = elem.status
obj.title = elem.title
manga.push(obj)
})
res.json({ status: "success", data: manga })
}).catch((err) => {
res.json({ status: "fail", error: err })
})
}).catch((err) => {
res.json({ status: "fail", error: err })
})
}
}
I don't see why this would cause "infinite loading on request", but you could greatly simplify the code to just
const express = require('express');
const MFA = require('mangadex-full-api');
module.exports = {
async indexManga(req, res) {
await MFA.login('DarksGol', 'R#ul1605', './md_cache/')
const mangaList = [];
const results = await MFA.Manga.search('Kiss');
results.forEach(elem => {
mangaList.push({
status: elem.status,
title: elem.title,
});
});
return mangalist;
},
};
I've been reading about async and await in JS and tried to implement them in my code (which I totally messed up).
Here is my JS.
var express = require('express');
var router = express.Router();
var jsforce = require('jsforce');
const SEC_TOKEN = 'SEC_TOKEN';
const USER_ID = 'USER_ID';
const PASSWORD = 'PWD';
const { default: axios } = require('axios');
router.get("/", async (req, res, next) => {
await initConnect;
await soqlData;
await slackPostTest;
});
initConnect = async () => {
var conn = new jsforce.Connection({
loginUrl: 'https://login.salesforce.com'
});
await conn.login(USER_ID, PASSWORD + SEC_TOKEN, (err, userInfo) => {
if (err)
console.log(err);
else {
console.log(userInfo.Id);
}
});
}
soqlData = async () => {
await conn.query('Select Id, Name from Account LIMIT 1', (err, data) => {
if (err)
console.log(err);
else
return data.records[0];
})
}
slackPostTest = async () => {
await axios.post('SLACK_WEBHOOK', {
"text": "soqlRes"
})
}
module.exports = router;
What I am trying to achieve?
Initialize my connection by passing in SEC_TOKEN, USER_ID, PASSWORD to my initConnect function this will give me a connection (conn).
Use this conn and query my salesforce instance and get some other data.
post some message(currently irrelevant, but will hook up with the above response later) to my slack endpoint.
Also can someone please give me a little detailed explanation of the solution (in terms of async/await)?
Thanks
Assuming everything else about your JsForce API usage was correct (I have no experience with it, so I can't say), here's how to promisify those callback-based APIs and call them.
var express = require("express");
var router = express.Router();
var jsforce = require("jsforce");
const SEC_TOKEN = "SEC_TOKEN";
const USER_ID = "USER_ID";
const PASSWORD = "PWD";
const { default: axios } = require("axios");
router.get("/", async (req, res, next) => {
const { conn, userInfo } = await initConnect();
const data = await soqlData(
conn,
"Select Id, Name from Account LIMIT 1",
);
await slackPostTest(data.records[0]);
});
function initConnect() {
const conn = new jsforce.Connection({
loginUrl: "https://login.salesforce.com",
});
return new Promise((resolve, reject) => {
conn.login(USER_ID, PASSWORD + SEC_TOKEN, (err, userInfo) => {
if (err) return reject(err);
resolve({ conn, userInfo });
});
});
}
function soqlData(conn, query) {
return new Promise((resolve, reject) => {
conn.query(query, (err, data) => {
if (err) return reject(err);
resolve(data);
});
});
}
function slackPostTest(soqlRes) {
return axios.post("SLACK_WEBHOOK", {
text: soqlRes,
});
}
module.exports = router;
I have a PUT in a REST API that should display an error message that says "upvoted already" if the vote_score is 1 (that is, they voted already), but instead I get a generic "internal server error" message in alert which is not good UX. That's always what the error will say with what I have tried so far.
How can I get my error message to display as "upvoted already"? Or for that matter, how can I get any error message to show up with a message? I hope I have provided enough information with the API code followed with the front-end code.
What I have tried thus far is trying different things like res.status(200).json({ error: err.toString() }); and next(err).
Hopefully something simple, I am hoping for a ELI5 type answer because I am a beginner and my error-handling game is weak. Thanks.
const db = require('../db');
const express = require('express');
const debug = require('debug')('app:api:vote');
const Joi = require('joi');
const auth = require('../middleware/auth');
const admin = require('../middleware/admin');
const { required } = require('joi');
const router = express.Router();
router.use(express.urlencoded({ extended: false }));
router.use(express.json());
// general error handler
const sendError = (err, res) => {
debug(err);
if (err.isJoi) {
res.json({ error: err.details.map((x) => x.message + '.').join('\n') });
} else {
res.json({ error: err.message });
}
};
router.put('/upvote/:emojiId/', auth, async (req, res, next) => {
try {
const schema = Joi.object({
emoji_id: Joi.number().required(),
user_id: Joi.number().required(),
vote_score: Joi.number(),
});
const vote = await schema.validateAsync({
emoji_id: req.params.emojiId,
user_id: req.user.user_id,
vote_score: 1,
});
if (!(await db.findVoteByUser(vote.emoji_id, vote.user_id))) {
const upvote = await db.upvote(vote);
} else if ((await db.findVoteByUser(vote.emoji_id, vote.user_id)) == 1) {
throw new Error('Upvoted already');
}
const upvoteScore = await db.getJustUpvotesForEmoji(vote.emoji_id);
res.json(upvoteScore);
} catch (err) {
res.status(500).json({ error: err.toString() });
}
});
module.exports = router;
And the front-end...
$(document).on('click', '.upvote-emoji-button', (evt) => {
const button = $(evt.currentTarget);
const emoji_id = button.data('id');
$.ajax({
method: 'PUT',
url: `/api/vote/upvote/${emoji_id}`,
data: emoji_id,
dataType: 'json',
})
.done((res) => {
if (res.error) {
bootbox.alert(res.error);
} else {
// $('#search-emoji-form').trigger('submit');
button.addClass('btn-danger').removeClass('btn-primary');
button.parent().next().next().html(res.upvotes);
button.parent().next().next().next().next().html(res.vote_count);
button.parent().next().next().next().next().next().html(res.total_score);
}
})
.fail((xhr, textStatus, err) => {
bootbox.alert(err);
});
});
try to replace
res.status(500).json({ error: err.toString() });
with
res.status(400).send(err.toString());
Documentation
Here is what I ended up doing. It took care of my error and a few other things too. :)
//setup
const db = require('../db');
const express = require('express');
const debug = require('debug')('app:api:vote');
const Joi = require('joi');
const auth = require('../middleware/auth');
const admin = require('../middleware/admin');
const { required } = require('joi');
const router = express.Router();
router.use(express.urlencoded({ extended: false }));
router.use(express.json());
// general error handler
const sendError = (err, res) => {
debug(err);
if (err.isJoi) {
res.json({ error: err.details.map((x) => x.message + '.').join('\n') });
} else {
res.json({ error: err.message });
}
};
router.put('/upvote/:emojiId/', auth, async (req, res, next) => {
let vote = {};
try {
const schema = Joi.object({
emoji_id: Joi.number().required(),
user_id: Joi.number().required(),
vote_score: Joi.number(),
});
vote = await schema.validateAsync({
emoji_id: req.params.emojiId,
user_id: req.user.user_id,
vote_score: 1,
});
if (!(await db.findUserByID(req.user.user_id))) {
throw new Error('log in again.');
}
const tester = await db.findVoteByUser(vote.user_id, vote.emoji_id);
if (!(await db.findVoteByUser(vote.user_id, vote.emoji_id))) {
await db.upvotePost(vote);
const upvoteScore = await db.getJustUpvotesForEmoji(vote.emoji_id);
const message = 'message';
upvoteScore[message] = 'Upvote sent.';
const action = 'action';
upvoteScore[action] = 1;
res.json(upvoteScore);
} else if (tester.vote_score == -1) {
await db.upvotePut(vote);
const upvoteScore = await db.getJustUpvotesForEmoji(vote.emoji_id);
const message = 'message';
upvoteScore[message] = 'Downvote changed to upvote.';
const action = 'action';
upvoteScore[action] = 2;
res.json(upvoteScore);
} else {
await db.deleteVoteByUserIdAndEmojiId(vote);
const upvoteScore = await db.getJustUpvotesForEmoji(vote.emoji_id);
const message = 'message';
upvoteScore[message] = 'Upvote deleted.';
const action = 'action';
upvoteScore[action] = 3;
res.json(upvoteScore);
}
} catch (err) {
sendError(err, res);
}
});
module.exports = router;
and front end..
$(document).on('click', '.upvote-emoji-button', (evt) => {
const button = $(evt.currentTarget);
const emoji_id = button.data('id');
$.ajax({
method: 'PUT',
url: `/api/vote/upvote/${emoji_id}`,
data: emoji_id,
dataType: 'json',
})
.done((res) => {
if (res.error) {
bootbox.alert(res.error);
} else {
if (res.action == 1) {
button.addClass('btn-danger').removeClass('btn-primary');
button.parent().next().next().html(res.upvotes);
button.parent().next().next().next().next().html(res.vote_count);
button.parent().next().next().next().next().next().html(res.total_score);
bootbox.alert(res.message);
} else if (res.action == 2) {
button.addClass('btn-danger').removeClass('btn-primary');
button.parent().next().children().addClass('btn-primary').removeClass('btn-danger');
button.parent().next().next().html(res.upvotes);
button.parent().next().next().next().next().html(res.vote_count);
button.parent().next().next().next().next().next().html(res.total_score);
bootbox.alert(res.message);
} else if (res.action == 3) {
button.removeClass('btn-danger').addClass('btn-primary');
button.parent().next().next().html(res.upvotes);
button.parent().next().next().next().next().html(res.vote_count);
button.parent().next().next().next().next().next().html(res.total_score);
bootbox.alert(res.message);
}
}
})
.fail((xhr, textStatus, err) => {
bootbox.alert(err);
// alert(`${textStatus}\n${err}\n${xhr.status}`);
});
});
I've been going crazy for a couple of days with an unresolved issue. Please help.
Working with a CSV database here. Creating own API. Small React App Fullstack MERN.
I'm trying to get to show all the employeees from my database.
I wrote the backend with express and have all the data now showing in json format on localhost:5000/employees
Also created a context on the front to deal with that data and reducer file for the functions.
Problem I have is I'm not beeing able to use the data on the ContextProvider file. ('EmployeesState')
Promise of my async function keeps giving me an undefined response. Tried both with fetch and axios but is not working.
Some of the code is from a previous project i did and it worked there so i 'm going crazy here. I can't seem to solve it. Please help.
BACKEND
db.js
const parse = require("csv-parse");
const fs = require("fs");
const employeesData = [];
const connectDB = () => {
fs.createReadStream(__dirname + "/employees1.txt")
.pipe(
parse({
delimiter: ",",
columns: true,
})
)
.on("data", (dataRow) => {
employeesData.push(dataRow);
})
.on("end", () => {
console.log(employeesData);
});
};
connectDB();
// console.log(connectDB());
module.exports = connectDB;
module.exports = employeesData;
server.js
var createError = require("http-errors");
var express = require("express");
const bodyParser = require("body-parser");
var cookieParser = require("cookie-parser");
const logger = require("morgan");
const cors = require("cors");
const connectDB = require("./config/db");
const employeesData = require("./config/db");
var path = require("path");
var app = express();
// const config = require("config");
//Connect DB
// connectDB();
let dataBase = employeesData;
console.log(employeesData);
app.use(cors());
// app.use(bodyParser.json()); //here
// Initial Middleware
//By doing this we can accepta data. Using the req.body
app.use(express.json({ extended: false }));
///Define Routes
app.use("/", require("./routes/index"));
app.use("/employees", require("./routes/employees"));
//Serve React in production
if (process.env.NODE_ENV === "production") {
//Set static folder (build folder)
app.use(express.static("client/build"));
app.get("*", (req, res) =>
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"))
);
}
// app.engine("html", require("ejs").renderFile);
app.set("view engine", "html");
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
employees.js
const express = require("express");
const router = express.Router();
const config = require("config");
const { check, validationResult } = require("express-validator");
const Employee = require("../models/EmployeeModel");
const employeesData = require("../config/db");
// router.get("/", function (req, res) {
// res.send({ employeesData });
// });
//#route GET api/employees
//#desc Get all users Employees
//#access Private
router.get("/", async (req, res) => {
try {
const employees = await employeesData;
res.json({ employees });
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
});
//#route POST api/employees
//#desc Add new Employee
//#access Private
router.post(
"/",
[
check("name", "Name is required").not().isEmpty(),
check("surname", "Surname is required").not().isEmpty(),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, surname, adress, phone, email, birthdate } = req.body;
try {
const newEmployee = new Employee({
name,
surname,
adress,
phone,
email,
birthdate,
});
const employee = await newEmployee.save();
res.json(employee);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
//#route DELETE api/employee
//#desc Delete Employee
router.delete("/:id", async (req, res) => {
try {
let employee = await Employee.findById(req.params.id);
if (!employee) return res.status(404).json({ msg: "Employee not found" });
await Employee.findByIdAndRemove(req.params.id);
res.json({ msg: `Employee removed` });
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
module.exports = router;
FRONTEND
EmployeesState.js
import React, { useReducer, useState } from "react";
import axios from "axios";
import { v4 as uuid } from "uuid";
import EmployeesContext from "./employeesContext";
import employeesReducer from "./employeesReducer";
import {
ADD_EMPLOYEE,
DELETE_EMPLOYEE,
SET_CURRENT,
CLEAR_CURRENT,
UPDATE_EMPLOYEE,
FILTER_EMPLOYEES,
EMPLOYEE_ERROR,
CLEAR_FILTER,
GET_EMPLOYEES,
CLEAR_EMPLOYEES,
} from "../types";
const EmployeesState = (props) => {
const initialState = {
employees: [],
current: null,
filtered: null,
error: null,
loading: false,
};
const [state, dispatch] = useReducer(employeesReducer, initialState);
const [employees, setEmployees] = useState(initialState);
//Get Employees
// // gives me promise{<prending>} on console
const getEmployees = async () => {
try {
const res = axios.get("http://localhost:5000/employees");
dispatch({ type: GET_EMPLOYEES, payload: res });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
});
}
};
//trying with fetch. throwing me undefined on console
const callAPI = () => {
fetch("http://localhost:5000/employees")
.then((res) => res.json())
.then((res) =>
setEmployees({
...initialState,
loading: false,
employees: res,
})
);
};
//Add Employee
const addEmployee = async (employee) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.post("/employees", employee, config);
dispatch({ type: ADD_EMPLOYEE, payload: res.data });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.data.msg,
});
}
};
//Delete Employee
const deleteEmployee = async (id) => {
try {
await axios.delete(`/employees/${id}`);
dispatch({ type: DELETE_EMPLOYEE, payload: id });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.msg,
});
}
};
//Update Employee
const updateEmployee = async (employee) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.put(
`/api/employees/${employee._id}`,
employee,
config
);
dispatch({ type: UPDATE_EMPLOYEE, payload: res.data });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.msg,
});
}
dispatch({ type: UPDATE_EMPLOYEE, payload: employee });
};
//Clear Employees
const clearEmployees = () => {
dispatch({ type: CLEAR_EMPLOYEES });
};
//Set Current Employees
const setCurrent = (employee) => {
dispatch({ type: SET_CURRENT, payload: employee });
};
//Clear Current Employee
const clearCurrent = () => {
dispatch({ type: CLEAR_CURRENT });
};
//Filter Employees
const filterEmployees = (text) => {
dispatch({ type: FILTER_EMPLOYEES, payload: text });
};
//Clear Filter
const clearFilter = (text) => {
dispatch({ type: CLEAR_FILTER });
};
return (
<EmployeesContext.Provider
value={{
employees: state.employees,
current: state.current,
filtered: state.filtered,
error: state.error,
loading: state.loading,
callAPI,
getEmployees,
addEmployee,
deleteEmployee,
clearEmployees,
setCurrent,
clearCurrent,
updateEmployee,
filterEmployees,
clearFilter,
}}
>
{props.children}
</EmployeesContext.Provider>
);
};
export default EmployeesState;
// // gives me undefined on console
// const getEmployees = () => {
// axios
// .get("http://localhost:5000/employees")
// .then((res) => dispatch({ type: GET_EMPLOYEES, payload: res }))
// .catch((err) => {
// dispatch({
// type: EMPLOYEE_ERROR,
// });
// });
// };
Home.js
import React, { Fragment } from "react";
import Search from "../employees/Search";
import Employees from "../employees/Employees";
const Home = () => {
return (
<>
<Search />
<Employees />
</>
);
};
export default Home;
Employees.js
import React, { useContext, useEffect } from "react";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import EmployeeItem from "./EmployeeItem";
import { v4 as uuidv4 } from "uuid";
import EmployeesContext from "../../contexts/employees/employeesContext";
import Spinner from "../layout/Spinner";
const Employees = () => {
const employeesContext = useContext(EmployeesContext);
const {
employees,
filtered,
getEmployees,
callAPI,
loading,
} = employeesContext;
useEffect(() => {
// callAPI();
getEmployees();
// eslint-disable-next-line
console.log(employees);
console.log(getEmployees());
console.log(callAPI());
}, []);
return (
<div>
<>
{[employees].map((employee) => (
<EmployeeItem key={uuidv4()} employee={employee} />
))}
</>
</div>
);
employeesReducer.js
import {
GET_EMPLOYEES,
ADD_EMPLOYEE,
DELETE_EMPLOYEE,
SET_CURRENT,
CLEAR_CURRENT,
UPDATE_EMPLOYEE,
FILTER_EMPLOYEES,
CLEAR_FILTER,
EMPLOYEE_ERROR,
CLEAR_EMPLOYEES,
} from "../types";
export default (state, action) => {
switch (action.type) {
case GET_EMPLOYEES:
return {
...state,
employees: action.payload,
loading: false,
};
case ADD_EMPLOYEE:
return {
...state,
employee: [action.payload, ...state.employees],
loading: false,
};
case UPDATE_EMPLOYEE:
return {
...state,
employees: state.employees.map((employee) =>
employee._id === action.payload._id ? action.payload : employee
),
loading: false,
};
case DELETE_EMPLOYEE:
return {
...state,
employees: state.employees.filter(
(employee) => employee._id !== action.payload
),
loading: false,
};
case CLEAR_EMPLOYEES:
return {
...state,
employees: null,
filtered: null,
error: null,
current: null,
};
case SET_CURRENT:
return {
...state,
current: action.payload,
};
case CLEAR_CURRENT:
return {
...state,
current: null,
};
case FILTER_EMPLOYEES:
return {
...state,
filtered: state.employees.filter((employee) => {
const regex = new RegExp(`${action.payload}`, "gi");
return employee.name.match(regex) || employee.email.match(regex);
}),
};
case CLEAR_FILTER:
return {
...state,
filtered: null,
};
case EMPLOYEE_ERROR:
return {
...state,
error: action.payload,
};
default:
return state;
}
};
You need to await for the response
const res = await axios.get("http://localhost:5000/employees");
dispatch({ type: GET_EMPLOYEES, payload: res });
or
axios.get("http://localhost:5000/employees")
.then(res => {
dispatch({ type: GET_EMPLOYEES, payload: res });
})
I have 2 Firebase functions that I want to execute when there is an Http request, one function (createEmailList) to save data in the Firebase database, the other (zohoCrmHook) to to save in a 3rd party CRM called Zoho.
When the functions are deployed to Firebase, the functions log shows that both are properly deployed. However, when the Http request is made from the frontend, the log shows that only one of the functions (createEmailList) is being executed.
As the log shows, the first function createEmailList is being executed and the data shows up in the Firebase database with no problem. However, The second function zohoCrmHook is not even being executed.
index.js
const functions = require('firebase-functions');
const admin = require("firebase-admin")
const serviceAccount = require("./service_account.json");
const createEmailList = require('./createEmailList')
// zoho
const zohoCrmHook = require('./zohoCrmHook')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://landing-page.firebaseio.com"
})
exports.zohoCrmHook = functions.https.onRequest(zohoCrmHook)
exports.createEmailList = functions.https.onRequest(createEmailList)
createEmailList.js
const admin = require('firebase-admin')
const cors = require('cors')({ origin: true })
module.exports = (req, res) => {
cors(req, res, () => {
if (!req.body.email) {
return res.status(422).send({ error: 'Bad Input'})
}
const email = String(req.body.email)
const firstName = String(req.body.firstName)
const lastName = String(req.body.lastName)
const data = {
email,
firstName,
lastName
}
const db = admin.firestore()
const docRef = db.collection('users')
.doc(email)
.set(data, { merge: false })
.catch(err => res.status(422).send({ error: err }))
res.status(204).end();
})
}
zohoCrmHook.js
const axios = require('axios');
const functions = require('firebase-functions');
// zoho
const clientId = functions.config().zoho.client_id;
const clientSecret = functions.config().zoho.client_secret;
const refreshToken = functions.config().zoho.refresh_token;
const baseURL = 'https://accounts.zoho.com';
module.exports = async (req, res) => {
const newLead = {
'data': [
{
'Email': req.body.email,
'Last_Name': req.body.lastName,
'First_Name': req.body.firstName,
}
],
'trigger': [
'approval',
'workflow',
'blueprint'
]
};
const { data } = await getAccessToken();
const accessToken = data.access_token;
const leads = await getLeads(accessToken);
const result = checkLeads(leads.data.data, newLead.data[0].Email);
if (result.length < 1) {
try {
return res.json(await createLead(accessToken, newLead));
}
catch (e) {
console.log(e);
}
}
else res.json({ message: 'Lead already in CRM' })
}
function getAccessToken () {
const url = `https://accounts.zoho.com/oauth/v2/token?refresh_token=${refreshToken}&client_id=${clientId}&client_secret=${clientSecret}&grant_type=refresh_token`;
return new Promise((resolve, reject) => {
axios.post(url)
.then((response) => {
return resolve(response);
})
.catch(e => console.log(e))
});
}
function getLeads(token) {
const url = 'https://www.zohoapis.com/crm/v2/Leads';
return new Promise((resolve, reject) => {
axios.get(url, {
headers: {
'Authorization': `Zoho-oauthtoken ${token}`
}
})
.then((response) => {
return resolve(response);
})
.catch(e => console.log(e))
})
}
function createLead(token, lead) {
const url = 'https://www.zohoapis.com/crm/v2/Leads';
return new Promise((resolve, reject) => {
const data = JSON.stringify(lead);
axios.post(url, data, {
headers: {
'Authorization': `Zoho-oauthtoken ${token}`
}
})
.then((response) => {
console.log(response.data)
return resolve(response);
})
.catch(e => reject(e))
})
}
function checkLeads(leads, currentLead) {
return leads.filter(lead => lead.Email === currentLead)
}
Since you're exporting two functions.https.onRequest declarations, you'll end up with two Cloud Functions, each with their own URL/endpoint. So if that's what you need, you'll need to configure two web hooks that call these functions.
From reading your question however, it sounds more like you want a single Cloud Function that does two things, in which case you should only have one functions.https.onRequest declaration that then calls two regular JavaScript functions (for example).
So something more like:
exports.myWebHook = functions.https.onRequest(function(req, res) {
zohoCrmHook(...);
createEmailList(...);
})
You'll need to figure out what to pass into the two function calls here, as you can't pass the request and response along.
Alternatively you can call the two Cloud Functions from here, but that typically just drives up your cost with little benefit.