Question
How do I use Express within Firebase Cloud Functions?
Expectations
Using either of the URLs I've setup, I expect to see, "Hello from Express on Firebase!" in the console logs.
Why? My understanding is, "*" means all routes requested should response.send("Hello from Express on Firebase!");
app.get("*", (_request, response) => {
response.send("Hello from Express on Firebase!");
});
Issue
When I use, https://us-central1-myapp.cloudfunctions.net/helloWorld I get the expected Hello from Firebase! in the logs. Should I also see "Hello from Express on Firebase!"?
When I use, https://us-central1-myapp.cloudfunctions.net/api, I get a 404 error
The URL, https://us-central1-myapp.cloudfunctions.net/api is the issue. See why in the answer below.
Code
// Express
import express = require("express");
const app = express();
const cors = require("cors")({
origin: "*"
});
app.use("*", cors);
// Firebase Functions SDK
import functions = require("firebase-functions");
app.get("*", (_request, response) => {
response.send("Hello from Express on Firebase!");
});
exports.api = functions.https.onRequest(app);
exports.helloWorld = functions.https.onRequest((_request, response) => {
response.send("Hello from Firebase!");
});
tl;dr
An example of what I'm hoping to accomplish is here, but none of the code examples worked for me. I get I get a 404 error with each one.
The Express Documentation here shows a similar HelloWorld example, but I confuse how Firebase takes the place of app.listen(3000, () => console.log('Example app listening on port 3000!'))
Is cors working properly in my example code? Although I get the expected response and log, Chrome console warns: Cross-Origin Read Blocking (CORB) blocked cross-origin response https://appengine.google.com/_ah/lo....
I have a Slack App that is hitting these URLs (I hit them with chrome too). Eventually, I'd like to use Botkit middleware in my Google Cloud Functions. I don't yet grasp proper setup of Express app.use() and app.get()
Answer
I made a simple mistake by treating /api as a function when it's actually a part of the path.
By using this URL with the trailing /
https://us-central1-myapp.cloudfunctions.net/api/
I'm now hitting the Express route and function.
Related
I am unable to get rid of this browser error in my sample React/Express app:
Access to fetch at 'http://localhost:9000/getFiles' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Where /getFiles makes an API call from React to Express.
I've done a bunch of googling and I thought I have everything coded correctly.
App.js:
import express from 'express';
import routes from './routes/index.js'
import cors from "cors";
const app = express();
app.use(cors());
app.use("/", routes);
const PORT = 9000;
app.listen(PORT,() => {
console.log(`Running on PORT ${PORT}`);
})
I had suspected that it was related to the ES6 formatting but I've seen other code examples that use that import statement exactly as is.
I figured it out. The issue is because I incorrectly added the cors() reference AFTER setting the routes. After reversing the order and restarting my app, it started working.
Correct order:
...
app.use("/", routes);
app.use(cors());
...
Props to this article for putting me on the right track.
When I'm sending a request to my express API through Axios, the GET and POST request respond correctly, but PUT and DELETE does not.
This is not a code problem as all my tests are passing, I believe this is related to CORS
I have installed morgan npm package to log the requests to the server.
The GET and POST are working fine, but PUT/DELETE are not and console.log() messages in those route handlers don't even show up!!!
The response from PUT and DELETE is 404
app.js
app.use(cors())
router.js
router.delete('/', (req, res) => {
// this log statement does not show up!
console.log('request recieved')
Controller.DeleteItem(req.body.data.title).then(() => {
res.redirect('/')
}
}
console output
OPTIONS 204
DELETE 404
request
axios.delete('http://localhost:5000/', {
data: {
title: title
}
}
This was discussed outside SO : the issue was that the put and delete routes were placed inside the post route, the indentation (not appearing here) was obvious when seeing the whole code in context.
Hi I am a newbie and started to learn about node recently. I took an Heroku tutorial on websockets (https://devcenter.heroku.com/articles/node-websockets) and adapted it for a specific project I was working on. In the example code there was a single index.html file with some embedded javascript. I moved this script out to a separate file and referenced it in the HTML. Everything worked fine locally but doesn't work when i deploy to Heroko. I chatted with the very helpful team at Heroku who informed me that my server side code is serving up all files as HTML and I need to change the code. They gave me some pointers and I tried as many things as I could over several days but to no avail. In the end they recommended coming to this forum as a way to solve the problem as it is beyond their scope. The existing code that serves up the index.html file is as follows:
const express = require('express');
const SocketServer = require('ws').Server;
const path = require('path');
const PORT = process.env.PORT || 3000;
const INDEX = path.join(__dirname, 'index.html');
const server = express()
.use((req, res) => res.sendFile(INDEX) )
.listen(PORT, () => console.log(Listening on ${ PORT }));
At first i edited this to include the line:
app.use(express.static('public'))
but this didn't work. I then amended as follows and it still doesn't work:
const INDEX = path.join(__dirname, 'index.html');
const JS = path.join(__dirname, 'client.js');
const server = express()
.use((req, res) => {
res.sendFile(INDEX);
res.sendFile(JS);
I have looked at other tutorials that work when i run them in isolation but when I try to adapt my above code it simply doesn't work. I would really appreciate if someone out there could point me in the right direction.
BTW this is what Heroku told me:
"To explain a bit further this error Uncaught SyntaxError: Unexpected token < is because the URL for http://thawing-journey-33085.herokuapp.com/client.js isn't serving a javascript file but is instead trying to serve the HTML for the homepage. This suggests you have an issue with the routing in your application which you'll need to review. This is probably because your server.js file doesn't check for any particular URL before sending the index.html file."
Thanks
I serve my static files like this:
// define the folder that will be used for static assets
app.use(Express.static(path.join(__dirname, '../public')));
// handle every other route with index.html, which will contain
// a script tag to your application's JavaScript file(s).
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, '../public', 'index.html'));
});
This way i set the static folder in the express.static middleware so i can serve the files. And then i redirect all url request to the index.html
To know more: express static
I am using Express to serve static assets. Frontend is AngularJS 1.x and I have html5mode enabled. Trying to implement Recaptcha is where I noticed the following in Chrome dev tools:
Uncaught SyntaxError: Unexpected token <
api.js?onload=vcRecaptchaApiLoaded&render=explicit“:1
When I click on the function to initiate the Recaptcha process I receive:
Error: reCaptcha has not been loaded yet.
So far this makes sense to be bacause I noticed the string that the first error is reporting is part of the url path to load Recaptcha from Google.
When I click on the url (api.js?onload=vcRecaptchaApiLoaded&render=explicit“:1) in chrome tools it loads my index.html! Strange!
This has be believing it has something to do with my static asset serving. I have played around with my express server until the cows came home and cannot figure out how to remedy.
Live example:
http://ninjacape.herokuapp.com
Here is my code and thank you for taking a look!
index.html
<script src=“https://www.google.com/recaptcha/api.js?onload=vcRecaptchaApiLoaded&render=explicit“ async defer></script>
express.js
var express = require('express');
var compression = require('compression');
var app = module.exports.prod = exports.prod = express();
var devAPI = 'http://localhost:1337';
app.use(compression());
app.use(express.static('.tmp'));
app.get('/*', function(req, res) {
res.sendFile(__dirname + '/.tmp/index.html');
});
var proxy = require('express-http-proxy');
app.use('/api', proxy(devAPI));
var port = process.env.PORT || 8000;
app.listen(port);
Well... I wish I had a better answer however I am just happy I got it to work. Something in the way I am statically serving files is appending any url in index.html to http://localhost:8000. To work around this I took a look at the actual request coming into Express and found the url. Then added logic to redirect that request to the real url. See commented code below for more info:
// Any requests matching /*
app.get('/*', function(req, res, next) {
// Log the original url express is tying to go to
console.log(req.url);
// This is the url found from the step above (Where are the extra characters coming from?!)
var url ='/%E2%80%9Chttps://www.google.com/recaptcha/api.js?onload=vcRecaptchaApiLoaded&render=explicit%E2%80%9C'
// Self explanatory
if (req.url === url) {
// Respond by redirecting the request
res.redirect('https://www.google.com/recaptcha/api.js?onload=vcRecaptchaApiLoaded&render=explicit')
//End this block and continue
next();
} else {
// If it doesn't match the above url, proceed as normal
res.sendFile(__dirname + '/.tmp/index.html');
}
});
I've been working on this seemingly simple problem for about a week now and feel like there is conflicting information and am hoping someone can give shed some light for me. I'm trying to use Parse Hosting for a marketing site with bootstrap, just HTML and CSS with a little JS; and Cloud Code to do some simple server side tasks like charging a card via Stripe. Everything in the documentation makes it seem this is easily doable, but the documentation also seems to lead me to believe certain methods aren't.
For example, this video shows a Stripe engineer building exactly what I want. However, it's not abundantly clear that he is using pure HTML and CSS for the front end instead of an Express templating engine (which I am not using) - http://blog.parse.com/videos/parse-developer-day-2013-a-new-kind-of-checkout/
This post says Parse Hosting and Express now work hand in hand, GREAT!
http://blog.parse.com/announcements/building-parse-web-apps-with-the-express-web-framework/
But the documentation (JS > Cloud Hosting > Dynamic Websites) says you have to delete index.html >> "If you choose to use Express or Node.js, you'll first need to delete public/index.html so that requests can get through to your custom handler functions."
I want to have a single page website hosted at public/index.html that uses Stripe Checkout v3 to create a token then pass that to Parse for a quick execution of the charge, but again, every which way I try has been unsuccessful so far.
In addition, I'm thinking Parse Hosting of pure HTML/CSS won't work with Cloud Code the way I want because a simple call of /hello below returns nothing.
Here's my code:
//public
//index.html
<form action="/charge" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_zippitydoo"
data-image="http://image.jpg"
data-name="Thing"
data-description="Shut up and take my money"
data-amount="4000">
</script>
</form>
//cloud
//main.js
var express = require('express');
var app = express();
var Stripe = require('stripe');
Stripe.initialize('sk_test_blahblahblah');
app.get('/hello', function(req, res) {
res.send('hello world');
});
app.post('/charge', function(req, res) {
res.send('Charge Attempt');
token_id = req.body.stripe_token
Stripe.Tokens.retrieve(token_id).then(function(token) {
return Stripe.Charges.create({
amount: 1000,
currency: "usd",
source: token_id
});
});
});
What you need is for express to serve your HTML. To do this, register a static resources directory. In your main.js, after you instantiate your app with var app = express(), do this:
app.use(express.static('public'));
Express should then treat your /public/index.html file as the directory index by default, and your app will serve any other files under /public. More info: http://expressjs.com/4x/api.html#express.static
There are several things I did wrong here. I'll explain my mistakes, then you can compare the below code that works with the above code in the question that doesn't.
1) I wasn't parsing the data I was receiving (see underneath // App configuration section)
2) The JSON that is passed needs to be parsed using CamelCase (stripeToken not stripe_token)
3) The charge is set as a variable, not returned (var = charge instead of return charge). Return may work, I didn't test it however.
4) It is imperative that you include the app.listen(); in order to connect to the public folder from the cloud folder
//cloud
//main.js
var express = require('express');
var Stripe = require('stripe');
Stripe.initialize('sk_test_blahblahblah');
var app = express();
// App configuration section
app.use(express.bodyParser()); // Middleware for reading request body
app.post('/charge', function(req, res) {
var stripeToken = req.body.stripeToken;
var stripeEmail = req.body.stripeEmail;
res.send('Charging your card...');
var charge = Stripe.Charges.create({
amount: price,
currency: "usd",
source: stripeToken,
receipt_email: stripeEmail
}, function(err, charge) {
if (err && err.type === 'StripeCardError') {
res.send('The card has been declined. Please check your card and try again.');
}
});
});
// Attach the Express app to your Cloud Code
app.listen();