GraphQL Markdown
Write markdown, generate GraphQL TypesDefs & Resolvers, query via GraphQL, and serve as html.🔥
GraphQL Markdown is a simple library that parses and converts your .md
files into html
and automatically generates GraphQL FieldDefinitions
from its frontMatter content which can then be queried via a GraphQL server.🔥
After generating the FieldDefinitions
, we save the processed data to an in-memory db. We export the generated TypeDefs
and Resolvers
to enable you to have complete control over creating your own GraphQL Schema.🎉
A simple GraphQL API for querying your processed content is provided. Which includes a way to sort
, limit
, and skip
your results enabling a basic form of pagination. 🍾
Table of Contents
Quick Start
NOTE: This package is still in development and is not yet published to the npm repository.
npm install --save https://github.com/okgrow/graphql-markdown
yarn add https://github.com/okgrow/graphql-markdown
Basic example
---
id: myFirstMdFile
groupId: homePage
---
# Hello World!
Welcome to this pale blue dot!
import express from 'express';
import graphqlHTTP from 'express-graphql';
import { makeExecutableSchema } from 'graphql-tools';
import { runGraphqlMarkdown } from '@okgrow/graphql-markdown';
const options = {
contentRoot: `${__dirname}/content`,
};
const app = express();
(async () => {
try {
const {
typeDefs,
resolvers,
fileCount,
} = await runGraphqlMarkdown(options);
console.log(
`Loaded \n${fileCount} ContentItems into our in memory DB!`,
);
const schema = makeExecutableSchema({
typeDefs: typeDefs,
resolvers: resolvers,
});
app.use(
'/graphiql',
graphqlHTTP({
schema,
graphiql: true,
}),
);
app.listen(4000);
console.log('Server Started! http://localhost:4000/graphiql');
} catch (error) {
console.error('[runGraphqlMarkdown]', error);
}
})();
Options
Below are all the options you may pass to runGraphqlMarkdown
.
const options = {
contentRoot: `fullpath/to/root/of/content`,
imageResolver: ({ imgPath, contentRoot }) => {...},
replaceContents: ({ contentRoot, rawContents }) => {...},
imageFormats: '(png|svg)',
debugMode: true,
syntaxHighlighter: (code) => {...},
};
contentRoot (Required)
The fullpath of where your .md
content is located.
imageResolver (Optional)
imageResolver
expects a function that will return the URI for the image. By default images are converted to base64 if no function is assigned to imageResolver.
NOTE: You shouldn't use base64 in production as it increases the size of images significantly.
const serveImagesFromServer = ({ imgPath, contentRoot }) =>
`/images${imgPath.slice(contentRoot.length)}`;
replaceContents (Optional)
replaceContents
is an optional function that if provided will be called against your .md
content in order to replace the static content at run time. See below example for usage & signature.
const replaceWords = ({ contentRoot, rawContents }) =>
rawContents.replace(new RegExp('deployment-server', 'g'), `${isProduction ? 'production' : 'development'}`);
imageFormats (Optional)
Image formats that we should process. If not provided we will use the DEFAULT_SUPPORTED_IMAGE_FORMATS
.
imageFormats
expects a string in the following format '(ext|ext|ext|..etc)', e.g '(png|svg)'
.
debugMode (Optional)
To enable additional logging during development. Default is false
.
syntaxHighlighter (Optional)
syntaxHighlighter
can be used to provide runGraphqlMarkdown
with a function to syntax highlight your content. Simple example below using highlight.js
.
import hljs from 'highlight.js';
const yourCodeHighlighter = (code) => hljs.highlightAuto(code).value;
Querying Your Data
GraphQL Markdown provides a few different approaches to querying the data extracted from your .md
files.
- 💪 A powerful & all purpose query: Search by logical
AND
, OR
conditions on any fields!!! 🎉
contentItems(filter: FilterFields!, pagination: Pagination): [ContentItem!]
- 🎁 Simplified helper queries: providing a clean & crisp syntax for common querying patterns. 🎊
contentItemById(id: ID!): ContentItem
contentItemsByIds(ids: [ID!]!, pagination: Pagination): [ContentItem!]
contentItemsByGroupId(groupId: ID!, pagination: Pagination): [ContentItem!]
- 🎀 Organise the query results: Simple syntax to
sort
, skip
, and limit
your results. 🎈
enum OrderBy {
ASCENDING
DESCENDING
}
input Sort {
sortBy: String!
orderBy: OrderBy!
}
input Pagination {
sort: Sort
skip: Int
limit: Int
}
Markdown files
A markdown file contains two distinct sections, the FrontMatter section and the Markdown section.
---
FrontMatter Section
---
Markdown Section
FrontMatter section
The FrontMatter section contains key
:value
pairs. Every Markdown file is required to contain a FrontMatter section and must contain an id
and an groupId
key-value pair. You can add as many additional key
:value
pairs as you like, we will generate GraphQL Field Definitions
from these additional key
:value
at runtime. Check out the typeDefs.graphql file to see where we inject these Field Definitions
.
---
id: myFirstMdFile
groupId: homePage
---
Markdown section
The Markdown section is placed after the FrontMatter section and contains your markdown content. The markdown content will be converted into valid HTML when we process all markdown files and store them in memory.
# My First Markdown file
Hello World!
Putting it all together
---
id: home-content-section-1
groupId: homePage
type: pageContent
title: Wonder Website
description: Wonder Website - Home Page
date: "2017-12-25"
tags: [Happy, Learnings]
---
# Welcome to this wonderful website!
Hello world!
Thanks for dropping by to say hello! 🔥🔥🔥
Seeing it in Action!
Run the simple-example found in examples/simple
, and copy/paste the below snippet into GraphiQL to see the response yourself!🔥
{
contentItemById(id: "homePage") {
html
description
}
contentItemsByIds(ids: ["graphqlIntro", "homePage"]) {
id
tags
order
}
contentItemsByGroupId(
groupId: "simple-example"
pagination: {
sort: {
sortBy: "order",
orderBy: DESCENDING
}
skip: 0
limit: 0
}
) {
id
order
groupId
}
contentItems(
filter: {
AND: {
order: 2,
groupId: "simple-example"
}
}
pagination: {
sort: {
sortBy: "order",
orderBy: ASCENDING
}
}
) {
id
type
date
groupId
description
}
}
Advanced Example
This example will be showing all the possible options and features of this package.
import hljs from 'highlight.js';
import { makeExecutableSchema } from 'graphql-tools';
import { runGraphqlMarkdown } from '@okgrow/graphql-markdown';
const isProduction = process.env.NODE_ENV === 'production';
const serveImagesFromServer = ({ imgPath, contentRoot }) =>
`/images${imgPath.slice(contentRoot.length)}`;
const replaceWords = ({ contentRoot, rawContents }) =>
rawContents.replace(new RegExp('deployment-server', 'g'), `${isProduction ? 'production' : 'development'}`);
const yourCodeHighlighter = (code) => hljs.highlightAuto(code).value;
const options = {
contentRoot: `fullpath/to/root/of/content`,
imageResolver: isProduction ? serveImagesFromServer : null,
replaceContents: replaceWords,
imageFormats: '(png|svg)',
debugMode: true,
syntaxHighlighter: yourCodeHighlighter,
},
};
(async () => {
try {
const {
typeDefs,
resolvers,
fileCount,
} = await runGraphqlMarkdown(options);
console.log(`DB ready!\n${fileCount} ContentItems loaded!`);
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
} catch (error) {
console.error('[runGraphqlMarkdown]', error);
}
})();
Testing
git clone git@github.com:okgrow/graphql-markdown.git
npm install
npm test
Examples
Check out the examples folder to see how it all works. Please note:
- Node version 8+ is required.
- You must run
npm install
on the main package first as the examples import the /dist
files. - Examples contain detailed instructions & example queries to copy paste into Graphiql.
Maintainers
This is an open source package. We hope to deal with contributions in a timely manner, but that's not always the case. The main maintainers are:
@cfnelson
@okgrow
Feel free to ping if there are open issues or pull requests which are taking a while to be dealt with!
Contributing
Issues and Pull Requests are always welcome.
Please read our contribution guidelines.
If you are interested in becoming a maintainer, get in touch with us by sending an email or opening an issue. You should already have code merged into the project. Active contributors are encouraged to get in touch.
Please note that all interactions in @okgrow's repos should follow our Code of Conduct.
License
Released under the MIT license © 2017 OK GROW!.