Storyblok Raises $80M Series C - Read News

What’s the True Total Price of Enterprise CMS? Find out here.

Skip to main content

Building a serverless architecture with Firebase and Storyblok

Try Storyblok

Storyblok is the first headless CMS that works for developers & marketers alike.

In this tutorial, we will manage and authenticate data between Firebase and Storyblok. The concept of this tutorial aims to help readers understand how they can create a secure and scalable database system for their content management needs.

What is Firebase

Firebase is a Backend-as-a-Service (BAAS) platform provided by Google. It offers a suite of services for building scalable and secure web and mobile applications. Firebase provides developers with a real-time database for data synchronization across all connected devices, making it an ideal platform for building applications that require real-time data exchange and collaboration between users. Firebase also provides authentication, hosting, cloud storage, and cloud functions, making it a one-stop shop for all backend needs. With its simple and intuitive interface, Firebase makes it easy for developers to manage and scale their applications while providing a highly secure environment for their data.

What is Storyblok

Storyblok is a content management system (CMS) that allows for creating, managing, and delivering content to multiple channels, such as websites, mobile apps, and voice-activated devices. Unlike traditional content management systems, Storyblok is headless. Storyblok uses a decoupled architecture that separates its content management from the presentation layer (front-end). This allows for greater flexibility in delivering content to multiple channels and devices without complex integrations. Storyblok has a robust API for retrieving and manipulating content and an intuitive user interface for creating and managing content. With its flexible and scalable architecture, Storyblok is the perfect solution for organizations looking to manage their content in a modern, efficient, and secure manner.

By integrating Firebase's real-time database and authentication services with Storyblok's headless CMS, developers can have a flexible and scalable system for managing their content and user information.

Prerequisites

Basic experience/knowledge of front-end applications such as React.js or Next.js is needed to follow along with this tutorial. You must also have Node.js installed on your computer to install the following dependencies:

Firebase:

npm install firebase

Storyblok:

npm install @storyblok/react


Setting up Firebase

In this section, we will set up Firebase and create a project for email and password authentication.

  • The first step is visiting the Firebase website at https://firebase.google.com and automatically signing in with your Gmail account. 
  • Once the account is created, you will be redirected to the Firebase console where you can create a new project.  Click on “Create a project”.
  • Give your project a name and agree to Firebase’s terms of service.
  • Once your project is created, click on the build dropdown, and navigate to the authentication section of the Firebase console.
  • Click “Get started” and enable the email/password sign-in method. This will allow users to log in to your application using their email and password.
  • Finally, go to “Project overview” and create a new app by selecting a platform you want to develop on. This tutorial went with the Web selection.
  • Register the app and copy the Firebase SDK provided. This SDK contains useful information that you can use for development, such as your personalized API key, Auth domain, project ID, storage bucket, and more.

In order to use an app from Firebase on any front-end platform, the app must first be initialized with the provided SDK information for configuration.

Create a “hooks” folder and a new file called “useFirebaseAuth.js”. First we’ll initialize Firebase, then create 2 functions: signInWithEmailAndPasswor() and signOut(), which will help sign users in and out of the application. We’ll also use React’s useEffect() hook to check if a user of the application is signed in or not.

        
      
import { useState, useEffect } from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/auth";

function useFirebaseAuth() {
  const [user, setUser] = useState(null);
  const config = {
    apiKey: 'YOUR-API-KEY',
    authDomain: '',
    projectId: '',
    storageBucket: '',
    messagingSenderId: '',
    appId: '',
    measurementId: '',
  };
  firebase.initializeApp(config);
  const signInWithEmailAndPassword = (email, password) => {
    return firebase.auth().signInWithEmailAndPassword(email, password);
  };
  const signOut = () => {
    return firebase.auth().signOut();
  };
  useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        setUser(user);
      } else {
        setUser(null);
      }
    });
    return () => unsubscribe();
  }, []);

  return { signInWithEmailAndPassword, signOut, user };
}
export default useFirebaseAuth;
    

Setting up Storyblok

In this section, we will set up Storyblok and create a new space for content management. A space in Storyblok is a separate area where you can manage and store your content. Each space is in its own isolated environment, allowing you to manage multiple projects with different content and settings. 

  • To get started, you'll need to sign up for an account on the Storyblok website(https://app.storyblok.com/#!/signup). 
  • After signing up, you will be onboarded to Storyblok and asked if you would like to create a new space immediately.
  • Give your new space and select a preferred server location to host your space.
  • Click on “Create space”.

