How do I do a GET for Firebase.functions().httpsCallable? - javascript

How do I do a GET for Firebase.functions().httpsCallable?
I keep receiving a POST error 404 but this is a GET request to my server. Should I pass in nothing or there is something to change this httpsCallable to get function?
Client
let updateWorkshop = Firebase.functions().httpsCallable('api/update/workshop');
updateWorkshop({/* nothing */})
.then(res => {
console.log(res);
}, err => {
console.log(err);
})
Server
app.get('/v3/update/workshop', asyncMiddleware( async (req, res, next) => {
let results = await UPDATE_WORKSHOP_DATE.Run()
res.status(200).json({results: results})
}))
exports.api = FUNCTIONS.https.onRequest(app);

If you are just trying to ping your callable function endpoint, a GET won't work. As you can see from the protocol specification for callable functions, it uses a POST. If you use a GET, it's an error because you're not following the protocol.

Related

How to attach user credentials to the request pipeline in expressjs?

I am trying to write a middleware that extracts the user model and attach it to the request pipeline.
I have already written a token extractor middleware and managed to attach the token to the request pipeline, but for some reason when I try to extract the user model, it works fine inside the middleware function yet inside my controller it returns as undefined.
Here's what I have tried:
utils/middleware.js
const tokenExtractor = async (request, response, next) => {
const authorization = await request.get('authorization');
if (authorization && authorization.toLowerCase().startsWith('bearer ')) {
request.token = authorization.substring(7);
} else{
request.token = null;
}
next();
};
const userExtractor = async (request, response, next) => {
tokenExtractor(request, response, next);
if(request.token){
const decodedToken = jwt.verify(request.token, process.env.SECRET);
request.user = await User.findById(decodedToken.id);
console.log(request.user); // Works
next();
} else{
response.status(403).json({ error: 'no token received' });
}
};
Inside my controllers it breaks down:
controllers/blogs.js
blogRouter.post("/", async (request, response, next) => {
if (request.body.title && request.body.url) {
const token = request.token;
if (!token) {
return response.status(401).json({ error: 'invalid token' });
}
console.log(request.user); // undefined !
if(!request.user){
return response.status(401).json({ error: 'invalid user' });
}
const user = request.user;
const blog = new Blog({
title: request.body.title,
author: request.body.author,
url: request.body.url,
likes: request.body.likes,
user: user._id,
});
await blog.save();
user.blogs = user.blogs.concat(blog._id);
await user.save();
response.status(201).json(blog);
}
response.status(400).end();
});
Both middleware are already attached to the express app.
EDIT:
I have fixed the issue by removing the call to tokenExtractor from userExtractor function, and then chaining the middleware to the router instead of calling them before everything.
I was using the tokenExtractor globaly, while the userExtractor locally to the blogsRouter. What was happening was that while the tokenExtractor was working fine, the blogRouters was being called before the userExtractor ever get called, hence why I was getting undefined.
app.js
// app.use(tokenExtractor);
app.use(requestLogger);
app.use(errorHandler);
// app.use(userExtractor);
app.use('/api/login', tokenExtractor, loginRouter);
app.use('/api/users', usersRouter);
app.use('/api/blogs', tokenExtractor, userExtractor, blogRouter); // chaining the extractors
It makes sense, let next() carry the (req, res, next) instances forward, as a pipe. No hacks are needed and you can stack as many middlewares as needed and even reuse values from one inside the other - if you can trust the order of the call stack.
You don't need to chain it. The callback argument for the next middleware function only needs to be specified as follows.
const tokenExtractor = (request, response, next) => {
const auth = request.get('authorization')
if (auth && auth.toLowerCase().startsWith('bearer ')) {
request.token = auth.substring(7)
next()
} else {
next()
}
}

