Going Serverless with Next.js and Firebase LR Lee Robinson / July 19, 2019 5 min read • ––– views

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.

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.

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.

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.

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.

lib/firebase.js

import admin from 'firebase-admin' ; try { admin . initializeApp ( { 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 ) { if ( ! / already exists / u . test ( error . message ) ) { console . error ( 'Firebase admin initialization error' , error . stack ) ; } } export default admin . firestore ( ) ;

pages/api/city/[name].js

import firebase from '../../../lib/firebase' ; export default ( req , res ) => { firebase . collection ( 'cities' ) . doc ( req . query . name ) . get ( ) . then ( ( doc ) => { res . json ( doc . data ( ) ) ; } ) . 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

collection req.query.name has the dynamic route value of des-moines

has the dynamic route value of We return the data for that document

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.

/pages/city/[name].js

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 ( < div > < p > Population : { data . population } < / p > < / div > ) ; } export default City ;

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

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.

React 2025 Build and deploy a modern Jamstack application using the most popular open-source software.