How does Sanity's GraphQL endpoint return Portable Text as parsed JSON?

12 replies
Last updated: May 10, 2020
Hi! I was wondering, how does Sanity's GraphQL endpoint resolves Portable Text to raw (parsed) JSON? https://www.sanity.io/docs/graphql#portable-text-5ea9c0e79586 I am not an expert, and only asking for a co-worker. He only manages to send it as a JSON string, which means I have to use
JSON.parse
in the frontend to be able to pass it to
BlockContent
in my React app. We have a MongoDB, and using dotnet-graphql, although I do not know if these are relevant here. I am curious how it is done in JS anyway! If more context needed, please ask. 🙂
AI Update

The Sanity GraphQL endpoint returns Portable Text as raw JSON through a special field naming convention. When you have a Portable Text field in your schema, the GraphQL API automatically generates a <fieldName>Raw field that contains the Portable Text content as a JSON scalar type.

How it works

For example, if you have a field called body that contains Portable Text, the GraphQL schema will expose it as bodyRaw. This field returns the Portable Text as parsed JSON (not a JSON string), which means you can directly use it in your frontend without needing JSON.parse().

Here's what this looks like in a query:

{
  allPost {
    title
    bodyRaw
  }
}

The bodyRaw field will return the Portable Text structure as a proper JSON object, ready to be passed to your BlockContent component or any Portable Text renderer.

Why you might be getting a string

If you're receiving the Portable Text as a JSON string (requiring JSON.parse()), this is likely due to how your GraphQL client or server is handling the response. The issue might be:

  1. Your GraphQL client serialization: Some GraphQL clients might be stringifying the response
  2. dotnet-graphql configuration: The .NET GraphQL library might need configuration to properly handle JSON scalar types
  3. Custom scalar handling: You may need to define how the JSON scalar type should be handled in your GraphQL implementation

For .NET/GraphQL.NET specifically

In GraphQL.NET (which is commonly used with dotnet-graphql), you'll want to ensure you have proper JSON scalar support. GraphQL.NET provides a JsonGraphType that can handle arbitrary JSON data. The Sanity GraphQL API uses a JSON scalar for the *Raw fields, so your .NET client needs to understand how to deserialize this type.

If you're using Hot Chocolate (another popular .NET GraphQL library), it has built-in JsonType support that should handle this automatically.

The key issue is that the JSON scalar type in GraphQL is not a standard scalar like String or Int—it's a custom scalar that represents arbitrary JSON. Your .NET GraphQL client needs to be configured to treat this as an object/dictionary rather than a string.

Alternative: Consider GROQ

While Sanity supports GraphQL, I'd recommend considering GROQ (Sanity's native query language) instead. GROQ handles Portable Text more naturally and gives you more flexibility in how you query and transform your content. It also has type generation support, making it type-safe like GraphQL.

If you do need to stick with GraphQL, the key is ensuring your .NET GraphQL client properly handles JSON scalar types without stringifying them. You might need to configure a custom scalar type handler in your GraphQL client library.

Show original thread
12 replies
Until we have updated the Portable Text specification to be fully typed, I'm afraid that getting it as a JSON string that you have to parse in to JS objects is the way to go.
So

const doc = await fetch('GRAPHQL_ENDPOINT_WITH_QUERY')

const body = JSON.parse(doc.bodyRaw)

<BlockContent blocks={body} />
would be the way to go about it
Not sure of the implications or feasibility of it, but it would be nice if you could just pass the JSON string to the BlockContent component and have it deal with it.
☝️ This isn't totally correct. We send the data as JSON, not as a string of JSON. The bit about the typing holds true, though.
I also read this question a little differently: I think he's asking how we are managing to send the data from the GraphQL API as "untyped" JSON instead of as a string of JSON.
The way we do this is to declare a scalar called "JSON", which doesn't re-encode data. Here's one such implementation:
https://github.com/taion/graphql-type-json
If I pass it as a string wouldn't it need to rerender the whole tree as a new object will be generated each time?
Only if you re-fetch the data, but the same holds true for "pre-parsed" JSON, I guess.
Also not 100% sure how typed languages deal with a JSON scalar, so this may not be considered "best practise", but it's the best we have right now until we get properly typed GraphQL schemas for portable text fields.
Thanks, and yeah.. I am not sure if it is feasible in C# this way, but I'll forward this to our backend developer. So to type it, one must define a recursive type, I guess? I know it can be done in Typescript, we have to find out if it is a thing in C#.
I'm not sure what the best approach is in C# - let me know what you end up with though 🙂
Will do! Appreciate the help from both of you! 🙂
(Sorry about the confusion!)
No problem. :) Right now, I run JSON.parse when fetching the data. Hopefully doesn't need to do it on every render. I'll get back if we manage to solve this on the backend.

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?