I made an api app in Node Express. Routes post and put are working perfectly. Delete route is also working but I dont see confirmation of resolved promise, instead after few secs I see those errors
Access to XMLHttpRequest at 'https://ahi' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
PageAdmin.js:179 fail Error: Network Error
DELETE https://ahi net::ERR_FAILED
I was already dealing with that problem at beginning of setting up app on server so I searched how to deal with it. Here is my middleware code on server for setting up headers
function setHeaders(app) {
app.use(function (req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, OPTIONS, PUT, PATCH, DELETE"
);
res.setHeader(
"Access-Control-Allow-Headers",
"Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Origin, Access-Control-Allow-Methods, x-auth-token"
);
res.setHeader("Access-Control-Allow-Credentials", true);
// handle OPTIONS method
if ("OPTIONS" == req.method) {
return res.sendStatus(200);
} else {
next();
}
});
}
Here is the code for routes
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const auth = require("../middleware/auth");
const { ProgrammingTool, validate } = require("../models/programmingTool");
const msg400 = "Bad request";
const msg400InData = "Item already exists in database";
const msg404 = "Could not find item in database";
const msg500 = "Something went wrong";
router.get("/", async (req, res) => {
try {
const tools = await ProgrammingTool.find();
res.send(tools);
} catch (e) {
console.log("failed getting tools", e);
res.status(500).send(msg500);
}
});
router.get("/:id", async (req, res) => {
if (!mongoose.Types.ObjectId.isValid(req.params.id))
return res.status(404).send(msg404);
const tool = await ProgrammingTool.findById(req.params.id);
if (!tool) return res.status(404).send(msg404);
res.send(tool);
});
router.post("/", auth, async (req, res) => {
const validation = validate(req.body);
if (validation.error) return res.status(400).send(msg400);
try {
const toolInData = await ProgrammingTool.findOne({ name: req.body.name });
if (toolInData) return res.status(400).send(msg400InData);
} catch (e) {
return res.status(500).send(msg500);
}
const tool = new ProgrammingTool({ name: req.body.name });
try {
await tool.validate();
} catch (e) {
console.log("did not pass mongoose validation at posting", e);
return res.status(400).send(msg400);
}
try {
const result = await tool.save();
res.send(result);
} catch (e) {
console.log("failed to post tool");
res.status(500).send(msg500);
}
});
router.delete("/:id", auth, async (req, res) => {
const id = req.params.id;
let tool;
try {
tool = await ProgrammingTool.findById(id);
} catch (e) {
console.log("could not find the tool with provided id", e);
res.status(404).send(msg404);
}
try {
await tool.delete();
} catch (e) {
console.log("failed deleting tool", e);
res.status(500).send(msg500);
}
});
router.put("/:id", auth, async (req, res) => {
const validation = validate(req.body);
if (validation.error) return res.status(400).send(msg400);
const id = req.params.id;
const tool = await ProgrammingTool.findById(id);
if (!tool) res.status(404).send(msg404);
tool.name = req.body.name;
try {
await tool.validate();
} catch (e) {
console.log("did not pass mongoose validation at putting", e);
return res.status(400).send(msg400);
}
try {
const result = await tool.save();
res.send(result);
} catch (e) {
console.log("failed to save edited tool");
res.status(500).send(msg500);
}
});
router.delete("/", auth, (req, res) => {
res.status(400).send(msg400);
});
router.put("/", auth, (req, res) => {
res.status(400).send(msg400);
});
What really suprises me is that the error is getting thrown but route does it job, item is deleted from database.
In the delete route there are 2 problems.
you are not using return so even after the first error it goes to the second try catch block. Add return to both res.status(..).send(..)
For a successful Delete operation you are not returning a 2xx status code. So if there is a successful deletion the request will be stuck cause you are not returning anything, after some time probably it will time out.
Related
I'm trying to follow the Stripe Checkout subscription instructions:
https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=checkout
When I submit the form, over localhost I get "Cannot POST /create-checkout-session" (404 in network tab), and if I run from my production server, it just opens /create-checkout-session as a new (blank) Vue page. I have tried changing the form action to https://mydomain.xyz/create-checkout-session, but it didn't work.
Subscribe.vue
<form action="/create-checkout-session" method="POST">
<input type="hidden" name="priceId" value="[removed for Stack Overflow]" />
<button type="submit">Checkout</button>
</form>
I don't know if something is interfering with the endpoint, so I've copied my entire app.js file:
app.js
let express = require('express'),
cors = require('cors'),
mongoose = require('mongoose'),
database = require('./database'),
bodyParser = require('body-parser');
// Set your secret key. Remember to switch to your live secret key in production.
// See your keys here: https://dashboard.stripe.com/apikeys
const stripe = require('stripe')('[removed for StackOverflow]');
//connect mongoDB
mongoose.Promise = global.Promise;
mongoose.connect(database.db, {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log("Database connected")
},
error => {
console.log("Database couldn't be connected to: " + error);
}
)
const cryptoEndPoint = require('../backend/routes/crypto.route')
const app = express();
app.use(decodeIDToken);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cors())
//API
app.use('/api', cryptoEndPoint)
app.post("/create-checkout-session", async (req, res) => {
console.log("can't even get anything to console log, only get createError is not defined");
const { priceId } = req.body;
// Create new Checkout Session for the order
// Other optional params include:
// [billing_address_collection] - to display billing address details on the page
// [customer] - if you have an existing Stripe Customer ID
// [customer_email] - lets you prefill the email input in the form
// [automatic_tax] - to automatically calculate sales tax, VAT and GST in the checkout page
// For full details see https://stripe.com/docs/api/checkout/sessions/create
try {
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [
{
price: priceId,
// For metered billing, do not pass quantity
quantity: 1,
},
],
// {CHECKOUT_SESSION_ID} is a string literal; do not change it!
// the actual Session ID is returned in the query parameter when your customer
// is redirected to the success page.
success_url: 'https://mydomain.xyz/thankyou?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://mydomain.xyz/thankyou',
});
return res.redirect(303, session.url);
} catch (e) {
res.status(400);
return res.send({
error: {
message: e.message,
}
});
}
});
//create port
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
console.log('Connected to port ' + port);
})
//Find 404
app.use((req, res, next) => {
next(createError(404));
})
//error handler
app.use(function (err, req, res, next) {
console.log(err.message);
if (!err.statusCode) err.statusCode = 500;
res.status(err.statusCode).send(err.message);
})
/**
* Decodes the JSON Web Token sent via the frontend app
* Makes the currentUser (firebase) data available on the body.
*/
async function decodeIDToken(req, res, next) {
if (req.headers?.authorization?.startsWith('Bearer ')) {
const idToken = req.headers.authorization.split('Bearer ')[1];
// console.log(idToken);
try {
const decodedToken = await admin.auth().verifyIdToken(idToken);
// console.log("one");
req['currentUser'] = decodedToken;
// console.log("two");
} catch (err) {
console.log("error: " + err);
}
}
next();
}
app.post("/webhook", async (req, res) => {
let data;
let eventType;
// Check if webhook signing is configured.
const webhookSecret = '[removed for Stack Overflow]'
if (webhookSecret) {
// Retrieve the event by verifying the signature using the raw body and secret.
let event;
let signature = req.headers["stripe-signature"];
try {
event = stripe.webhooks.constructEvent(
req.body,
signature,
webhookSecret
);
} catch (err) {
console.log(`⚠️ Webhook signature verification failed.`);
return res.sendStatus(400);
}
// Extract the object from the event.
data = event.data;
eventType = event.type;
} else {
// Webhook signing is recommended, but if the secret is not configured in `config.js`,
// retrieve the event data directly from the request body.
data = req.body.data;
eventType = req.body.type;
}
switch (eventType) {
case 'checkout.session.completed':
//...
break;
case 'invoice.paid':
//...
break;
case 'invoice.payment_failed':
//...
break;
default:
// Unhandled event type
}
res.sendStatus(200);
});
Does anyone know what is going wrong?
I added this to my app.js file, and it's working:
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
app.use(allowCrossDomain);
app.use(
express.json({
verify: (req, res, buf) => {
req.rawBody = buf.toString();
},
}),
);
app.post('/webhook', async (req, res, next) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.rawBody, sig, endpoint_secret);
} catch (err) {
return console.log(err)
}
if (event.type === 'invoice.payment_succeeded') {
//...
}
res.send();
});
I tried following this link but I kept getting express.raw is not a function error, I also tried this:
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()(req, res, next);
}
});
And still got the same error, would really appreciate it if I could get some help.
I'm using Firebase Cloud Functions as my webhook API, as so:
import webhook_app_creator from "express";
import cors from "cors";
// This example uses Express to receive webhooks
//the Cloud Function calls the webhook_app
export const webhook_app = webhook_app_creator();
// The Firebase Admin SDK to access Cloud Firestore.
//const cors = require("cors");
// Automatically allow cross-origin requests
webhook_app.use(cors({ origin: true }));
// build multiple CRUD interfaces:
webhook_app.post("/direct", async (request, response) => {
//send the response early - the only valid response is "received"
await commonHandler(request, response, endpointDirectSecret);
response.json({ received: true });
});
webhook_app.post("/connect", async (request, response) => {
//send the response early - the only valid response is "received"
await commonHandler(request, response, endpointSecret);
response.json({ received: true });
});
const commonHandler = async (request, response, secret) => {
const sig = request.headers["stripe-signature"];
try {
request.fullEvent = stripe.webhooks.constructEvent(
request.rawBody,
sig,
secret
);
} catch (err) {
logger(`Webhook Error: ${err.message}`);
return;
}
return queue_event(request.fullEvent);
};
The Cloud Function couldn't be simpler:
//import functions from "firebase-functions";
import { functions, webhook_app } from "../../../../services";
// Expose Express API as a single Cloud Function:
export default functions.https.onRequest(webhook_app);
This is my code for getting user info
router.post("/getuser", fetchuser, async (req, res) => {
try {
userId = req.user.id;
const user = await User.findById(userId).select("-password");
res.send(user);
} catch (error) {
console.error(error.message);
res.status(500).send("Internal Server Error");
}
});
and this is the code for middleware fetchuser
const fetchuser = async (req, res, next) => {
const token = req.header('auth-token');
if (!token) {
res.status(401).send({ error: "Please authenticate using a valid token" })
}
try {
const data = jwt.verify(token, process.env.SECRET);
console.log(data);
req.user = data.user;
next();
} catch (error) {
res.status(401).send({ error: "Please authenticate using a valid token" })
}
};
I am getting the user id in console.log but when I try to get the user id in the router.post then I am not able to get the user Info.
Here is the result I am getting.
Server is running on port 5000
Connected to database
{ id: '61e98c45a9d8818292b38505', iat: 1642743501 }
Cannot read properties of undefined (reading 'id')
Please can anyone tell me what is wrong with this?
router.post("/getuser", fetchuser, async (req, res) => {
try {
const params = JSON.parse(req)
userId = params.id;
const user = await User.findById(userId).select("-password");
res.send(user);
} catch (error) {
console.error(error.message);
res.status(500).send("Internal Server Error");
}
});
Your code is working fine , you just have to change this line on fetchuser middleware
req.user = data.user
to
req.user = data
and your code work as expected..
add file: src/types/express/index.d.ts
content:
declare namespace Express {
interface Request {
user: {
id: string;
};
}
}
Sorry if the title if confusing, I wasn't too sure how to word it. I have a PATCH request to update a value in my database, but even though it is "working" (200 status), it's not actually.
I have a .route('/:movie_id/:user_id').all() handler to trigger for all my methods, where it pulls a movie from the database by movie_id and user_id. This works. Then I move on to my PATCH request, but it seems like the PATCH request isn't actually running. I am getting the correct response from the .all() handler, but no update is happening. Even if I completely comment out the code for my PATCH, I am still getting a 200 status.
Here is my .all() handler with my PATCH request:
movieRouter
.route('/:movie_id/:user_id')
.all(requireAuth)
.get((req, res, next) => {
const db = req.app.get('db')
MovieService.getById(db, req.params.movie_id, req.params.user_id)
.then(movie => {
if(!movie) { // this runs fine
return res.status(404).json({ error: `Movie doesn't exist`})
}
// res.json({movie : movie}); --> old code
// solution:
res.movie = movie;
next();
return movie;
})
.catch(next)
})
.patch(requireAuth, (req, res, next) => {
const db = req.app.get('db')
const { watched } = req.body
const updatedMovie = { watched }
// this doesn't run
const numVal = Object.values(updatedMovie).filter(Boolean).length
if(numVal === 0) {
return res.status(400).json({ error: `Must not be blank`})
}
MovieService.updateMovie(db, req.params.movie_id, req.params.user_id, updatedMovie)
.then(movie => {
res.status(200).json(updatedMovie)
})
.catch(next)
})
Here is my MovieService:
updateMovie(db, movie_id, newMovie) {
return db('your_movie_list').where('id', movie_id).where('user_id', user_id).update(newMovie).returning('*')
}
It should be the problem of the 2nd .all(), .all() will catch all request, no matter it is GET, POST, PATCH, DELETE. So even when you comment out PATCH code, it will return 200.
Change the 2nd .all to .get like below
app.use(express.json())
movieRouter
.route('/:movie_id/:user_id')
.all(requireAuth)
.get((req, res, next) => { // use .get instead of .all to avoid catching all requests
const db = req.app.get('db')
MovieService.getById(db, req.params.movie_id, req.params.user_id)
.then(movie => {
if(!movie) { // this runs fine
return res.status(404).json({ error: `Movie doesn't exist`})
}
res.json({movie : movie});
})
.catch((e) => {
console.log("From getMovie", e);
res.status(400).json({ error: e.message })
})
})
.patch((req, res, next) => {
try {
const db = req.app.get('db')
const { watched } = req.body
const updatedMovie = { watched }
// this doesn't run
const numVal = Object.values(updatedMovie).filter(Boolean).length
if(numVal === 0) {
return res.status(400).json({ error: `Must not be blank`})
}
MovieService.updateMovie(db, req.params.movie_id, req.params.user_id, updatedMovie)
.then(movie => {
console.log(movie) // nothing logs
res.status(200).json(movie[0])
})
.catch((e) => {
console.log("From updateMovie", e);
res.status(400).json({ error: e.message })
})
}catch(e) {
console.log("From try/catch", e);
res.status(400).json({ error: e.message })
}
})
A little working example for cross-checking
const express = require("express");
const app = express();
const PORT = process.env.PORT || 8080;
app.use(express.json())
const movieRouter = express.Router()
movieRouter
.route('/:movie_id/:user_id')
// .all(requireAuth)
.get((req, res, next) => {
res.json({"movie:get" : 1});
})
.patch((req, res, next) => {
res.json({"movie:patch" : 1});
})
app.use(movieRouter)
app.listen(PORT, function (err) {
if (err) console.log(err);
console.log("Server listening on PORT", PORT);
});
I'm trying to make a page that only registered users can access to that page and I'm checking it by middleware at the server side, but when im trying to access the page I get 401 error because the session.user is undefined, while when im trying my code through "postman" it works great without any issues, here is the middleware:
function isLoggedIn(request, response, next) {
if (!request.session.user) {
response.status(401).send("You are not logged in!");
return;
}
next();
}
module.exports = isLoggedIn;
And here is the controller:
router.get("/", isLoggedIn,async (request, response) => {
try {
const vacations = await vacationsLogic.getAllVacationsAsync();
response.json(vacations);
}
catch (err) {
response.status(500).send(err.message);
}
});
and here is the login router:
router.post("/login", async (request, response) => {
try {
const credentials = request.body;
const user = await authLogic.login(credentials);
if (!user) {
response.status(401).send("Illegal username or password");
return
}
request.session.user = user;
response.json(user);
}
catch (err) {
response.status(500).send(err.message);
}
})
and here is the register router:
router.post("/register", async (request, response) => {
try {
const user = new User(0, request.body.firstName, request.body.lastName, request.body.username, request.body.password, 0);
// if username already exits -
// return some error(400) to the client.
const addedUser = await authLogic.register(user);
// Save that user in the session:
request.session.user = addedUser;
response.status(201).json(addedUser);
}
catch (err) {
response.status(500).send(err.message);
}
})