Node.js Express.js Mongoose Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I have this error and try evething, I read that 'return' can be one cause of the error and promise can be another one but as you can see in the code above none of this is missing in the code. In the console.log(docs) it returns the document correctly.
can any one have some solution, tks
exports.databaseController = (req, res) => {
const promise = Database.find({}).exec()
promise
.then(docs => {
if (!docs) {
throw new Error('docs not found')
}
console.log(docs)
return res.status(200).json(docs)
})
.catch(err => {
console.log(err) // not found
return res.status(404).json({ err }) // return your error msg
})
}
// app
const controllerDatabase =
require('../controllers/database.controller')
module.exports = app => {
app.use((req, res, next) => {
res.header('Access-Control-Allow-Headers', 'x-access-token,
Origin, Content-Type, Accept')
next()
})
app.get('/api/v1/database',
controllerDatabase.databaseController)
}
// server.js
require('./app/routes/database.routes')(app)
Remove the console.log . its will set the header that why you are calling 2nd time you got the error
console.log(res.json(docs)) // remove
Or if you need to console.log do with direct docs
console.log(docs)
You are already sending your response in console.log. Remove the following
console.log(res.json(docs));
and this error will go away.
If you want to console log your docs do this:
console.log(docs);

How to use router.get and https.get together? (Node.js)

I want to get information from the http request and send it to the frontend via the path '/ get'. I combined these 2 functions and it works but I don't think it is correct:
const router = express.Router();
const https = require('https');
router.get('/get', (req, res) => {
https.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY', (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
res.json(JSON.parse(data).explanation)
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
});
Is there a better way to do this?
The better way would be to use Axios to make all your http requests to external apis which you want from nodejs application. It is a promise based request making library which you can use in browser as well as on backend server.
Install axios using npm install axios
axios.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY').then(response => res.send(response.data)).catch(error => console.log(error));
No, it works fine.
But if you want another way, then use axios
you need to require axios and then add your request in the router.
const axios = require('axios');
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
you get more information from here

Send a string from a express post route back to the client

I am trying to send back a string, once computed, as it happens back to the client from a post route on express
On the back end I have a post route:
router.post('/', async (req, res) => {
try {
// Here I do some computation on a succesion that generates a new string everytime
const foo = 'string that changes and I want to send to client'
} catch (error) {
res.status(500).send(error)
}
})
Then on the client I am using axios to send data to post
(async () => {
try {
await axios.post('/api/send/', { data })
} catch (error) {
console.log(error)
}
})()
Is there a way I could, after sending a post request, get data back from that route once it happens?
I tried to use res.send() from the post route but it just makes the functions on the back end fail.
Many thanks.
router.post('/', (req, res) => {
try {
// Here I do some computation on a succesion that generates a new string everytime
const foo = 'string that changes and I want to send to client'
res.send(foo);
} catch (error) {
res.status(500).send(error)
}
})
This should suffice. If not, you need to detail what your error message is.

Using chai to mock http requests

I'm testing a nodejs app written using express. For the unit testing I'm using chai and sinon. I have the following route in my API that I would like to test.
In my test, I'm simulating the get request with the following code:
chai.request(app)
.get('/downloads')
.send({ wmauth: {
_identity: {
cn: "username",
}
} })
.end((err, res) => {
res.status.should.be.equal(200);
res.body.should.be.a('object');
res.body.should.have.property('Items', []);
AWS.restore('DynamoDB.DocumentClient');
done();
However, I'm always getting the error "Cannot read property '_identity' of undefined". Because the object "wmauth" is not sent in the request, so it is undefined. I have tried to use the send method to try to include it in the request, but no luck. I guess I need to mock it somehow and send it into the request but have no idea how to do it. Could someone help me with this?
Below the method to test:
app.get('/downloads', async (req, res) => {
const created_by_cn = req.wmauth['_identity'].cn;
if(!created_by_cn) {
return res.status(400).json({
error: 'Mandatory parameters: created_by_cn',
});
}
try {
const data = await downloadService.getDownloads(created_by_cn);
return res.status(200).json(data);
}
catch(error){
res.status(500).json({error: error.message});
}
});
THanks
I guess you forgot to use req.body as in:
const created_by_cn = req.body.wmauth['_identity'].cn;
Hope can solve your issue
Since chai-http use superagent, so according to its doc, you need to use query() in order to pass query parameter in get request:
chai.request(app)
.get('/downloads')
.query({ wmauth: {_identity: {cn: "username"}}})
.end((err, res) => { ... });
Then in the express route you can find the parameters in req.query:
app.get('/downloads', function (req, res) {
const created_by_cn = req.query.wmauth._identity.cn;
...
})

Categories