← back

Collected Notes + Gatsby

Previously I made a starter template joining 11ty and collected notes, here's the post, this time, I wanted to try it out with Gatsby, I ended up building two things, a gatsby starter and a gatsby source plugin.

You could do this in a single project but I wanted to try and make my own separate plugin

Setup

Luckily gatsby has great docs, so, I followed these instructions.

First our repo for the starter:

gatsby new gatsby-starter-collected-notes https://github.com/gatsbyjs/gatsby-starter-hello-world

Then the repo for the source plugin:

gatsby new gatsby-source-collected-notes https://github.com/gatsbyjs/gatsby-starter-plugin

Source plugin

From the plugin starter template, I removed all the other gatsby files but the gatsby-node.js and index.js.

const axios = require("axios");
const POST_NODE_TYPE = `CollectedNote`;

exports.sourceNodes = async ({
  actions,
  createContentDigest,
  createNodeId,
}, { notesOwner }) => {
  const { createNode } = actions;

  // Call the Collceted Notes API with the passed in notesOwner
  const result = await axios.get(`https://collectednotes.com/${notesOwner}.json`);
  if (result.status !== 200)
    throw new Error("Could not fetch notes from collected notes API");

  result.data.notes.forEach((note) =>
    createNode({
      ...note,
      id: createNodeId(`${POST_NODE_TYPE}-${note.id}`),
      internal: {
        type: POST_NODE_TYPE,
        contentDigest: createContentDigest(note),
      },
    })
  );

  return;
};

This is basically it, remember to install axios if you are following along (or the library that you prefer).

Starter template

The important part here is to

plugins: [
    {
      resolve: `gatsby-source-collected-notes`,
      options: {
        notesOwner: "jenaro", //the aprameter we passed in the plugin
      },
    }
  ],
const path = require("path")

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

  // We have our data available on the graphql layer because of our plugin
  const result = await graphql(
    `
      query CollectedNotesQuery {
        allCollectedNote {
          nodes {
            path
          }
        }
      }
    `
  )
  // Handle errors
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  // post template to use.
  const blogPostTemplate = path.resolve(`src/templates/blog-post.js`)

  // Create pages for each note.
  result.data.allCollectedNote.nodes.forEach(node => {
    const path = node.path
    createPage({
      path: `/blog/${path}`,
      component: blogPostTemplate,
     // this context is later passed in to the template
      context: {
        pagePath: path,
      },
    })
  })
}
import React from "react"
import ReactMarkdown from "react-markdown"
import { graphql } from "gatsby"
import Layout from "../components/Layout"
import SEO from "../components/SEO"

// here pagePath is exposed to the template because we defined it in the context.

export const query = graphql`
  query NoteQuery($pagePath: String!) {
    collectedNote(path: { eq: $pagePath }) {
      body
      created_at
      curated
      headline
      id
      path
      poster
      site_id
      title
      updated_at
      url
      user_id
      visibility
    }
  }
`

const BlogPost = ({ data, location }) => {
  if (!data) return null

  const note = data.collectedNote
  return (
    <Layout pathname={location.pathname}>
      <SEO description={note.headline} title={note.title} bodyClass="post" />
      {/* Remember to include this dependency to be able to render the markdown appropiately */}
      <ReactMarkdown source={note.body} />
    </Layout>
  )
}

export default BlogPost

And that's basically it! There you go once more, easy peasy, you got your CMS with Collected Notes and Gatsby, everything else on this template is styling or easy enough to follow along.

You can find the repositories at:

Demo at https://gatsbynotes.netlify.app