acf-json-to-graphql
v0.1.1
Published
CLI tool that converts ACF JSON sync files into WPGraphQL query fragments
Maintainers
Readme
acf-json-to-graphql
CLI tool that reads your ACF JSON sync folder and generates WPGraphQL query fragments as .graphql files.
Why
WPGraphQL's GraphiQL IDE has a query composer that can generate fragments for you — but it requires a running WordPress instance, and you have to manually re-compose every time a field group changes.
This tool automates that: it reads ACF's local JSON files directly and regenerates all fragments in one command. No browser, no clicking through the composer, no running WP required. When your field groups change regularly, npm run gen keeps your fragments in sync and the diffs are reviewable in version control.
Install
npm install -D acf-json-to-graphqlUsage
Add a script to your package.json:
"scripts": {
"gen:fragments": "acf-json-to-graphql --path ./acf-json --out ./src/graphql/fragments --all"
}Then run:
npm run gen:fragmentsOr run interactively to pick specific field groups:
npx acf-json-to-graphql --path ./acf-json --out ./src/graphql/fragments--path defaults to ./acf-json if omitted. Without --out, output is printed to stdout.
Output
Each field group generates a .graphql file with a named fragment typed to the graphql_types configured in ACF:
# Homepage (auto-generated by acf-json-to-graphql)
fragment HomepageFields on Page {
homepage {
introduction {
contentIntroduction
}
summary {
... on HomepageSummaryColumnLayout {
columnTitle
}
}
}
}Relationship fields
Relationship and post_object fields fall back to { id slug } by default. To control what fields are queried on related post types, create an acf-json-to-graphql.config.ts (or .js) in your project root:
import { defineConfig } from "acf-json-to-graphql";
export default defineConfig({
relationshipFragments: {
project: `
title
excerpt
projectFields {
year
}
`,
post: `
title
date
`,
},
});Recommended setup
This tool fits into a two-step pipeline:
ACF JSON → acf-json-to-graphql → .graphql fragments → GraphQL Code Generator → TypeScript types1. Generate fragments (this tool)
"scripts": {
"gen:fragments": "acf-json-to-graphql --path ./acf-json --out ./src/graphql/fragments --all"
}2. Generate TypeScript types (GraphQL Code Generator)
Install:
npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operationscodegen.ts:
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "https://your-site.local/graphql",
documents: "src/graphql/fragments/**/*.graphql",
generates: {
"src/graphql/types.ts": {
plugins: ["typescript", "typescript-operations"],
},
},
};
export default config;Add to your scripts:
"gen:types": "graphql-codegen",
"gen": "npm run gen:fragments && npm run gen:types"Run npm run gen to regenerate everything in one shot.
3. Auto-regeneration on field group save
Add this to your theme's functions.php to regenerate fragments automatically whenever a field group is saved in the WP admin:
add_action('acf/update_field_group', function(array $field_group): void {
$project_root = '/absolute/path/to/your/project';
try {
exec("cd {$project_root} && npm run gen 2>&1", $output, $exit_code);
if ($exit_code !== 0) {
throw new \RuntimeException(implode("\n", $output));
}
} catch (\Throwable $e) {
error_log('acf-json-to-graphql failed: ' . $e->getMessage());
}
});Local dev only. This requires Node.js to be available on the same machine as WordPress (Laragon, MAMP, etc.). Do not use this on a production server.
Programmatic API
import { scanAcfJson, convertFieldGroup, defineConfig } from "acf-json-to-graphql";
const config = defineConfig({ relationshipFragments: { project: "title" } });
const groups = await scanAcfJson("./acf-json");
for (const group of groups) {
const fragment = convertFieldGroup(group, config);
console.log(fragment);
}Notes
- Requires ACF fields to have Show in GraphQL enabled and a
graphql_field_nameset. - Inline fragment type names follow WPGraphQL's naming convention (
{GroupContext}{LayoutName}Layout). - The
graphql_typesfield on the ACF group determines the fragment'son <Type>— defaults toNodeif absent.
