Firebase Cloud Functions with NGINX like reverse proxy

How to create http endpoints on Firebase Cloud Functions and serve them with an NGINX like reverse proxy, without any additional overheads or NGINX setup.

I assume by now most of us have already had their fare share of the Serverless architecture, either by directly working with such a setup or at least coming across the buzz word. However if you aren't familiar with the Serverless concept, I'd strongly advise reading about it and getting yourself up to speed. I'd not dig into the nitty gritties of a serverless setup as that would be something worth an article on its own. I'd rather focus on the problem at hand, ie creating multiple such serverless http functions and routing them through our custom domain, all without having to configure and maintain an NGINX reverse proxy or any such similar additional overheads.

For this article I'd focus on using Firebase Cloud Functions to create our http endpoints. There are plenty of cloud providers in the market providing similar capabilities as Cloud Functions, and though they all have somewhat similar setups, I'd leave those for separate articles of their own.


Let's dig in and start setting things up in context by making some hypothetical apis that we want to serve via the serverless setup. This is how we are going to do it in Firebase Cloud Functions.

const functions = require('firebase-functions')

// hypothetical e-commerce platform apis for
//  - user related operations
//  - product related operations
//  - merchant related operations

exports.usersEndpoint = functions.https.onRequest((req, res) => {
  // check request and send response
})

exports.productsEndpoint = functions.https.onRequest((req, res) => {
  // check request and send response
})

exports.merchantsEndpoint = functions.https.onRequest((req, res) => {
  // check request and send response
})

exports.defaultEndpoint = functions.https.onRequest((req, res) => {
  res.status(404).send('Not found!')
})

index.js inside of your functions folder

Now we would deploy these functions from command-line using firebase command-line tools firebase deploy --only functions. Once the deployment is complete, depending upon which location your http functions were deployed to, the following endpoints will now be available for you to use, assuming you replace project-id with your firebase project id and us-central1 with the location where you deployed the cloud function.

  • https://us-central1-<project-id>.cloudfunctions.net/usersEndpoint
  • https://us-central1-<project-id>.cloudfunctions.net/productsEndpoint
  • https://us-central1-<project-id>.cloudfunctions.net/merchantsEndpoint

While in theory now you could invoke these http endpoints directly using the above urls, you wouldn't want to do so because:

  • the endpoint doesn't speak of your brand domain
  • it exposes information about your infrastructure source
  • you need to enable CORS to be able to consume the api from your clients

Instead what you would like to have is https://api.my_ecommerce_site.com serving these different endpoints.

  • https://api.my_ecommerce_site.com/users
  • https://api.my_ecommerce_site.com/products
  • https://api.my_ecommerce_site.com/merchants

Some background to facilitate the setup

In order to achieve such a setup, there are a few building blocks that firebase provides, which we can leverage to implement our use case.

Steps

  1. Go to your hosting setting inside your firebase project console, and create a new hosting site called my_ecommerece_site-proxy.
  2. Go to your newly created my_ecommerece_site-proxy site configuration settings and connect your custom domain api.my_ecommerce_site.come to the site.
  3. Create a new target for deploying the previously created hosting site by running firebase target:apply hosting proxy my_ecommerce_site-proxy from the command-line.
  4. Create a new blank folder in the root of your firebase project called proxy and add an empty file .gitkeep inside the folder.
  5. Update your firebase.json file with the following configurations.
{
  "hosting": {
    "target": "proxy",
    "public": "proxy",
    "rewrites": [
      {
        "source": "/users/**",
        "function": "usersEndpoint"
      },
      {
        "source": "/users?(?)*",
        "function": "usersEndpoint"
      },
      {
        "source": "/products/**",
        "function": "productsEndpoint"
      },
      {
        "source": "/products?(?)*",
        "function": "productsEndpoint"
      },
      {
        "source": "/merchants/**",
        "function": "merchantsEndpoint"
      },
      {
        "source": "/merchants?(?)*",
        "function": "merchantsEndpoint"
      },
      {
        "source": "**",
        "function": "defaultEndpoint"
      }
    ]
  }
}

firebase.json file in the root of your project

6. Deploy the hosting configuration updates by running firebase deploy --only hosting:proxy form the command-line.

That's it, you would now be able to invoke your endpoints from your own custom domain. And I hope by now you get the idea, you have an easy to use reverse proxy setup that you can configure as and when you add new http serverless functions. Simply update the firebase.json file with the updates and redeploy your proxy host.

Bonus

Add the following as part of your package.json scripts, so that in future you can simply run npm run deploy-proxy from the command line to deploy your reverse proxy configuration updates.

{
  ...
  "scripts": {
    ...
    "deploy-proxy": "firebase deploy --only functions:proxy"
  }
  ...
}

Updates for the package.json file.


If you happen to run into issues or spot any mistakes with this article, please feel free to comment and I'd try my best to help you out / correct the mistakes.

Happy Coding!