How to disable GCF URL query string parsing - node.js

I have a simple node.js cloud function that I need the un-parsed original URL within. In the request object, we have request.url, request.originalURL but both of these seem to be parsed upstream of the function into key=parameter pairs. For example, calling the function from firebase hosting with the url https://www.example.com/index.html?var1&var2&var3 would yield an output of "/index.html?var1=&var2=&var3=" for either request attribute. I can remove the "=" characters, but it gets more complex. For example, if var1="123,456" I now have "/index.html?123%2C456=&var2=&var3=" which requires me to un-parse, and also decode the URL.
The real show stopper, is that for some reason var1 can end up after var2 and var3. I.E. "/index.html?var2&var3&var1". This doesn't happen with the local emulator, but does on the deployed version. Since I'm not using key=value pairs, I rely on the variables being in the same order. I can't change the structure without a lot of work on the front end app.
When I run the function locally with firebase emulator, i can use request.headers["x-original-url"] but this is not available when deployed to GCF.
In the express documentation, it shows that the query parser can be turned off by app.disable("query parser") or app.set("query parser", false), but neither of these have an effect on the url locally or in the cloud.
Here is a snippet to recreate the issue:
const functions = require("firebase-functions");
const express = require('express');
const app = express();
app.disable('query parser');
// also tried
// app.set('query parser', false);
app.get("*", async (req, res) => {
functions.logger.log(req.url, req.originalUrl);
return res.status(200).send(""); //
});
exports.ssr = functions.https.onRequest(app);
I think either I'm missing something in the documentation, or there is a feature to disable the express parser missing from google cloud functions.

Related

Firebase Functions With Express Failed to decode param %CO

There is a solution to this error without the use of Firebase here when using app.listen(8080) however this does not work while serving in cloud functions with exports.app = functions.https.onRequest(app)
Here is a simple reproduction code
const app = express();
app.get('**', (req, res) => res.send('working'));
app.use((err, req, res, next) => res.redirect('/404'));
exports.app = functions.https.onRequest(app) // doesnt work
// app.listen(5000) // works
How do you go about catching this error in firebase functions? I would like the redirect to work.
firebase test: firebase serve --only functions
express serve: node index.js
URL to test: http://localhost:5000/%CO
Note that the additional %CO is the one that cannot be decoded by express. This error is caught while serving with the express method but not with the firebase functions method.
As this seems like a bug, I have also created an issue here on github incase I find no workaround on it.
Try changing the name of app in exports.
exports.newApp = functions.https.onRequest(app)
According to the documentation, the correct way of using an Express app with Firebase Functions is to pass the application to a Function like:
// Expose Express API as a single Cloud Function:
exports.app = functions.https.onRequest(app);
Listening to a port for requests does not apply when running through Firebase Functions. As for how to catch errors when passing your app to a function, it’s done in the same way as the question you referenced as far as I have reviewed. You can catch errors by using Express middleware while using Cloud Functions.
Moreover, implementing redirects with Firebase Functions is explained in this related question, which makes use of the documentation to configure how to redirect by modifying the firebase.json file.

Firebase Functions routing problem when doing post using html form data

I am building a simple web app using node js express and cloud firebase functions
i have 2 endpoints,
1. first point renders the form(GET request) and
2. POST endpoint that takes the form data on submit
for some reason firebase skipping the function name in the post url(2nd end point) on form submission but it works fine on local express server
example: if form acion attribute value is "check" and firebase function name is "helloWorld"
the form submit url should be "<default firebase pefix>/helloWorld/check"
but instead the url it posting to is "<default firebase pefix>/check".
firebase function name in the url is getting skipped. which is giving me function not found in location us-central etc
another thing i observed is if i give slash as prefix to action attribute value like "action = "\check". firebase skipping the whole base url and appending from attribute value the port value
i tried a work around by setting the static absolute path (path after production) to the form action attribute.
But i want to if its a bug or am i missing something
<form action="check" method="POST"
<label for="uname"><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="uname" required>
<button type="submit">Login</button>
</form>
// action = "/check" this is skipping total base url all together
const functions = require('firebase-functions');
const express = require('express')
const bodyparser = require('body-parser')
const app = express()
app.set('port',6000)
app.use(bodyparser.json())
app.use(bodyparser.urlencoded({extended:true}))
app.set('view engine', 'ejs');
app.get('/',(req,res)=>{
res.render('formfill')
})
// this below end point is supposed to get triggered on form submission.
// and it is working fine on local express server, but not on firebase functions
app.post('/check',(req,res)=>{
res.send(`you said ${req.body.uname}`)
})
exports.helloWorld = functions.https.onRequest(app);
You can't use Cloud Functions to run a web server or listen on a port. All your code that's trying to run express is not going to work. When you deploy an HTTP function, it's assigned a URL, and you use that URL as the endpoint for requests.
You should review the documentation for HTTP triggers to better understand how this works.
I solved it. i don't know if it is my mistake or lack of knowledge.
so the firebase functions url is something like this
https://us-central1-<project-id>.cloudfunctions.net/<functionname>
in the local firebase server if i go to the functions url(localhost prefix) with or without a slash at the end of the url. my root endpoint is getting consumed. which is fine.
but it is not the case in production url, a slash at end of url(after my function name) is required to load the endpoint. and any anchor tag href in the webpage should omit the prefix slash
example: action = "/check" this is not working but
action ="check/" this is working
so i just removed prefix slash in my action attribute and re deployed, now it is working.

