SimpleNext.js

Nextjs + Firebase : How to use firestore database in Next.js apps

Cover Image for Nextjs + Firebase : How to use firestore database in Next.js apps
Marouane Reda
Marouane Reda
If you need to understand the basics of Next.js, i recommend this course. (Disclaimer : this is an affiliate link that may earn me a small commission, but with no extra cost to you if you choose to enroll)

After Implementing Firebase authentication in Next.js app, we will continue our series of Nextjs and Firebase by implementing firestore access, data fetching and data insert from a Next.js app.

What is Firestore

Firestore is one of the databases that comes a part of Firebase. It is could-hosted, Non-relationnal ( NoSQL ), and can be used to store and sync-data in Real-Time. You can access it using C++, Java, Javascript or Unity and it supports RPC and REST APIs. the major advantages of FireStore are :

  • Easy to use
  • Scalability : It will automatically scale to accommodate your traffic
  • NoSQL : you are not bound to SQL constraints like a set schema, primary and foreign keys, …
  • Realtime : You can set a listener to your data, and every change on the data will automatically (almost « magically ») be shown on you interface ( without reloading your page or re-querying your data) but Firestore has also some negative points :
  • It is not free after a set number of reads in a month. If you don’t set up your code in the right way and end up querying your data too much, you can find a hefty bill waiting for you by the end of the month
  • Some applications need the structuring that comes with SQL : especially when you need data that exists in multiple collections, the sql joins come very handy. in NoSQL you need to « denormalize » your data, which is quite cumbersome sometimes

Create the posts collection

From here onwards we will take the app created in the first article on firebase as the base for the next steps.

In our Firebase projects, Go to the Firestore database tab then create the ‘posts’ collection and its first document (don’t forget to click on the ‘ID generated automatically button’)

https://firebasestorage.googleapis.com/v0/b/kmx1-16598.appspot.com/o/blog%2FCapture%20d%E2%80%99e%CC%81cran%202021-08-21%20a%CC%80%2017.04.53.png?alt=media&token=cca57d3b-16ad-4303-9b4e-8f499f83af67

Retrieve data from Firesotre

Our logged_in.js page was a simple page with a sign out button in it. Now we will retrieve the documents for the ‘posts’ collection and show the titles in our page.

First, in the root directory of our app, install the react-firebase-hooks package :

nom install react-firebase-hooks

then in our logged_in.js page, use the useCollection hook to retrieve our documents from the ‘posts’ collection :

const [posts, postsloading, postserror] = useCollection(
    firebase.firestore().collection("posts"),
    {}
  );

then in our jsx :

<Container>
      // ...
      <Button onClick={signOut}>Sign out</Button>
      // ...
      <Col>
      <h1> Posts </h1>
      <div>
      {postserror && <strong>Error: {JSON.stringify(postserror)}</strong>}
        {postsloading && <span>Collection: Loading...</span>}      
      {posts && posts.docs.map((doc) => (
              <div>
                {JSON.stringify(doc.data())},{' '}
              </div>
            ))}
            </div>
</Col>
    </Container>

so we try to retrieve data, while we are still loading the page will show : Collection: Loading...

If there is an error, the error will be shown. Otherwise, the data retrieved will be shown on the page : https://firebasestorage.googleapis.com/v0/b/kmx1-16598.appspot.com/o/blog%2FCapture%20d%E2%80%99e%CC%81cran%202021-08-21%20a%CC%80%2017.13.48.png?alt=media&token=b085dc26-0d68-40d2-85fe-86ac82e44d6e

Insert data into our collection

To add a new post into our collection, we will create a form in which we will add the post’s title. On submitting, we will simply add the post to the collection:

const onSubmit =  (event) => {
   console.log(post)
  try {
    db.collection("posts").add({
     title : post,
   }).then((docRef) => {
     console.log("Document written with ID: ", docRef.id);
 })
} catch(error)  {
     console.error("Error adding document: ", error);
 };
   event.preventDefault()
 };

https://firebasestorage.googleapis.com/v0/b/kmx1-16598.appspot.com/o/blog%2FCapture%20d%E2%80%99e%CC%81cran%202021-08-21%20a%CC%80%2017.16.41.png?alt=media&token=4a417616-b3ce-4fe7-81c3-e56b3de1e765

After clicking the ‘add post’ button, the data shown in the page is automatically updated to show this change :

https://firebasestorage.googleapis.com/v0/b/kmx1-16598.appspot.com/o/blog%2FCapture%20d%E2%80%99e%CC%81cran%202021-08-21%20a%CC%80%2017.17.56.png?alt=media&token=5ce026e3-3af7-4575-9fca-590aebf08dcd

This is the Realtime Functionality that comes with Firestore, which is very important for apps like chats, social media feeds, …

this is the final logged_in.js file :

import { useEffect, useState, React } from 'react';
import { useRouter } from 'next/router';
import { useAuth } from '../context/AuthUserContext';
import firebase from '../lib/clientApp'
import {Container, Row, Col, Button, Input, Form} from 'reactstrap';
import { useCollection } from "react-firebase-hooks/firestore";

const LoggedIn = () => {

  const { authUser, loading, signOut } = useAuth();
  const [post, setPost] = useState('');
  const db = firebase.firestore();
  const router = useRouter();
  const onSubmit =  (event) => {
    console.log(post)
   try {
     db.collection("posts").add({
      title : post,
    }).then((docRef) => {
      console.log("Document written with ID: ", docRef.id);
  })
} catch(error)  {
      console.error("Error adding document: ", error);
  };
    event.preventDefault()
  };
  const [posts, postsloading, postserror] = useCollection(
    firebase.firestore().collection("posts"),
    {}
  );

  // Listen for changes on loading and authUser, redirect if needed
  useEffect(() => {
    if (!loading && !authUser)
      router.push('/')
  }, [authUser, loading])
  

  return (
    <Container>
      // ...
      <Button onClick={signOut}>Sign out</Button>
      // ...
      <Col>
      <h1> Posts </h1>
      <div>
      {postserror && <strong>Error: {JSON.stringify(postserror)}</strong>}
        {postsloading && <span>Collection: Loading...</span>}      
      {posts && posts.docs.map((doc) => (
              <div>
                {JSON.stringify(doc.data())},{' '}
              </div>
            ))}
            </div>
            <Form  className="custom-form"
            onSubmit={onSubmit}>
            <Input value={post} onChange={(event) => setPost(event.target.value)} />
            <Button >Add post</Button>
            </Form>
      </Col>
    </Container>
  )
}

export default LoggedIn;

Conclusion

After seeing how we can implement Firebase authentication for Next.js app, we have now seen how to do the same for Firestore database. In the next article of the series, we will see how to use Firebase Storage with Next.js to store our images, files, …