Going Serverless with Next.js and Firebase
Santheepkumar / 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.
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
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
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.
Vercel
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
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';
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert({
project_id: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
private_key: process.env.FIREBASE_PRIVATE_KEY,
client_email: process.env.FIREBASE_CLIENT_EMAIL
}),
databaseURL: 'YOUR_DATABASE_URL_HERE'
});
}
export default admin.firestore();
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 req.query.name
has the dynamic route value ofdes-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 (
<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! 🎉
Conclusion
If you find yourself with similar requirements, you can now consider using this stack to provide an elegant solution. For more information, check out Real-Time Blog Post Views With Next.js and Firebase.
Subscribe to the newsletter
Get emails from me about web development, tech, and early access to new articles.
- subscribers – 32 issues