The standard process for converting MD to HTML in Gatsby is for complete files on the local system.
I need to convert a specific field, NOT a file. The top related question is this one, but as you can see that is using Contentful, which now provides a plugin to solve it.
The API I am using (Airtable) is returning a field that contains Markdown.
The recommended solution is to convert using a resolver...but I cannot fully understand the Gatsby docs on the topic.
The error I see when building is this:
UNHANDLED REJECTION Airtable.FAQ provided incorrect OutputType:
'Object({ resolve: [function resolve], extensions: Object({ createdFrom: "createResolvers" }) })'
I think I am close, but I don't know if I am meant to create a new type or actually what the resolver is meant to return...a new field?
The field in question is FAQ
, which you can see in this example of a query in the GraphQL explorer:
query MyQuery {
allAirtableManufacturer(filter: {data: {Premium: {eq: true}}}) {
edges {
node {
data {
Premium
Manufacturer
Premium_Manufacturers {
recordId
data {
FAQ
Downloads_File_Name
Is_Supplier
}
internal {
type
}
}
}
recordId
queryName
}
}
}
}
My understanding is that the resolver can/should add a new field with the markdown content converted to html.
So here is my resolver code, where you can see I am trying to add a field called "FAQ_html" to the Airtable node:
createResolvers({
Airtable: {
FAQ_html: {
resolve(source: any, args: any, context: any, info: any) {
return remark().use(html).processSync(source.data.FAQ).contents
},
},
}
})
My gatsby-config for airtable is:
resolve: `gatsby-source-airtable`,
options: {
apiKey: process.env.AIRTABLE_API_KEY,
concurrency: 5,
tables: [
{
baseId: `appP5vBdAitw6yyDH`,
tableName: `Manufacturers`,
queryName: `Manufacturer`,
tableView: `AppView_Details_DONOTCHANGE`,
tableLinks: [`Premium_Manufacturers`],
separateNodeType: true,
defaultValues: {
Company_Description: '',
},
},
{
baseId: `appP5vBdAitw6yyDH`,
tableName: 'Premium_Manufacturers',
tableLinks: [`Manufacturers`],
},
],
The table 'Premium_Manufacturers' is obviously linked as a child to 'Manufacturers'.
However, when I explore in GraphQL I also see them appear as a top-level node called 'Airtable', which I did not expect. You can see that here:
allAirtable {
edges {
node {
data {
FAQ
Downloads_File_Name
Last_update
Is_Supplier
Section_Name
Section_No
Name
Cell_Number
Email
Rep_Name
Technical_Rep_Name
Consolidated_Rep
}
}
}
}
That's why my resolver uses 'Airtable' as the name of the node, but clearly it does not work.
I also tried changing the config to provide a separate node:
{
baseId: `appP5vBdAitw6yyDH`,
tableName: 'Premium_Manufacturers',
queryName: 'Premium',
separateNodeType: true,
tableLinks: [`Manufacturers`],
},
so now 'allAirtable' becomes 'allAirtablePremium'.
I tried changing the resolver to use that:
createResolvers({
allAirtablePremium: {
FAQ_html: {
resolve(source: any, args: any, context: any, info: any) {
return remark().use(html).processSync(source.data.FAQ).contents
},
},
}
})
But that throws a warning:
warn `createResolvers` passed resolvers for type `allAirtablePremium` that doesn't exist in the schema.
So clearly it doesn't like the 'all', so then I changed it to remove the 'all' like so:
createResolvers({
AirtablePremium: {
FAQ_html: {
resolve(source: any, args: any, context: any, info: any) {
return remark().use(html).processSync(source.data.FAQ).contents
},
},
}
})
And I am back to the original error, which at least tells me there is something wrong with what I am trying to return (as the error clearly says 'wrong OutputType').
So what should that be returning?
Thanks in advance for any help!
UPDATE 1
The code now compiles, but I don't see my custom field.
This is the schema customisation (just create FAQ_HTML as an empty string to start off):
import { GatsbyNode, CreateSchemaCustomizationArgs } from 'gatsby'
export const createSchemaCustomization: GatsbyNode['createSchemaCustomization'] = async ({
actions,
}: CreateSchemaCustomizationArgs) => {
const { createFieldExtension, createTypes } = actions
createFieldExtension({
name: "FAQ_HTML",
extend() {
return ''
},
})
const typeDefs = `
type airtablePremium implements Node {
FAQ_HTML: String @FAQ_HTML
}
`
createTypes(typeDefs)
}
The resolver is now like this:
createResolvers({
airtablePremium: {
FAQ_HTML: {
resolve(source: any, args: any, context: any, info: any) {
console.log("SOURCE IS", source)
const faqHtml = remark().use(html).processSync(source.FAQ).contents
console.log("faqHtml IS", faqHtml)
return faqHtml;
},
},
},
})
It compiles but the field does not show up in the GraphQL explorer. No console output either.
After a nice chat with @rmcsharry, we confirmed that you'd need to create the type first.
The biggest issue was the type name. In the code, it should be
AirtablePremium
vsairtablePremium
. You can find out the type name in GraphiQL by clicking on the field name in the left column, or holding cmd and clicking on the field in your query.You'd use the typename in
createResolvers
as well:Update from @rmcsharry
After @Derek Nguyen's very helpful assistance, I found an alternative and easier solution.
If you want to avoid the hassle of creating types, you can go with a much simpler solution using the plugin markdown-to-jsx.
Then you simply wrap the incoming markdown field:
and it creates a react JSX component on the fly. :)