Once you have created a new space, you can start adding content and organizing it into a structure that makes sense for your project. In each space, a new story can be created. A story in Storyblok is a content entry that holds all the blocks and schemas you create. 

Let’s create our first story!

  • Click on the “Create story” button at the top right corner and select “Story”. The second option, “Folder”. creates a folder for multiple stories. We won’t be using that the “Folder” option, but it’s a pretty neat feature to know.
  • Next, give the story and slug of the story a name, choose “Root” as the parent directory, and name the content type. I’ll go with the default content type name, “Page”.
  • Now click on “Create” to get into the visual editor.
  • While inside the visual editor, click on the “+” icon underneath “Body” to create a new block. 
  • You will find some predefined blocks provided by Storyblok. Let’s create a new one. Type “Container” into the input field and click “Create new Container”.
  • You will be asked immediately to define the field of schemas for the new block. For the first schema, input a name, preferably “author”, and select “Text” as the schema type.  Click on “Add”. The input field will turn blank, and your schema will be added underneath.
  • For the second schema, which will contain the main blog content, we can name it “blog” and select “Textarea” as the schema type. Click on “Add”.
  • Now click on the “Save” button.
  • Inside the Container block, input a name for the author field and a quick blog for the blog field. Save the changes with the “Save” button at the top right corner.
  • Keep your content as a draft or publish it if you want to.

To use content outside of Storyblok, you have to generate an access token. You can do this by navigating to the settings tab of your newly created space and clicking on “Access Tokens” under the configuration tab. Ensure that the Access level is set to “Preview”. Once you have an access token generated, you can use it to initialize Storyblok and fetch content into your front-end application.

Before initialization, you will have to create JSX components for your content. The first component to create is one for the content type of the story that holds all of your content (Page). Create a “components” folder and a new file in it called “Page.jsx”. Paste the following code:

        
      
import {storyblokEditable, StoryblokComponent} from "@storyblok/react";
/*
  storyblokEditable helps to make the component fed to it editable
  StoryblokComponent helps to render the component dynamically
*/
const Page = ({ blok }) => (
  <main {...storyblokEditable(blok)} key={blok._uid}>
    {blok.body.map((nestedBlok) => (
      <StoryblokComponent blok={nestedBlok} key={nestedBlok._uid} />
    ))}
  </main>
);
export default Page;
    

In the same “components” folder, create a new file, “Container.jsx”, for the schemas created in the “Container” block. 

        
      
import { storyblokEditable } from "@storyblok/react";
const Container = ({ blok }) => {
  return (
    <div {...storyblokEditable(blok)}>
      <div>{blok.author}</div>
      <div>{blok.blog}</div>
    </div>
  );
};
export default Container;
    

Now, create a custom hook called “useStoryblokContent.js” inside your “hooks” folder to initialize the Storyblok bridge and fetch your story. You will need the following imports:

        
      
import {
  storyblokInit,
  apiPlugin,
  useStoryblok
} from "@storyblok/react";
import Page from "./components/Page.jsx";
import Container from "./components/Container.jsx";
    
  • storyblokInit initializes the Storyblok bridge. It takes in 3 arguments in a JSON object format (“accessToken”, “use”, and “components).
  • apiPlugin is a function from the Storyblok SDK which you can parse to “use” under StoryblokInit to fetch your content data.
  • useStoryblok will help you fetch your story from the Storyblok CDN API. It takes in 2 arguments to help it know the path to your story. The first argument it takes is the slug of your story and the second argument is the version to fetch in a JSON object format {version: “published”}.

Initialize the Storyblok bridge:

        
      
function useStoryblokContent () {
  storyblokInit({
    accessToken: 'YOUR-ACCESS-TOKEN',
    use: [apiPlugin],
    components: {
      page: Page,
      container: Container,
    },
  });
  const story = useStoryblok(
    'blogs',
    { version: 'published' } // Or draft
  );
  return { story };
};
export default useStoryblokContent;
    

In the App component, import the new custom hook, retrieve your content from the hook, and render it with StoryblokComponent:

        
      
import { StoryblokComponent } from '@storyblok/react';
import useStoryblokContent from './hooks/useStoryblokContent';

function App () {
  const {story} = useStoryblokContent();
  if (!story || !story.content) {
    return <div>Just a sec...</div>;
  }
  return <StoryblokComponent blok={story.content} />;
};
export default App;
    

In the next section, we’ll add authentication to the App component before rendering content from Storyblok.

Integrating Firebase and Storyblok

