ƒ(m)(o)(b)(b).dev

      

GatsbyJS + Firebase + Cloudinary + Netlify

GatsbyJS + Firebase + Cloudinary + Netlify

I want to show you how I managed to build this blog easy and without hassle.

During my winter break, I decided to get more social and spend more time on Twitter since I think it is the place where developers share stuff. I wasn't wrong. I quickly followed as many developers as I found and started to take the time to write answers or tweet something. Then #100DaysOfGatsby hit me and I was ready with my first idea to make something - a tiny blog.

I had used Gatsby before and I really love how easy you can get up and running and do all kinds of cool stuff thanks to the ecosystem and all of the plugins the community produces (I plan to make one in the future). I wanted a fast, simple, static site, so Gatsby is the #1 choice. Check!

The setup

Gatsby

npx gatsby new mobb.dev


We are all set here! Nice and clean.

Firebase

So the base is clear React + GatsbyJS, but where to store my articles? My first choice was Firebase and I DON'T regret it! All you need to do is make an account then navigate to the Console and create a new project, next, next, next and you are ready.

I am using the Realtime Database here. I haven't tried the Firestore yet, but I think Realtime DB is good enough to store some articles' data. So from the Console choose Database and create Realtime Database (you should start it in test mode). The next thing you need to do is to go to the Project settings (the ⚙️ icon next to the Project Overview) and in Your apps panel add a new web app. Give it a name and hit Register. Now you have the configuration to connect to the DB. Get back to the console and navigate to Service accounts in the settings. Choose Firebase Admin SDK and Node.js and hit Generate new private key (you will find out why we are doing this later). After you generate the key, store it and move on.

To recap:

  1. Create a Firebase account and create a new project
  2. Create a Realtime Database from the Database section of the console and start it in test mode.
  3. Navigate to Project settings -> Your apps and create a new web app.
  4. Navigate to Service accounts -> Firebase Admin SDK -> Node.js and generate a new private key.


Connect Firebase to React App

My goal was to not only make a blog but to make a blog with a simple CMS so I can easily add articles. So I have to be able to send information to Firebase and read from it also. Now, this is where Gatsby plugins enter the game.

gatsby-plugin-firebase is the right thing to install.

npm install firebase gatsby-plugin-firebase


It is advisable to use .env for the credentials so dotenv is required also

npm install -D dotenv


As per the docs of the plugin make .env file and store all credentials you received from Project settings -> Your apps