Parameter restriction with Node.js

What I want to do is check the script URL for a parameter & display the content of that parameter like:
www.mywebsite.com/mynodescript.js?parameter=i+am+new+to+node!
Now I want to display "I am new to node!" on browser screen and if the parameter is not present I just want to exit.
edit:
I found this code but I am not sure how to deploy it
var url = require('url');
var url_parts = url.parse(request.url, true);
var query = url_parts.query;
Note: i want to upload my script on heroku & want it to call it remotely
When you say you don't know how to deploy it, I'm assuming you don't have a http server setup yet?
Look at using Express (http://expressjs.com/). It's easy enough to get started with.
Create a file called app.js like this:
const express = require('express')
const app = express()
// This handles the path /mynodescript.js You can create a bunch of functions like this to handle different paths. See the express docs for more.
app.get('/mynodescript.js', (req, res)=>{
let parameter = req.query.parameter; // <-- Could also do let {parameter} = req.query This is where you would pull out your url parameters
if(parameter){
res.send(parameter); // <-- this sends it back to the browser.
}else{
res.status(422).end(); // <-- you can set a status here or send an error message or something useful.
}
})
app.listen(3000, () => console.log('Example app listening on port 3000!'))
Start the script using node app.js from the same directory.
Then open a browser and go to http://localhost:3000/mynodescript.js?parameter=i+am+new+to+node and you should see your parameter
Note that you will have to install express first npm install express --save
Note that you do not have to use express. There are quite a few http libraries available for nodejs. Or you can use the built-in http server (https://nodejs.org/api/http.html). It's good to get familiar with the NodeJS docs, but their http server is cumbersome to work with.

nodejs json endpoint from api json stream

I am connecting to an api and pulling a set of json data. the javascript outputs the json as the variable feedData and when i include that in a html i get the json on a html page as expected. What i want to do is output it as a json endpoint. I tried to get fancy and when i tried:
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send(feedData);
});
app.listen(3000, function () {
console.log('posting data');
});
the problem is that i need to import an api.js file and when i attempt to load this i get an error related to the api.js file which is windows is not defined. Like i said, the html
document.getElementById('mydiv').innerHTML += JSON.stringify(feedData, undefined,2);
then
<pre id="mydiv"></pre>
works fine but i will also to recall every x seconds because this is a live json feed.
Currently, i just decided to connect via python, loading it into mongodb and creating a nodejs endpoint from there, which works fine, but it seems there should be a way here.
Try using res.json, res.json(feedData); to send objects. If you want to send a string just, use res.send
Instead of
res.send(feedData);
use
res.json(feedData);
Hope this helps

Capture and log the api calls made to the server along with the passed data and the results

I have an api written in node.js that handles calls coming in from websites, desktop applications, iOS applications etc. There are probably 50+ endpoints and each end point can accept anywhere from 1 parameter to possibly 10-20 depending on what is intendeding to be accomplished. These can be GET/POST/PUT/DEL
I want to start load testing my API and simulating users activities.
What I am looking for is suggestions on how you can capture the API call and the parameters that were passed along with it in a logical way.
I use forever to run my app and everything is written to a log file so my initial reaction was to do something like add a piece of middleware to the express routes that would capture the endpoint as well as the req.params and req.body but then I need to put this middleware in all 50+ routes kind of tedious.
Anyone done something like this before and has a good idea on how to capture calls / data with those calls as well as possibly capturing what is returned from my API.
Perhaps some module?
I need to have this in a readable format to provide to other people so they can structure a fake set of calls... so raw log files aren't really helpful unless they are outputted.... "pretty".
Thanks!
You're on the right track – just add your logger middleware via app.use, which runs the middleware on every request (rather than adding it to each route).
In fact, the Express docs give an example of using logger middleware:
var express = require('express');
var app = express();
// simple logger
app.use(function(req, res, next){
console.log('%s %s', req.method, req.url);
next();
});
Connect (on which Express is built) provides logger middleware, so you can just do:
var logFile = fs.createWriteStream('./myLogFile.log', {flags: 'a'});
app.use(express.logger({stream: logFile}));

Resources