
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Consume relational, flat-file data using GraphQL 🥯 inside damn near any framework.
Eat your relational markdown data and query it, too, with GraphQL inside damn near any framework (statement awaiting peer-review).
If it runs ES Modules + Node 16+, it's down to clown.
Born out of a desire to Gridsome (or Gatsby) anything, this project harnesses a plugin architecture to be easily customizable to fit your use cases.
🚧 This project is currently experimental, and the API may change considerably before v1.0. Feel free to hop in and contribute some issues or PRs!
To use the most common setup for markdown files sourced from the filesystem, Flatbread interally ships with + exposes the source-filesystem + transformer-markdown plugins.
The following example takes you through the default flatbread setup.
pnpm i flatbread@latest
Automatically create a flatbread.config.js file:
npx flatbread init
If you're lookin for different use cases, take a peek through the various
packagesto 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
In reference to that structure, set up a flatbread.config.js in the root of your project:
import { defineConfig, transformerMarkdown, sourceFilesystem } from 'flatbread';
const transformerConfig = {
markdown: {
gfm: true,
externalLinks: true,
},
};
export default defineConfig({
source: sourceFilesystem(),
transformer: transformerMarkdown(transformerConfig),
content: [
{
path: 'content/posts',
collection: 'Post',
refs: {
authors: 'Author',
},
},
{
path: 'content/authors',
collection: 'Author',
refs: {
friend: 'Author',
},
},
],
});
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",
},
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.
pnpm run dev
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.
NOTE: detecting changes to your content while Flatbread is running is not yet supported. You'll have to restart the process to get updated content.
The following arguments are listed in their order of operation.
filterEach 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 syntaxA 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.
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 } },
];
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 },
},
];
filter operationseq - equal
filterValue === resultValue in JavaScriptne - not equal
filterValue !== resultValue in JavaScriptin
filterValue.includes(resultValue) in JavaScriptnin
!filterValue.includes(resultValue) in JavaScriptincludes
resultValue.includes(filterValue) in JavaScriptexcludes
!resultValue.includes(filterValue) in JavaScriptlt, lte, gt, gte
<, <=, >, >= respectivelyexists
filterValue ? resultValue != undefined : resultValue == undefinedtrue or false as a value to compare against (filterValue)null or undefinedstrictlyExists
filterValue ? resultValue !== undefined : resultValue === undefinedtrue or false as a value to compare against (filterValue)undefinedregex
wildcard
Caveats:
Date types in filters
Date object from your client, though not extensively testedYou can union multiple filters together by adding peer objects within your filter object to point to multiple paths.
Using the entries from the previous example, let's combine multiple filters.
query FilteredPosts {
allPosts(
filter: { title: { wildcard: "*tion" }, postMeta: { rating: { gt: 80 } } }
) {
title
}
}
Results in:
result = [{ title: 'My pretzel collection' }];
sortBySorts by the given field. Accepts a root-level field name. Defaults to not sortin' at all.
orderThe direction of sorting. Accepts ASC or DESC. Defaults to ASC.
skipSkips the specified number of entries. Accepts an integer.
limitLimits the number of returned entries to the specified amount. Accepts an integer.
Check out the example integrations of using Flatbread with frameworks like SvelteKit and Next.js.
Field overrides allow you to define custom GraphQL types or resolvers on top of fields in your content. For example, you could optimize images, encapsulate an endpoint, and more!
{
content: {
...
overrides: [
{
// using the field name
field: 'name'
// the resulting type is string
// this can be a custom gql type
type: 'String',
// capitalize the name
resolve: name => capitalize(name)
},
]
}
}
basic nested objects
nested.object
a basic array (will map array values)
an.array[]
a nested object inside an array (will also map array)
an.array[]with.object
for more information in Overrides, they adhere to the GraphQLFieldConfig outlined here https://graphql-compose.github.io/docs/basics/what-is-resolver.html
fieldNameTransformAccepts a function which takes in field names and transforms them for the GraphQL schema generation -- this is used internally to remove spaces but can be used for other global transforms as well
{
...
// replace all spaces in field names with an underscore
fieldNameTransform: (fieldName) => field.name.replace(/\s/g,'_')
...
}
See CONTRIBUTING.md for the release workflow (bumping versions and publishing).
FAQs
Consume relational, flat-file data using GraphQL 🥯 inside damn near any framework.
We found that flatbread demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.