Finally, add the plugin to your gatsby-config.js file and restart your dev environment (don't forget to add require("dotenv").config() at the top).

So far so good, but then I realized that as this plugin is really good I don't need a realtime connection with my DB. I need this to be as static as possible with no external calls for feeding information to my main page. Also, I wanted to make calls to the DB only when building the site, because of the limits that the Firebase free plan offers. According to this, there can't be more than 100 simultaneous connections to the DB and I didn't want to take my chances.

So I needed to use the data layer of Gatsby. It is implemented with GraphQL and there are plugins in the ecosystem that connect different kinds of data sources to the GraphQL in Gatsby.  From the docs:

"Gatsby’s data layer lets you pull data from these (and any other source) directly into your components—in the shape and form you want."

Gatsby's source plugins are meant to supply data from different sources into the Gatsby data layer.

gatsby-source-firebase is the right choice for this. This is where the private key from Firebase gets used. According to docs, you should store it in .json file in the root folder. However, I prefer to store it in my .env file so it won't get committed to the repo (believe me I went through that situation). So in your .env file add:

...
FIREBASE_PRIVATE_KEY:<contents of the json file>
...


Now initialize the plugin in your gatsby-config.js

{
 resolve: `gatsby-plugin-firebase`,
 options: {
   credential: JSON.parse(process.env.FIREBASE_PRIVATE_KEY),
   databaseURL: process.env.GATSBY_FIREBASE_DATABASE_URL,
   types: [<Add your types>]
 }
}


Restart your development server and visit http://localhost:8000/___graphql and you should see the newly generated nodes based on the types you provided.

Cloudinary

Lastly I wanted to be able to put some photos in my articles, so I needed a place to store them. One way to do it is to store every photo in your project and then compress it and manipulate it directly in you project. You can use gatsby-image and generate images on build time. This way every image is local and you won't need to load it from external source. I have used this approach on other projects and it is really nice. You can use features like:

  • lazy loading
  • image optimization
  • webp generation
  • thumbnails generation
  • etc.

Check out the documentation if you want to explore this concept further.

The other way is to use external service to store your images and then load them from there. Firebase has storage plans, but thanks to Wes Bos and his reaaaally good Advanced React Course I found out about Cloudinary which has a very generous free plan and also you can manipulate your images! So let's make some setup.

After creating an account navigate to the console and then to Settings (⚙️ in the header) -> Upload -> scroll down to Upload Presets and add a new one. There you can set all the manipulations you want. You can also set all kinds of goodies like Upload Control, Storage Access, etc., but for the sake of this article, we are not going to explore these options. Give your preset a name and underneath set it to Unsigned then save it.

We are all set here. Now let's write some code!

The implementation

Feed

First, let's make our Feed which is the main thing in our blog.

We are going to feed it with data from our GraphQL layer. Since I am using React hooks a lot and I also want my feed to be in separate component it is best to use Gatsby's static query to supply data to our component.

So our Feed.js

import React from 'react';
import { useStaticQuery, graphql } from 'gatsby';

export default () => {
 const data = useStaticQuery(graphql`
   query articles {
     // Get it from your graphiql console. Below is mine implementation
     allArticles {
       edges {
         node {
          id
          title
         }
       }
     }
   }
 `);

 return (
   <div>
     // Map over them
     {data.allArticles.edges.map(article => console.log(article.node.title))}
   </div>
 );
};


Now you can use the data and make your Article Preview component.

Next thing to do is to make a single article page. Let's see...

Gatsby exposes an API so you can programmatically create pages from GraphQL data. In your gatsby-node.js do something like this:

const path = require(`path`);

exports.createPages = async ({ graphql, actions }) => {
 const { createPage } = actions;

 const result = await graphql(`
   query {
     allArticles {
       edges {
         node {
           id
           title
           url
         }
       }
     }
   }
 `);

 result.data.allArticles.edges.forEach(({ node }) => {
   if (node.type === 'authored') {
     createPage({
       path: node.url, // This is the slug. I generate it when the article is added via the CMS. More on this later.
       component: path.resolve(`./src/templates/article.js`), // This is the template to use. We will make it later.
       context: {
         title: node.title,
       },
     });
   }
 });
};


Next thing to do is the template ./src/templates/article.js

import React from 'react';
import { graphql } from 'gatsby';

export default ({ data }) => {
 return (
   <div>{data.articles.title}</div>
 );
};

// Here we are getting the data to be used in the component.
// Gatsby takes care to inject it.
// We are filtering it by the url so we can extract the information for the specific url only, not all articles.
// The url comes from the gatsby-node.js implementation

export const query = graphql`
 query($url: String!) {
   articles(url: { eq: $url }) {
     id
     title
   }
 }
`;


So far so good. We are feeding our components with the data from our Firebase which is stored in GraphQL and all is statically generated at build time. Cool!

Now we need to make the our pseudo CMS so we can add articles.

Create new page in pages folder and call it add.js. Now we need to use gatsby-plugin-firebase to live connect to our database so we can send information and store it. After a quick look at the documentation we are going to use FirebaseContext

import React from 'react';
import { FirebaseContext } from 'gatsby-plugin-firebase';

export default () => {
 // This is the state of our new article
 const [state, setState] = React.useState({
   id: Date.now(),
   title: '',
   content: '',
   url: '',
   image: ''
 });

 // Here we init the connection to the DB
 const firebase = React.useContext(FirebaseContext);

 // Function to generate slug based on title
 const genSlug = text =>
   text
     .toLowerCase()
     .replace(/\s+/g, '-') // Replace spaces with -
     .replace(/[^\w-]+/g, '') // Remove all non-word chars
     .replace(/--+/g, '-') // Replace multiple - with single -
     .replace(/^-+/, '') // Trim - from start of text
     .replace(/-+$/, '');

 // Save to DB
 const save = e => {
   e.preventDefault();

   firebase
     .database()
     .ref(`articles/${state.id}`) // here you should point to your field where you store data. Mine is articles.
     .set(state)
     .then(() => {
       // Reset the local state when the article is published
       setState({
         id: Date.now(),
         title: '',
         content: '',
         url: '',
         image: ''
       });
     );
 }

 // Handle input
 const handleInput = e => {
   if (e.target.name === 'title') {
     const slug = genSlug(e.target.value);
     setState({ ...state, url: slug, [e.target.name]: e.target.value });       
   } else {
     setState({ ...state, [e.target.name]: e.target.value });       
   }
 }

 return (
   <form onSubmit={save}>
     <input name="title" value={state.title} onChange={handleInput} />
     <textarea name="content" value={state.content} onChange={handleInput}></textarea>
     <button type="submit">Save</button>
   </form>
 );
};


This is the way to publish to Firebase. Now we need to add input for the image. Let's add this.

...

export default () => {
 ...
// Handle Image Upload
 const handleImage = async e => {
   const data = new FormData();
   const file = e.target.files[0];
   data.append('file', file);
   data.append('upload_preset', '<name of your preset>');
// this field is to specify which preset to handle the image
   const res = await fetch(
     `https://api.cloudinary.com/v1_1/<your-id>/image/upload`,
// your-id is in the top right corner in the cloudinary console
     {
       method: 'POST',
       body: data,
     }
   );
   const cloudinaryFile = await res.json();
   setState({ ...state, image: cloudinaryFile.secure_url });
 };


 return (
   <form onSubmit={save}>
<input type="file" onChange={handleImage} />
     <input name="title" value={state.title} onChange={handleInput} />
     <textarea name="content" value={state.content} onChange={handleInput}></textarea>
     <button type="submit">Save</button>
   </form>
 );
};


This is only the basic implementation for adding data to Firebase and Cloudinary. You can go ahead and implement draft.js for article content, you can add authorization layer using Firebase, and you can add dropzone.js for files. You can check the repo of this website in github for these things (there is a link in the end of this article).

Add some articles then restart your dev server and you should see them generated in your feed. You can add a Gatsby <Link> with the slug so you can navigate to the single page.

Finally...

Host it on Netlify

Netlify is the best place to host you statically generated website. Easy to work with and deploying is done under 5 minutes!

Go ahead and register. Connect your github repo and then add some configurations. (Or you can build locally and directly drag-and-drop your build folder there, so if you prefer this way you can skip the next steps).

We only need to migrate our .env file to Netlify's build options. Search for Environment in Build configuration and add all of your env variables there. Aaaand you are done! Netlify will automatically recognize your static site generator and all of the configuration is done so you only need to save and deploy.

If you want to go further you can buy domain and connect it to your build. It is easy-peasy and blazing fast!

Here is my repo if you want to take a look at my implementation.

Since I am mostly on twitter you can check me out there @mobbdev .
Like, retweet and comment the tweet for this article. Feedback is greatly appreciated!

Thank you!

Powered by