Problem setting up node js server to listen for webhook and post to database - rest

Good morning everyone, I'm having a bit of a struggle setting up a server to listen for webhook data and post it to a database. I'm mostly front-end, so some of this is a bit new for me. So I have a deli website that i built on snipcart. I have a receipt printer that queries an api and prints out new orders. So what I'm wanting is a server to listen for the webhook and store the info in a database. I've got it where it listens for the webhook correctly, but it refuses to post to the database. Here's the code in the app.js file.
'use strict';
require('./config/db');
const express = require('express');
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
const app = express();
var routes = require('./api/routes/apiRoutes');
routes(app);
let orderToken;
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
app.listen(process.env.PORT || 8080);
app.post('/hook', (req, res) => {
orderToken = req.body.content.token;
console.log(orderToken);
const secret = "snipcart api key";
const apiFetch = async function(){
};
let buffered = new Buffer.from(secret);
let base64data = buffered.toString('base64');
const start = async function(){
const request = await fetch('https://app.snipcart.com/api/orders/'+orderToken, {
headers: {
'Authorization': `Basic ${base64data}`,
'Accept': 'application/json'
}
});
const result = await request.json();
console.log(result);
};
start();
res.status(200).end();
});
app.get('/', (req, res) => {
res.send('hello world')
});
Here's the code in my apiController.js file
const mongoose = require('mongoose'),
Order = mongoose.model('apiModel');
// listAllOrders function - To list all orders
exports.listAllOrders = (req, res) => {
api.find({}, (err, api) => {
if (err) {
res.status(500).send(err);
}
res.status(200).json(api);
});
};
// createNewOrder function - To create new Order
exports.createNewOrder = (req, res) => {
let newApi = new api (req.body);
newApi.save((err, api) => {
if (err) {
res.status(500).send(err);
}
res.status(201).json(api);
});
};
// deleteOrder function - To delete order by id
exports.deleteOrder = async ( req, res) => {
await api.deleteOne({ _id:req.params.id }, (err) => {
if (err) {
return res.status(404).send(err);
}
res.status(200).json({ message:"Order successfully deleted"});
});
};
and my apiModel.js file
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ApiSchema = new Schema({
customerName: {
type:String,
required:true
},
customerPhone: {
type:String,
required:true
},
name: {
type:String,
required:true
},
orderNumber: {
type:String,
required:true
},
price: {
type:String,
required:true
},
customFields: {
type:Array,
required:false
},
});
module.exports = mongoose.model("apiModel", ApiSchema);
apiRoutes.js
module.exports = function(app){
var orderList = require('../controllers/apiController');
app
.route('/orders')
.get(orderList.listAllOrders)
.post(orderList.createNewOrder);
app
.route('/order/:id')
.delete(orderList.deleteOrder);
};
and my db.js
const mongoose = require("mongoose");
//Assign MongoDB connection string to Uri and declare options settings
var uri = "<mongodb atlas info>
retryWrites=true&w=majority";
// Declare a variable named option and assign optional settings
const options = {
useNewUrlParser: true,
useUnifiedTopology: true
};
// Connect MongoDB Atlas using mongoose connect method
mongoose.connect(uri, options).then(() => {
console.log("Database connection established!");
},
err => {
{
console.log("Error connecting Database instance due to:", err);
}
});
and here's a sample response that I need to place into the database
{
"token": "93c4604e-35ac-4db7-b3f1-2871476e9e6a",
"creationDate": "2013-10-22T20:54:40.377Z",
"modificationDate": "2013-10-22T20:55:45.617Z",
"status": "Processed",
"paymentMethod": "CreditCard",
"invoiceNumber": "SNIP-1427",
"email": "geeks#snipcart.com",
"cardHolderName": "Geeks Snipcart",
"creditCardLast4Digits": "4242",
"billingAddressName": "Geeks Snipcart",
"billingAddressCompanyName": "Snipcart",
"billingAddressAddress1": "4885 1ere Avenue",
"billingAddressAddress2": null,
"billingAddressCity": "Quebec",
"billingAddressCountry": "CA",
"billingAddressProvince": "QC",
"billingAddressPostalCode": "G1H2T5",
"billingAddressPhone": "1-877-301-4813",
"notes": null,
"shippingAddressName": "Geeks Snipcart",
"shippingAddressCompanyName": "Snipcart",
"shippingAddressAddress1": "4885 1ere Avenue",
"shippingAddressAddress2": null,
"shippingAddressCity": "Quebec",
"shippingAddressCountry": "CA",
"shippingAddressProvince": "QC",
"shippingAddressPostalCode": "G1H2T5",
"shippingAddressPhone": "1-877-301-4813",
"shippingAddressSameAsBilling": true,
"finalGrandTotal": 287.44,
"shippingFees": 10,
"shippingMethod": "Shipping",
"items": [
{
"uniqueId": "1aad3398-1260-419c-9af4-d18e6fe75fbf",
"id": "1",
"name": "Un poster",
"price": 300,
"quantity": 1,
"url": "http://snipcart.com",
"weight": 10,
"description": "Bacon",
"image": "",
"customFieldsJson": "[]",
"stackable": true,
"maxQuantity": null,
"totalPrice": 300,
"totalWeight": 10
},
...
],
"taxes": [
{
"taxName": "TPS",
"taxRate": 0.05,
"amount": 12.5,
"numberForInvoice": ""
},
{
"taxName": "TVQ",
"taxRate": 0.09975,
"amount": 24.94,
"numberForInvoice": ""
},
...
],
"rebateAmount": 0,
"subtotal": 310,
"itemsTotal": 300,
"grandTotal": 347.44,
"totalWeight": 10,
"hasPromocode": true,
"totalRebateRate": 20,
"promocodes": [
{
"code": "PROMO",
"name": "PROMO",
"type": "Rate",
"rate": 20,
},
...
],
"willBePaidLater": false,
"customFields": [
{
"name":"Slug",
"value": "An order"
},
...
],
"paymentTransactionId": null,
}
I dont need all the info placed in the database, just a few key items, like customer name, phone number and the order info. but if there's more than one item in the order, I need it to take that into account and add all the items in the order. here is the docs for the printer that i'm needing to integrate https://star-m.jp/products/s_print/CloudPRNTSDK/Documentation/en/index.html Would appreciate any help that you all can give me. Thanks!

