Request body values not being picked up in firebase cloud function [duplicate] - javascript

When sending {"identifiant": "iJXB5E0PsoKq2XrU26q6"} to the below Cloud Function, I cannot get the identifiant value in the request body and it will always return PROBLEMAS NO REQUEST.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
exports.meusCandidatos = functions.https.onRequest((req, res) => {
const identifiant = req.body.identifiant;
if(identifiant) res.status(200).json('ok').end();
res.status(500).json('PROBLEMAS NO REQUEST').end();
});

Unlike a Callable function, the body of a request is not parsed automatically and needs to be parsed before you can use it.
In addition, json(...) will call end() internally so you don't need both. Also make sure that you don't call end(), send(), json(), etc. multiple times, as this will lead to errors.
const jsonParser = require('body-parser').json();
exports.meusCandidatos = functions.https.onRequest((req, res) => {
jsonParser(req, res, (err) => {
if (err) {
res.status(500).json({error: err.message});
return; // stop here
}
const identifiant = req.body.identifiant;
if (!identifiant) {
res.status(500).json({error: 'PROBLEMAS NO REQUEST'});
return; // stop here
}
// continue
res.status(200).json({ status: 'ok' });
})
});

Related

Problem while fetching a route from another route

I have a route to check if a user is logged in. It works well, but I don't understand what is the problem if I create a second route just below that calls it just to do the same thing. It seems like I can't access the cookie anymore in the second route, but I don't know why. Thanks for your help !
// This route works :
router.get('/loggedin', async (req, res) => {
try {
const token = req.cookies.jwt;
console.log("token : " + token) // Token is correct here in loggedin route, but is undefined if I use the route below
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
if (decodedToken) {
res.send(true);
}
else {
res.send(false);
}
}
catch (err) {
res.status(500).send(false);
}
});
// This route calls the route above and doesn't work
router.get('/loggedinbyanotherway', async (req, res) => {
const checking = await fetch(`${process.env.API_URL}:${process.env.PORT || 3000}/loggedin`)
console.log(checking.ok) // Returns false
const data = await checking.json()
console.log(data) // Returns false
res.send(data)
});
Your fetch request isn't providing any cookies, so how could the code handling the request read any cookies?
More to the point... This entire operation is unnecessary. Why make an HTTP request to the application you're already using? Instead, extract the functionality you want into a common function and just call that function from both routes. For example:
const isLoggedIn = (req) => {
const token = req.cookies.jwt;
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
if (decodedToken) {
return true;
} else {
return false;
}
};
router.get('/loggedin', async (req, res) => {
try {
res.send(isLoggedIn(req));
}
catch (err) {
res.status(500).send(false);
}
});
router.get('/loggedinbyanotherway', async (req, res) => {
const checking = isLoggedIn(req);
res.send(checking);
});
In the example it's not really clear why you need the second route or what else it offers, but I can only assume it's just a placeholder for some additional functionality you plan to add.
Either way, the point is that the application doesn't need to make an entire HTTP request to itself, since you're already in that application and have access to the same logic.

Google Cloud Function Cors Error Only When Error is Thrown