In this section, we will integrate Firebase and Storyblok to build a serverless authentication process before rendering content. With Firebase providing the backend solution and Storyblok providing the CMS, we have the tools needed to manage and deliver content efficiently and securely with the following steps:

Step 1: Fetch the needed imports for authentication into App.jsx

        
      
import { useState } from "react";
import firebase from "firebase/compat/app";
import useFirebaseAuth from "./hooks/useFirebaseAuth";
    

Step 2: Empty the contents of your App component. Take out the Firebase functions you created out of the useFirebaseAuth() hook and set the states for email and password authentication

        
      
const { signInWithEmailAndPassword, signOut, user } = useFirebaseAuth();
const { story } = useStoryblokContent();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [hasAccount, setHasAccount] = useState(true);
const [error, setError] = useState(null);
    

Step 3: Create a function to handle signing in

        
      
function handleSignIn(event) {
    event.preventDefault();
    signInWithEmailAndPassword(email, password)
      .then(() => {
        setEmail('');
        setPassword('');
        setError(null);
        setHasAccount(true);
      })
      .catch((error) => {
        setError(error.message);
      });
}
    

Step 4: Create a function to handle signing out

        
      
function handleSignOut() {
    signOut();
}
    

Step 5: Create a function to handle signing up

        
      
function handleSignUp(event) {
    event.preventDefault();
    firebase
      .auth()
      .createUserWithEmailAndPassword(email, password)
      .then((user) => {
        setEmail('');
        setPassword('');
        setError(null);
      })
      .catch((error) => {
        setError(error.message);
      });
};
    

Step 6: Return the JSX component provided below. We first check if a user is signed in before rendering the content from Storyblok. Next, we also make sure the content exists. If a user isn’t logged in, the user will be presented with a sign in or sign up form

        
      
return (
    <>
      {user ? (
        <>
          {!story || !story.content ? (
            <div>Just a sec...</div>
          ) : (
            <>
              <StoryblokComponent blok={story.content} />
              <button type="button" onClick={handleSignOut}>
                Sign out
              </button>
            </>
          )}
        </>
      ) : hasAccount ? (
        <>
          {error && <p>{error}</p>}
          <form onSubmit={handleSignIn}>
            <input
              type="email"
              value={email}
              placeholder="Email"
              onChange={(event) => setEmail(event.target.value)}
            />
            <input
              type="password"
              value={password}
              placeholder="Password"
              onChange={(event) => setPassword(event.target.value)}
            />
            <button type="submit">Sign in</button>
            <p>
              Don't have an account?{' '}
              <u onClick={() => setHasAccount(false)}>SignUp!</u>
            </p>
          </form>
        </>
      ) : (
        <>
          {error && <p>{error}</p>}
          <form onSubmit={handleSignUp}>
            <input
              type="email"
              placeholder="Email"
              value={email}
              onChange={(event) => setEmail(event.target.value)}
            />
            <input
              type="password"
              placeholder="Password"
              value={password}
              onChange={(event) => setPassword(event.target.value)}
            />
            <button type="submit">Sign Up</button>
            <p>
              Already have an account?{' '}
              <u onClick={() => setHasAccount(true)}>SignIn!</u>
            </p>
          </form>
        </>
      )}
    </>
);

    

Tips and best practices

Here are some tips and best practices for building a serverless architecture with Firebase and Storyblok:

  • Use clear naming conventions for blocks and schemas. This will make it easier to manage and keep track of your content.
  • Cache data on the client side for improved performance.
  • All new blocks you create inside a Storyblok story are nestable, meaning you can create a chain of blocks within blocks.
  • The “Asset” access level of Storyblok access tokens gives full access to create, update and delete content outside the Storyblok interface.
  • Ensure your API keys, access tokens, and other sensitive information are stored in a “.env” file.
  • Whenever you make changes in Storyblok’s visual editor, you can quickly save the changes with ctrl+s or cmd+s.

Conclusion

We have learned about Firebase and Storyblok, two powerful tools for managing and authenticating data. By combining them, you can create a database system that provides a seamless user experience and security for content management. You can also do more with both tools, such as using Firebase Dynamic Links to create deep links that direct users to specific content within Storyblok, using Firebase Analytics to track user interactions and behaviour in Storyblok and more. If you're interested in taking your front-end application to the next level, learn more about the Storyblok React SDK with this cheatsheet.

Author

Goodluck Woha

Goodluck Woha

Goodluck Woha is a software developer who specializes in frontend development and integration with technologies such as serverless and content management systems.