Snipcart will send the webhook to you endpoint for different events. I would suggest you to first filter the event by eventName, because you want to listen for only the order.completed event. After that from the body of the request message, you can extract the items that will be in the req.body.content.items. You can take from the available info what you want and store only that in the database.
Try this:
app.post('/hook', (req, res) => {
if (req.body.eventName === 'order.completed') {
const customer_name = req.body.content.cardHolderName;
const customer_phone req.body.content.billingAddressPhone;
const order_number = req.body.content.invoiceNumber;
let items = [];
req.body.content.items.forEach((item) => {
items.push({
name: item.name,
price: item.price,
quantity: item.quantity,
id: item.uniqueId
});
})
// Now store in database
apiFetch.create({
customerName: customer_name,
customerPhone: customer_phone
name: customer_name,
orderNumber: order_number
customFields: items
}).then(()=>{
res.status(200).json({success:true});
}, (error)=>{
console.log('ERROR: ', error);
})
}
};

Related

API call fails when i put required:true model schema

I am trying to make an api endpoint and this is my model schema MongoDB
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
},
password: {
type: String,
},
profilePic: {
type: String,
default: "",
},
},
{ timestamps: true }
);
module.exports = mongoose.model("User", UserSchema);
API logic
const router = require("express").Router();
const User = require("../models/User");
//REGISTER
router.post("/register", async (req, res) => {
try {
const newUser = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password,
});
const user = await newUser.save();
res.status(200).json(user);
} catch (err) {
res.status(500).json(err);
}
});
module.exports = router;
index.js
const express = require("express");
const app = express();
const dotenv = require("dotenv");
const mongoose = require("mongoose");
const authRoute = require("./routes/auth");
const cors = require("cors");
const bodyParser = require("body-parser");
app.use(
bodyParser.urlencoded({
extended: false,
})
);
app.use(bodyParser.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(express.json());
dotenv.config();
mongoose
.connect(process.env.MONGO_URL, {
useUnifiedTopology: true,
useNewUrlParser: true,
})
.then(() => console.log("DB Connected!!!"))
.catch((err) => {
console.log("did not work", err);
});
app.use("/api/auth", authRoute);
app.use("/", (req, res) => {
console.log("main url");
});
app.listen("5002", () => {
console.log("server running");
});
When I am trying to POST call to localhost:5002/api/auth/register , with the following body
{
"username": "testname",
"email": "test#gmail.com",
"password": "122",
}
I get the following error
{
"errors": {
"username": {
"name": "ValidatorError",
"message": "Path `username` is required.",
"properties": {
"message": "Path `username` is required.",
"type": "required",
"path": "username"
},
"kind": "required",
"path": "username"
}
},
"_message": "User validation failed",
"name": "ValidationError",
"message": "User validation failed: username: Path `username` is required."
}
When I remove required:true this api call works but i cant see the fields for username, email, password in collection of the database.
What am I doing wrong here?

why mongoose populate() request does not work?

I try to populate some data from other collection to an other collection.i had googled the search and also i follow the tutorial step by step but the population had fail.any help is appreciate friends. this is the code:
router.get("/", passport.authenticate("jwt", {session: false}), (req, res)=> {
const errors = {};
Profile.findOne({user: req.user.id})
.then(profile => {
if (!profile) {
errors.noprofile = "there is no profile for this user"
return res.status(404).json(errors);
}
res.json(profile);
}).catch(err=> res.status(404).json(err))
});
// #route POST api/profile
//#desc Create or edit user profile
//#access Private
router.get("/", passport.authenticate("jwt", {session: false}), (req, res)=> {
const {errors, isValid} = validateProfileInput(req.body);
//Check validation
if(!isValid) {
return res.status(400).json(errors);
}
// Get profile data
const profileData = {};
profileData.user = req.user.id;
if(req.body.handle) {
profileData.handle = req.body.handle
};
if(req.body.company) {
profileData.company = req.body.company
};
if(req.body.website) {
profileData.website = req.body.website
};
if(req.body.location) {
profileData.location = req.body.location
};
if(req.body.status) {
profileData.status = req.body.status
};
if(typeof req.body.skills !== 'undefined') {
profileData.skills = req.body.skills.split(',');
}
//social
profileData.social = {};
if(req.body.youtube) {
profileData.social.youtube = req.body.youtube
};
if(req.body.twitter) {
profileData.social.twitter = req.body.twitter
};
if(req.body.facebook) {
profileData.social.facebook = req.body.facebook
};
if(req.body.instagram) {
profileData.social.instagram = req.body.instagram
};
Profile.findOne({user: req.user.id})
.populate(
"user",
["name, avatar"]
)
this is the result that I get from the postman :
"_id": "62ee1058ceb295ccdfedffce",
"user": "62e6825958870d3db69d2da5",
"handle": "pablo",
"status": "developper",
"skills": [
"design web"
],
and the correct result must be :
"_id": "62ee1058ceb295ccdfedffce",
"user": {"_id": "62e6825958870d3db69d2da5",
"name": "pablo",
"avatar": "//www.gravatar.com/avatar/1ffsrenbdgeajks-ghsdereys1dkkdhddbc"
}
"handle": "pablo",
"status": "developper",
"skills": [
"design web"
],

One to one with populate mongoose not working

I'm new to mongoose and mongodb.
I have two collection (cart and produk)
1 cart have 1 produk, and I get the cart and populate the product but is not show the data relations.
Here the code:
routing
router.route('/relations/:app_id')
.get(cartController.relation);
model (cartModel)
var mongoose = require('mongoose');
var cartSchema = mongoose.Schema({
app_id: {
type: String,
required: true
},
product_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Produk'
},
qty: Number
});
var collectionName = 'cart';
var Cart = module.exports = mongoose.model('Cart', cartSchema, collectionName);
module.exports.get = function (callback, limit) {
Cart.find(callback).limit(limit);
}
model (produkModel)
var mongoose = require('mongoose');
// Setup schema
var produkSchema = new Schema({
name: {
type: String,
required: true
},
stok: Number
});
// Export Cart model
var collectionName = 'produk';
var Produk = module.exports = mongoose.model('Produk', produkSchema, collectionName);
module.exports.get = function (callback, limit) {
Produk.find(callback).limit(limit);
}
controller (cartController)
Cart = require('../model/cartModel');
exports.relation = function (req, res) {
const showCart = async function() {
const carto = await Cart.find().select('app_id product_id qty').populate("produk");
return carto;
};
showCart()
.then(cs => {
return apiResponse.successResponseWithData(res, "Operation success", cs);
})
.catch(err => console.log(err));
};
// Result
{
"status": 1,
"message": "Operation success",
"data": [
{
"_id": "60af72022d57d542a41ffa5a",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"qty": 1,
"product_id": "60112f3a25e6ba2369424ea3"
},
{
"_id": "60b020536ccea245b410fb38",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"product_id": "603f5aff9437e12fe71e6d41",
"qty": 1
}
]
}
expecting result
{
"status": 1,
"message": "Operation success",
"data": [
{
"_id": "60af72022d57d542a41ffa5a",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"qty": 1,
"product_id": {
"_id": "60112f3a25e6ba2369424ea3",
"name": "snack"
}
},
{
"_id": "60b020536ccea245b410fb38",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"product_id": {
"_id": "603f5aff9437e12fe71e6d41",
"name": "snack"
}
"qty": 1
}
]
}
what I miss ???
Thanks for your help
You need to pass the path to populate or an object specifying parameters to .populate(). So in this case, Your code should be:
const carto = await Cart.find().select('app_id product_id qty').populate("product_id");

mongoose When I Use update it updates Nothing with status 200(success)

I use update Query for push some data in array in Mongodb and I use mongoose in nodeJs.Pplease anyone can help out from this.
Model Schema :
var mongoose = require('mongoose')
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt')
var schema = new Schema({
email: { type: String, require: true },
username: { type: String, require: true },
password: { type: String, require: true },
creation_dt: { type: String, require: true },
tasks : []
});
module.exports = mongoose.model('User',schema)
So I use this schema and I want to push data in tasks array and here is my route code for pushing data.
Route For Update Data in Tasks:
router.post("/newTask", isValidUser, (req, res) => {
addToDataBase(req, res);
});
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime,
};
var usr = new User(req.user);
usr.update({ email: req.user.email }, { $push: { tasks: dataa } });
console.log(req.user.email);
try {
doc = await usr.save();
return res.status(201).json(doc);
} catch (err) {
return res.status(501).json(err);
}
}
Here I create a async function and call that function in route but when I post data using postman it response with status code 200(success) but it updates nothing in my database.
Output screenshot:
as you can see in this image task : [].. it updates nothing in that array but status is success
I don't know why is this happening.
You can achieve this task easier using findOneAndUpdate method.
router.put("/users", isValidUser, async (req, res) => {
var data = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime,
};
try {
const user = await User.findOneAndUpdate(
{ email: req.user.email },
{
$push: {
tasks: data,
},
},
{ new: true }
);
if (!user) {
return res.status(404).send("User with email not found");
}
res.send(user);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
Also I strongly suggest using raw / JSON data for request body, that's how most ui libraries (reactjs, angular) send data.
To be able to parse json data, you need to add the following line to your main file before using routes.
app.use(express.json());
TEST
Existing user:
{
"tasks": [],
"_id": "5e8b349dc285884b64b6b167",
"email": "test#gmail.com",
"username": "Kirtan",
"password": "123213",
"creation_dt": "2020-04-06T14:21:40",
"__v": 0
}
Request body:
{
"pName": "pName 1",
"pTitle": "pTitle 1",
"pStartTime": "pStartTime 1",
"pEndTime": "pEndTime 1",
"pSessionTime": "pSessionTime 1"
}
Response:
{
"tasks": [
{
"pName": "pName 1",
"pTitle": "pTitle 1",
"pStartTime": "pStartTime 1",
"pEndTime": "pEndTime 1",
"pSessionTime": "pSessionTime 1"
}
],
"_id": "5e8b349dc285884b64b6b167",
"email": "test#gmail.com",
"username": "Kirtan",
"password": "123213",
"creation_dt": "2020-04-06T14:21:40",
"__v": 0
}
Also as a side note, you had better to create unique indexes on username and email fields. This can be done applying unique: true option in the schema, but better to create these unique indexes at mongodb shell like this:
db.users.createIndex( { "email": 1 }, { unique: true } );
db.users.createIndex( { "username": 1 }, { unique: true } );
It's been awhile since I've done mongoose, but I'm pretty sure <model>.update() also actively updates the record in Mongo.
You use .update() when you want to update an existing record in Mongo, but you are instantiating a new User model (i.e. creating a new user)
try the following code instead for a NEW USER:
router.post('/newTask', isValidUser, (req, res) => {
addToDataBase(req,res)
})
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime
}
// email field is already in `req.user`
var usr = new User({ ...req.user, tasks: [dataa] });
console.log(req.user.email);
try {
await usr.save();
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
Now, if you wanted to update an existing record :
router.post('/newTask', isValidUser, (req, res) => {
addToDataBase(req,res)
})
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime
}
try {
await usr. updateOne({ email : req.user.email}, { $push: { tasks: dataa } });
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
For more info read: https://mongoosejs.com/docs/documents.html

postman returns empty array

I sent below (raw/json) from postman to mongodb and I received filled img, text but coordinates was empty array "[]"
{
"coordinates": [],
"_id": "5b309b5671903722e073827e",
"date": "2018-06-25T07:35:50.932Z",
"__v": 0
},
Why is this is and how get not empty coordinates array?
Postman
{
"img": "test",
"text": "Meet",
"coordinates": [12, 23]
}
this is my schema
const mongoose = require('mongoose')
const memoSchema = new mongoose.Schema({
img: {
type: String
},
text: {
type: String
},
date: {
type: Date,
default: Date.now
},
coordinates: [Number]
})
module.exports = mongoose.model('Memo', memoSchema)
this is my router
api.post('/', async (req, res) => {
let newMemo = new Memo()
newMemo.img = req.body.img
newMemo.text = req.body.text
newMemo.coordinates = req.body.coordinates
await newMemo.save((err) => {
if(err) return res.status(500).json({ message: 'internal error' })
res.json({ message: 'saved...' })
})
})
I didn't add bodyparser
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())