0
votes

so.. from this Youtube tutorial: https://www.youtube.com/watch?v=NA21dUBfJhw&list=PL4cUxeGkcC9gcy9lrvMJ75z9maRw4byYp&index=33 I got some code for a 'doto-list'. The problem is the tutorial guy only did local hosting and I'm trying to actually host it on an actual webpage through Firebase. so what I did was adding const functions = require('firebase-functions') at the top and adding exports.app = functions.https.onRequest((request, response) => { response.send("Hello from Firebase!") at the bottom and the only result I get on the actual webpage is "Hello from Firebase!". Is there any way to make the entire 'todo-list' program work on my actual webpage?

index.js

const functions = require('firebase-functions');
var express = require('express');
var app = express();

var todoController = require('./todoController');
app.set('view engine', 'ejs');

app.use(express.static('./'));

todoController(app);



exports.app = functions.https.onRequest((request, response) => {
    response.send("Hello from Firebase!");
});

todo.ejs

<html>
      <head>
        <title>Todo List</title>
        <script
        src="https://code.jquery.com/jquery-3.4.1.min.js"
        integrity="sha256- 
        CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
        crossorigin="anonymous"></script>
        <script src="./assets/todo-list.js"></script>
        <link href="./assets/styles.css" rel="stylesheet" 
         type="text/css">
      </head>
      <body>
        <div id="todo-table">
          <form>
            <input type="text" name="item" placeholder="Add new 
             item..." required />
            <button type="submit">Add Item</button>
          </form>
          <ul>

                  <% for(var i=0; i < todos.length; i++){ %>
                    <li><%= todos[i].item %></li>
                  <% } %>

          </ul>
        </div>

      </body>


    </html>

todoController.js

var bodyParser = require('body-parser');

var data = [{item: 'get milk'}, {item: 'walk dog'}, {item: 'kick 
some coding ass'}];
var urlencodedParser = bodyParser.urlencoded({extended: false});

module.exports = function(app) {

app.get('/todo', function(req, res){
    res.render('todo', {todos: data});

});

app.post('/todo', urlencodedParser, function(req, res){
    data.push(req.body);
    res.json(data);
});


app.delete('/todo/:item', function(req, res){
    data = data.filter(function(todo){
        return todo.item.replace(/ /g, '-') !== req.params.item;
    });
    res.json(data);
});

};

todo-list.js

$(document).ready(function(){

    $('form').on('submit', function(){

        var item = $('form input');
        var todo = {item: item.val()};

        $.ajax({
          type: 'POST',
          url: '/todo',
          data: todo,
          success: function(data){
            //do something with the data via front-end framework
            location.reload();
          }
        });

        return false;

    });

    $('li').on('click', function(){
        var item = $(this).text().replace(/ /g, "-");
        $.ajax({
          type: 'DELETE',
          url: '/todo/' + item,
          success: function(data){
            //do something with the data via front-end framework
            location.reload();
          }
        });
    });

  });

edit: Turns out that as long as I delete response.send("Hello from Firebase!"); and just use exports.app=functions.https.onRequest(app);, it works... which means response.send("Hello from Firebase!); was making this code not work. Any idea why?

1
This line of code isn't going to work: app.listen(5000, '127.0.0.1'). You can't listen on some port in Cloud Functions. If you want to run an express app in Cloud Functions, you should use this as an example: github.com/firebase/functions-samples/tree/master/…Doug Stevenson
I deleted: app.listen(5000, '127.0.0.1'). still not working. I read the documentation but it was super long and it wanted me to completely do over the whole thing and I remember hosting a JavaScript file on Cloud Functions in the past just by adding a couple lines of code such as: const functions = require('firebase-functions'); and exports.app = functions.https.onRequest((request, response) => { response.send("Hello from Firebase!"); }); and I don't know why this worked just fine in the past(like last month) but not anymore.Mike Kim
One thing I noticed is that in the code that I wrote in my question, the Cloud Functions works fine and shows the result "Hello from Firebase!" and I didn't get any error message. it's just that the "todo" program wasn't showing on the webpage (probably because something wasn't embedded while Firebase part was working correctly.)Mike Kim
Take a careful look at the example I pointed you at to see how to attach an express app to a function.Doug Stevenson
I looked at it. it included like a dozen files. which specific file are you referring to? is it index.js?Mike Kim

1 Answers

3
votes

Your original setup does not work because the express application (created using express() call) is never started. An express application must call app.listen(portNum) to start the server and start accepting incoming connections. This, however, is not how cloud functions operate. There is no 'start a server and wait for a request from client' approach. Instead in cloud functions approach, when the request is received, the JS file/project gets spawned, and a request is internally triggered. The idea of your app continuously listening for an incoming connection simply doesn't apply.

Now coming to the approach which does work!

exports.app=functions.https.onRequest(app);

If you notice, in your original code, functions.https.onRequest was accepting a function which is invoked with two parameters request, and response. These objects basically are instances of http.IncomingMessage and http.serverResponse classes respectively. NodeJS internally represents all http requests using a pair of objects like this. Coincidently, when an express application receives a http request from client, it also starts off with these two objects. Based on these two, a pair of much richer Express Request and Response objects are created, and propagated through the middleware chain.

Now when you pass the app, an Express Application, to the functions.https.onRequest, the firebase system will basically consider your app to be a function which takes a http.IncomingMessage object, and a http.serverResponse object as parameters. It's an undocumented feature of expressJS that invoking the application (stored in app reference here) as a function providing IncomingMessage and ServerResponse instances as 1st and 2nd params respectively, behaves like the http request was received by underlying server. The request and response is run through the initial processing, as well middleware chain exactly as any request received directly by express' server would have been. This can be used in the rare scenarios where the user needs a mix of multiple frameworks or otherwise cannot explicitly start the server, but can access the request and response objects by other means. Like in this scenario.

Indeed, every time you invoke your cloud function, firebase functions framework is going to call express application stored in app reference as function, and your middlewares do the rest.