DEV Community

Cover image for Flatbread πŸ₯ͺ - consume relational, flat-file data using GraphQL in any static framework
Anthony Gushu
Anthony Gushu

Posted on

Flatbread πŸ₯ͺ - consume relational, flat-file data using GraphQL in any static framework

TLDR; I built a framework-agnostic tool that gives you the markdown/YAML file -> GraphQL server experience of Gridsome/Gatsby.

Problem

Storing your relational data as files in a git repo, deployed onto a static site target (Netlify, Vercel, etc.) can be an incredibly powerful pattern with the only running cost being the price of the domain name.

I love using NetlifyCMS for generating that data as markdown files, then querying it from a compile-time GraphQL API into a static site. However, I found that when you go this direction, you're typically pigeonholed into using Gatsby, Gridsome, or Hugo - unless you have very simple, non-relational data. This has the consequence of dictating many aspects of your code and effectively vendor-locks you into the strong opinions of those frameworks.

Those frameworks are great and tons of work has gone into them, but I wanted total freedom to use something like SvelteKit, Remix, or Astro while having the same flat file -> local GraphQL server experience.

So as it goes, I built something to scratch my own itch...

Enter, Flatbread

I'm using it for a little tea log that's currently in the process of migrating from Gridsome to SvelteKit + Flatbread. Hopefully others find this useful! πŸ˜„

Installation

pnpm i flatbread@latest
Enter fullscreen mode Exit fullscreen mode

Automatically create a flatbread.config.js file:

npx flatbread init
Enter fullscreen mode Exit fullscreen mode

If you're lookin for different use cases, take a peek through the various packages to see if any of those plugins fit your needs. You can find the relevant usage API contained therein.

Take this example where we have a content folder in our repo containing posts and author data:

content/
β”œβ”€ posts/
β”‚  β”œβ”€ example-post.md
β”‚  β”œβ”€ funky-monkey-friday.md
β”œβ”€ authors/
β”‚  β”œβ”€ me.md
β”‚  β”œβ”€ my-cat.md
...
flatbread.config.js
package.json
Enter fullscreen mode Exit fullscreen mode

In reference to that structure, set up a flatbread.config.js in the root of your project:

import { defineConfig, markdownTransformer, filesystem } from 'flatbread';

const transformerConfig = {
  markdown: {
    gfm: true,
    externalLinks: true,
  },
};
export default defineConfig({
  source: filesystem(),
  transformer: markdownTransformer(transformerConfig),

  content: [
    {
      path: 'content/posts',
      collection: 'Post',
      refs: {
        authors: 'Author',
      },
    },
    {
      path: 'content/authors',
      collection: 'Author',
      refs: {
        friend: 'Author',
      },
    },
  ],
});
Enter fullscreen mode Exit fullscreen mode

Now hit your package.json and put the keys in the truck:

// before
"scripts": {
  "dev": "svelte-kit dev",
  "build": "svelte-kit build",
},

// after becoming based and flatbread-pilled
"scripts": {
  "dev": "flatbread start -- svelte-kit dev",
  "build": "flatbread start -- svelte-kit build",
},
Enter fullscreen mode Exit fullscreen mode

The Flatbread CLI will capture any script you add in after the -- and appropriately unite them to live in a land of fairies and wonder while they dance into the sunset as you query your brand spankin new GraphQL server however you'd like from within your app.

Run that shit πŸƒβ€β™€οΈ

pnpm run dev
Enter fullscreen mode Exit fullscreen mode

Construct queries πŸ‘©β€πŸ³

If everything goes well, you'll see a pretty graphql endpoint echoed out to your console by Flatbread. If you open that link in your browser, Apollo Studio will open for you to explore the schema Flatbread generated. Apollo Studio has some nice auto-prediction and gives you helpers in the schema explorer for building your queries.

You can query that same endpoint in your app in any way you'd like. Flatbread doesn't care what framework you use.

Query arguments

The following arguments are listed in their order of operation.

filter

Each collection in the GraphQL schema can be passed a filter argument to constrain your results, sifting for only what you want. Any leaf field should be able to be used in a filter.

The syntax for filter is based on a subset of MongoDB's query syntax.

filter syntax

A filter is composed of a nested object with a shape that matches the path to the value you want to compare on every entry in the given collection. The deepest nested level that does not have a JSON object as its value will be used to build the comparison where the key is the comparison operation and value is the value to compare every entry against.

Example

filter = { postMeta: { rating: { gt: 80 } } };

entries = [
  { id: 1, title: 'My pretzel collection', postMeta: { rating: 97 } },
  { id: 2, title: 'Debugging the simulation', postMeta: { rating: 20 } },
  {
    id: 3,
    title: 'Liquid Proust is a great tea vendor btw',
    postMeta: { rating: 99 },
  },
  { id: 4, title: 'Sitting in a chair', postMeta: { rating: 74 } },
];
Enter fullscreen mode Exit fullscreen mode

The above filter would return entries with a rating greater than 80:

result = [
  { id: 1, title: 'My pretzel collection', postMeta: { rating: 97 } },
  {
    id: 3,
    title: 'Liquid Proust is a great tea vendor btw',
    postMeta: { rating: 99 },
  },
];
Enter fullscreen mode Exit fullscreen mode

For more details, please check out the docs in the Github repo: https://github.com/tonyketcham/flatbread

πŸ”– Sidenotes

This is very much in Alpha and there are a couple quirks that need ironing out.

  • Handling cross-collection, mongoDB-style filtering.

    • That type of filtering is currently supported when done within a single collection, such as fetching a single post with a matching slug or title field, or all posts with a publish date greater than or equal to a week ago.
    • This library is ESM-centric, and while it also exports a CJS bundle, I haven't yet tested strongly in environments outside of Vite or Svelte(Kit). So while the plan is to test integration with all sorts of frameworks, all I can currently promise is that it's framework-agnostic by design and should work with all sorts of static-enabled things like Astro, Remix, Next, Nuxt, etc.

I'd love to find more people to work on Flatbread with me as I'm coming up against some interesting challenge areas and feel there's tons of potential here to disrupt this niche of the ecosystem

Top comments (0)