How to convert a graphql response to csv
- Step 1Save the GraphQL response — Capture the JSON from your GraphQL client, Postman, or
curl. It will look like{ "data": { "<query>": { "edges": [ { "node": {...} } ] } } }. - Step 2Extract the edges array first — Use json-path-extractor with a path like
$.data.orders.edges(substitute your query name) so the buried array becomes the top-level list. This is the key step — without it the whole response flattens to one row. - Step 3Drop the extracted array onto this converter — The extracted
edgesarray is a JSON array of{ node, cursor }objects. Drop it onto the converter above. Free files cap at 2 MB. - Step 4Keep flattening on — Flatten nested objects (default) turns
node.customer.emailand similar into columns. Each edge'scursoralso gets its own column for pagination reference. - Step 5Choose array handling for node lists — Nodes often contain arrays (
tags,lineItems). Pick JSON literal to keep them re-parseable or Comma joined for readable cells. - Step 6Convert and download — Click Convert to CSV, confirm one row per node, then download Excel-Ready CSV (BOM + CRLF) for spreadsheets or plain CSV for code.
GraphQL connection structure and what to extract
The path to extract before converting, by API. Substitute your actual query name.
| API / query | Response path to the array | Extract this |
|---|---|---|
| Shopify orders | data.orders.edges | $.data.orders.edges |
| GitHub issues | data.repository.issues.edges | $.data.repository.issues.edges |
| Generic connection | data.<query>.edges | $.data.<query>.edges |
| Non-connection list | data.<query> is already an array | $.data.<query> |
How an edge flattens
Each element of the extracted edges array becomes a row.
| Edge field | CSV column | Note |
|---|---|---|
node.id | node.id | The resource id |
node.customer.email | node.customer.email | Deeper node nesting still flattens |
cursor | cursor | Pagination cursor for the edge |
node.tags (array) | node.tags | One cell, per Array values setting |
Cookbook
GraphQL connection responses and the CSV produced after extracting the edges array.
Shopify orders connection
ExampleExtract $.data.orders.edges first, then convert. Each edge becomes a row with node fields flattened.
Extracted edges array:
[
{ "node": { "id":"gid://o/1", "customer": {"email":"a@x.com"} }, "cursor":"c1" }
]
Output CSV:
node.id,node.customer.email,cursor
gid://o/1,a@x.com,c1Without extraction — the common mistake
ExampleDropping the whole response in flattens everything to one row, because the edges array is buried under named keys, not at the top level.
Input (whole response):
{ "data": { "orders": { "edges": [ {"node":{"id":1}}, {"node":{"id":2}} ] } } }
Wrong output (one wide row):
data.orders.edges
"[{...},{...}]"
Fix: extract $.data.orders.edges first → then 2 rows.Optional node sub-fields aligned
ExampleOne node requested an extra field; the union header keeps the column and leaves the other empty.
Extracted edges:
[
{ "node": { "id":1, "total":50 } },
{ "node": { "id":2 } }
]
Output CSV:
node.id,node.total
1,50
2,Node tags joined for readability
ExampleA tags array inside the node reads better comma-joined in a spreadsheet cell.
Extracted edges:
[ { "node": { "id":1, "tags":["vip","eu"] } } ]
Array values = Comma joined:
node.id,node.tags
1,"vip, eu"GitHub issues to CSV
ExampleExtract the issues edges, then flatten author and labels-count style fields into columns.
Extracted $.data.repository.issues.edges:
[ { "node": { "number":42, "title":"Bug", "author":{"login":"ada"} } } ]
Output CSV:
node.number,node.title,node.author.login
42,Bug,adaErrors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Whole response flattens to one row
By designThe edges array is nested under data.<query> named keys, not the top level, so the converter sees one object and emits one wide row. Extract $.data.<query>.edges first with json-path-extractor.
`node` and `cursor` both present per edge
SupportedEach edge object flattens both node.* and cursor into columns. Keep cursor if you need pagination reference; drop it afterward in your spreadsheet if not.
Errors array alongside data
By designA partial GraphQL response includes a top-level errors array beside data. Extract the edges path you want — the errors array is ignored once you target the data path. Review errors separately if some nodes are null.
Some nodes are null (partial errors)
PreservedIf a node resolved to null, that edge contributes empty cells. The converter keeps the row; check the GraphQL errors array to understand why a node was null.
Nested connection inside a node
Not expandedA node containing its own connection (e.g. node.lineItems.edges) stays as a JSON literal in one cell. Extract that inner path separately for a row-per-line-item layout.
Only one page of a paginated query
ExpectedA single response holds one page (limited by first/after). Concatenate pages before converting if you need the full dataset, using each edge's cursor to paginate.
Response over the free 2 MB limit
BlockedFree conversions cap at 2 MB / 500 rows. Large connection results need Pro (100 MB / 100,000 rows) or page-by-page conversion.
Invalid JSON
Invalid JSONA truncated or malformed response errors with Invalid JSON. Re-fetch the full body and validate with json-validator if needed.
Frequently asked questions
Why does my whole GraphQL response become one row?
Because the edges array is nested under data.<query>, not at the top level. Extract $.data.<query>.edges first with json-path-extractor, then convert that array for one row per node.
What path do I extract for Shopify or GitHub?
Shopify orders: $.data.orders.edges. GitHub issues: $.data.repository.issues.edges. In general $.data.<query>.edges, substituting your query name.
Will the node fields become columns?
Yes. With flattening on, node.customer.email and other nested node fields each get a dot-notation column. The cursor for each edge also becomes a column.
Do I have to keep the cursor column?
No. It is included because it is part of each edge, but you can delete the cursor column in your spreadsheet if you only want node data.
What if some nodes are null?
Null nodes contribute empty cells but keep their row. Check the GraphQL errors array in the original response to understand why a node failed to resolve.
How do I handle a connection nested inside a node?
Inner connections stay as a JSON literal in one cell. Extract that inner edges path separately and convert it for a row-per-element layout.
Will optional node sub-fields break my columns?
No. The union-of-keys header includes every field requested across all nodes, leaving empties where a node lacks one.
Is my GraphQL data uploaded?
No. Parsing and conversion run in your browser; the response never reaches a server.
How do I get all pages, not just one?
GraphQL connections paginate with first/after and the per-edge cursor. Fetch and concatenate pages before converting, since one response holds only one page.
What about arrays inside a node like tags?
They go into one cell. Use Comma joined for readable text or JSON literal to keep them re-parseable. Arrays are never split into rows.
How big a response can I convert?
Free: 2 MB / 500 rows. Pro: 100 MB / 100,000 rows. Convert page by page for very large connections.
Can I convert the CSV back to GraphQL-shaped JSON?
You can convert CSV to a flat JSON array with csv-to-json, then reshape with json-unflattener to rebuild nesting from dot-notation columns.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.