Dealing with a google cloud function that receives and responds as expected when there are no errors, however IF the function throws an error, on the client-side (Chrome) I receive a CORS error. I can't figure out if the issue is with how I am handling CORS or if it is because of a misuse of throw new function.https.httpsError.
Unsure if its related, the thrown error appears to be thrown AFTER the execution of the function finishes (based on logs).
I have set the function to be available to allUsers in the console.
I am using the cloud console to edit the function.
I did try using cors package
cloud function:
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');
const { getAuth } = require('firebase-admin/auth');
const functions = require('firebase-functions');
const app = initializeApp();
const db = getFirestore();
exports.registerUser = (req, res) => {
let registerDetails = req.body.data;
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
// Send response to OPTIONS requests
res.set('Access-Control-Allow-Methods', 'GET, POST')
res.set('Access-Control-Allow-Headers', 'Content-Type, Accept')
res.set('Access-Control-Max-Age', '3600')
return res.status(204).send('')
}
return getAuth().createUser({
email: registerDetails.Email,
emailVerified: false,
password: registerDetails.Password,
displayName: registerDetails.DisplayName,
disabled: false,
}).then((user)=>{
const message = 'Registered user ' + registerDetails.DisplayName + '.'
console.log('Successfully created new user:', user.uid)
return res.status(200).json({data: message })
//ALL OF THIS PART WORKS JUST FINE
}).catch((error)=>{
console.log('Error creating new user:', error.errorInfo.message)
throw new functions.https.HttpsError("already-exists", error.errorInfo.message)
//IF AN ERROR HAPPENS I SEE A CORS ERROR CLIENT SIDE
})
};
Client Side Code:
const regUser = fbf.httpsCallable('RegisterUser');
regUser({
FirstName: $('#registerFirstName').val(),
LastName: $('#registerLastName').val(),
DisplayName: $('#publicName').val(),
Email: $('#regEmail').val(),
Password: $('#regPassword').val()
}).then((result)=>{
$('#regInputHide').hide();
$('#regResponse').show();
$('#submitNewReg').hide();
$('#regFuncResponse').text(result.data)
console.log(result.data)
}).catch((err)=>{
console.warn(err)
//THIS WILL LOG "Error: internal
// # etc."
//IN ADDITION TO THE CORS ERROR
})
You are mixing the APIs for a HTTP Event Cloud Function with a Callable Cloud Function.
You need to use one or the other or at least add in the code to format the response from your function in a way that httpsCallable can parse.
// Exporting/using a `(req: Request, res: Response) => any` function is a
// HTTP Event Cloud Function pattern
exports.registerUser = (req, res) => {
/* ... */
// Returning a Promise chain is a Callable Cloud Function pattern
return getAuth().createUser({
/* ... */
// sending a response is a HTTP Event Cloud Function pattern
return res.status(200).json({data: message })
/* ... */
// throwing HttpsError instances is a Callable Cloud Function pattern
throw new functions.https.HttpsError("already-exists", error.errorInfo.message)
/* ... */
}

make it possible to register webhook to API

I'm fairly new to webhooks and need to make it possible to register a webhook to an API that I'm creating. I use schemas to create and store data. I want the webhook to emit an event whenever a new 'student' object is created from the schema. I want the user to able to send a webhook request to https://localhost:8080/api/webhook containing a hook-secret in the header and along with the URL, to which the emit event will be sent, in the body.
If I create an object from, say a 'subscriber'-schema for each webhook, how should I go about emitting the event to all webhooks?
I'm writing in Vanilla JS.
I'm using Express and Socket.io for the server.
MongoDB as database.
Can I use res.io.emit() to do it?
If so, how do I tell the function where to send the emit event?
This is how I currently register a subscriber/webhook:
export class WebHook {
authorize (req, res, next) {
if (req.headers['x-app-secret'] !== process.env.HOOK_SECRET) {
res.status(403).send('incorrect hook secret')
return
}
next()
}
async registerSubscriber (req, res, next) {
try {
// Creates a new subscriber object.
const subscriber = await Subscriber.insert(req.body, req.user).then(response => {
return response
})
res.status(200).end()
} catch (error) {
let err = error
err = createError(500)
err.innerException = error
next(error)
}
}
}
An this is the function for how a new student object is created, and where I want all the subscribers to be notified:
async create (req, res, next) {
try {
// Creates a new student object.
const student = await Student.insert(req.body).then(response => {
return response
})
// SOMETHING LIKE "res.io.emit()" SHOULD HAPPEN HERE:
res
.status(201)
.send('Accepted')
.json(student)
} catch (error) {
let err = error
err = createError(500)
err.innerException = error
next(error)
}
}

How to handle errors in Next.js api?

What is the best practice how to organize code in Next.js api handlers? I saw this video and he puts all REST routes in two files:
pages/api/users/index.ts handles all operations that don't require id so GET /api/users - get all users and POST pages/api/users - create a new user
pages/api/users/[id].ts handles all operations that require id so GET api/users/1 - get user by id, PUT /api/users/1 - update user, and DELETE /api/users/1 - delete a user
This means a lot of code will go into just 2 files and handled by a switch case statement. How should all this code be organized?
Every case statement should have its own try catch block for handling database calls which is a lot of repetition, I could make single try catch around entire switch but that will wrap a lot of unnecessary code, and maybe each case needs different handling? I could put single try catch in higher order function and wrap each case block with it but I'm not sure that's nice either?
Also later I will need to protect some routes with withProtected and withRole middlewares but that wont be easy because now multiple api's are handled inside single handler call.
What is the best way to organize this? Is there already good complete example existing?
// pages/api/users/index.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { hash } from 'bcryptjs';
import prisma from 'lib/prisma';
/**
* POST /api/users
* Required fields in body: name, username, email, password
*/
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
): Promise<void> {
const { method, body } = req;
switch (method) {
case 'GET':
// get all
// try catch again?
const users = await prisma.user.findMany();
res.status(200).json({ users });
break;
case 'POST':
// create
try {
const { name, username, email, password: _password } = body;
// todo: validate...
const _user = await prisma.user.findFirst({
where: { email },
});
if (_user)
throw new Error(`The user with email: ${email} already exists.`);
const password = await hash(_password, 10);
const user = await prisma.user.create({
data: {
name,
username,
email,
password,
},
});
res.status(201).json({ user });
} catch (error) {
res.status(500).json({ error });
}
break;
default:
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
This is how I've done things. It also covers method not allowed
Working code example from my project
src/somewhere/globalAPICall.js
/**
*
* #param {http.IncomingMessage} req
* #param {http.ServerResponse} res
* #param {{[key: string]: () => Promise<void>}} actions
*/
export default async function globalAPICall(req, res, actions) {
try {
const method = req.method
// check an action exists with request.method else throw method not allowed
if (!Object.keys(actions).includes(method)) {
console.log('method not allowed')
throw new MyError('Method not allowed', 405)
}
// run the action matching the request.method
await actions[method]()
} catch(err) {
if (err instanceof MyError) {
res.status(err.code).send(err.message);
} else {
res.status(500).send("Internal server error");
}
}
}
src/pages/api/users.js
/**
*
* #param {http.IncomingMessage} req
* #param {http.ServerResponse} res
*/
export default async function handler(req, res) {
async function POST() {
const { email, password, username } = req.body;
if (!username) {
throw new MyError('Username required', 400)
}
await CreateUser(email, password, username)
res.status(201).send();
}
async function GET() {
const result = await ListUsers()
res.status(200).json(result);
}
await globalAPICall.js(req, res, {POST, GET})
}

method in express js get() - returns undefined

I have this Node.js code that should, using Express.js http get, decode my jwt token from my http auth header.
When I write the code of decodeToken() inside of my express js get() method, everything works fine.
When I extract it to outside method (decodeToken() method) I get that undefined value from this method. Mind you, when this code was hard coded inside of the method get() it works.
Why is that, the async nature of Node.js? Does the method finish after the get() method was supposed to assign the token?
If I will use promises, will it solve that?
var jwtCheck = expressJwt({
secret: "some secret"
});
app.use('/test', jwtCheck);
app.get('/test', function (req, res) {
var token = req.get('Authorization').split("Bearer ")[1];
var information = decodeToken(token)
console.log("information: "+information);
if (information!=null) {
res.json(information);
}
else {
res.json('Failed to authenticate token.');
}
});
var decodeToken = function (token) {
console.log(token);
jwt.verify(token, secret, function (err, decoded) {
if (err) {
console.log('Failed to authenticate token.');
return null;
} else {
return (decoded);
}
});
}
var getToken = function (req) {
return req.get('Authorization').split("Bearer ")[1];
}
jwt.verify is async because you pass a callback to it so anything that calls it needs to handle that. You could look at promises to clean up the logic a little bit but just using the code you have you could modify it like this:
var jwtCheck = expressJwt({
secret: "some secret"
});
app.use('/test', jwtCheck);
app.get('/test', function (req, res) {
var token = req.get('Authorization').split("Bearer ")[1];
// pass callback to decodeToken that gets called after the token is verified
decodeToken(token, function(information) {
// this function won't execute until decodeToke calls it as `next()`
console.log("information: "+information);
if (information!=null) {
res.json(information);
}
else {
res.json('Failed to authenticate token.');
}
})
});
// accept `next` callback
var decodeToken = function (token, next) {
console.log(token);
jwt.verify(token, secret, function (err, decoded) {
if (err) {
console.log('Failed to authenticate token.');
return next(null);
} else {
return next(decoded);
}
});
}
var getToken = function (req) {
return req.get('Authorization').split("Bearer ")[1];
}

Categories

Resources