Going Serverless with Next.js and Firebase


Lee Robinson / July 19, 2019

5 min read––– views

Next.js + Firebase + Vercel

A critical step in freelance development is assessing your client's business problem and finding the best technology to solve it. "Best" could mean a variety of things depending on the client: cheap, fast, robust, future-proof, or industry standard. This post will outline how I assessed a business problem, chose the "best" technology, and delivered a timely solution to my client.

BeyondHQ is an early-stage startup that helps companies expand outside of San Francisco. Think "Expansion-as-a-Service" - finding the right city, opening an office, growing your team, etc. They needed a basic website explaining who, what, and why - as well as a platform to build their SaaS solutions on top of. That's where I came in.

The Requirements#

For the main website, the requirements were fairly straight forward:

  • Basic splash page with information about the product.
  • About page with information on the employees.
  • Frequently asked questions page.
  • Save off information from a contact form.
  • Cheap hosting. Ideally free.


They also wanted a platform to build SaaS on. That meant I needed to consider more requirements, as it would be the next bit of functionality I would create.

The first step towards their full-featured SaaS product was a location evaluation tool. Users could enter information like their company size, the number of employees, and potential expansion locations to get a list of their top city matches. Then, they could explore each city and see metrics about what made it an attractive option to expand to. Those requirements were a bit more complex:

  • Resilient, highly available, and fast.
  • Cheap storage. Ideally free.
  • Great SEO (Search Engine Optimization).
  • City pages should be dynamic and pull from a database.
  • The ability for non-technical people to easily edit data.
  • Save off lead generation information from user input.


The Technology#

Given these requirements, what technical choices did I make and why? Let's take a look.


React is the industry-standard for building modern web applications. Of 90,000 developers surveyed by Stack Overflow, React was the most loved and most wanted. This technology provides a platform for the main website, as well as any future SaaS.


Next.js makes it easy to build scalable, performant React code. It's blazing fast and uses server-side rendering to improve SEO. On top of that, it simplifies the developer experience and provides a future-proof platform for the BeyondHQ team.


Deploying serverless applications couldn't be easier with Vercel. It's resilient, highly available, and fault-tolerant. Plus, it has an excellent free tier with the ability to scale as needed.


Firebase is the industry standard NoSQL platform. It has a sufficient user/permissions system and an easy interface for non-technical users. Did it mention it has a generous free tier? Firebase's Cloud Firestore product allowed us to save off contact info, city information, and user's lead gen input. It was a no-brainer!

Why use NoSQL? There weren't any requirements which needed a relational database. If there were, I might have considered Postgres on Hasura instead.

The Solution#

After reviewing the requirements and choosing the technology, the next step was to begin implementing a solution. But first, mockups!


I iterated over a handful of designs and went back and forth with the team, tweaking things based on their feedback. We eventually agreed on a mockup and the coding commenced.

API Route#

To use Firebase, we need to create an API route. This will allow us to use firebase-admin to communicate with our Cloud Firestore database.

Note: You need to generate and download a service account in Firebase.

import admin from 'firebase-admin';

try {
    credential: admin.credential.cert({
      project_id: process.env.FIREBASE_PROJECT_ID,
      private_key: process.env.FIREBASE_PRIVATE_KEY,
      client_email: process.env.FIREBASE_CLIENT_EMAIL
    databaseURL: 'https://vercel-serverless.firebaseio.com'
} catch (error) {
   * We skip the "already exists" message which is
   * not an actual error when we're hot-reloading.
  if (!/already exists/u.test(error.message)) {
    // eslint-disable-next-line no-console
    console.error('Firebase admin initialization error', error.stack);

export default admin.firestore();
import firebase from '../../../lib/firebase';

export default (req, res) => {
    .then((doc) => {
    .catch((error) => {
      res.json({ error });

Let's break this down.

  • A request is made to /api/city/des-moines
  • A connection to Firestore is created with your service account
  • We query the cities collection
  • req.query.name has the dynamic route value of des-moines
  • We return the data for that document

The Client#

For each city, we want to pass the name as a route (e.g. /city/des-moines) and fetch data from our API. Let's create pages/city/[name].js using dynamic routing and SWR.

import { useRouter } from 'next/router';
import useSWR from 'swr';

const fetcher = async (...args) => {
  const res = await fetch(...args);

  return res.json();

function City() {
  const router = useRouter();
  const { name } = router.query;
  const { data } = useSWR(`/api/city/${name}`, fetcher);

  if (!data) {
    return 'Loading...';

  return (
      <p>Population: {data.population}</p>

export default City;

This gives you the flexibility to add a loading state or placeholder to improve your user experience.

Loading Placeholder

Much better! 🎉


If you find yourself with similar requirements, you can now consider using this stack to provide an elegant solution. You can view the source code here.

Mastering Next.js

The premiere video course for building static and server-side rendered applications with Next.js and React.

Subscribe to the newsletter

Get emails from me about web development, tech, and early access to new articles.

Discuss on TwitterEdit on GitHub