I posted my frustration a week ago on X about how I lost my sanity studio code base. So yesterday morning, I finally finished reverse engineering the code base, lol. But guess what? I ended up spending the whole day again, failing to figure out a bug while trying to fetch the URL of a document in one of my data sets.
{
name: "file",
title: "File",
type: "file",
},
The file
object contains metadata relating to a file. However, when I was trying to get item.file.asset.url
, it returns undefined
. I wasn't aware that this was due to the structure of data I was receiving from the sanityClient.
Today that is when I realized that the default behavior of sanityClient fetch is that it does not deeply populate references, so if the file
is referencing another object asset
that has a property URL
, it won't be populated in fetch.
And to fetch deeply in Sanity, I have to explicitly state it in my query:
export async function fetchAllResources() {
try {
const query = `*[_type == "resources"]{ ..., file-> }`;
...
}
...
}
Here, file->
indicates that fetch should populate the file
reference.
In this scenario, I was now able to directly ask for the url of the file in the query itself.
So I decided to share with you some more technical ways around nested data structures with sanity.
Always remember: understanding the data structure you are working with is critical, and make sure to use correct fetching methods for dealing with references in databases like Sanity.
So lets get technical!
Fetching Deeply
Sanity.io is a platform for structured content that comes with an open-source editing environment, Sanity Studio, that you can customize with JavaScript and a real-time hosted data store . This write up will guide you through the process of fetching deeply nested data from Sanity.io, which can be a common requirement when working with complex data structures.
Understanding The Structure of Sanity.io
In Sanity.io, data is stored as documents in a dataset. Each document contains a set of fields, which can be of different types such as string, number, boolean, array, and object. In addition, one important type is the reference type, which allows linking between different documents (Source 3).
For example, a resources
document might look like this:
{
_id: "resource1",
_type: "resources",
title: "Resource Title",
mainImage: { /* image object */ },
description: "Resource description",
file: { /* file object */ },
Year: "2020-03-27T11:30:00.000Z",
publisher: "Publisher Name",
tags: ["tag1", "tag2"],
status: "active",
category: "forms"
}
Fetching Data
Sanity.io uses Groq (Graph-Relational Object Queries) as its primary query language. To fetch data from Sanity, you need to send a query to Sanity's API endpoint. This can be done manually by constructing a URL with your project ID, dataset name, and query, or by using a Sanity client library .
Here is an example of how to fetch all resources
documents using the Sanity client in JavaScript:
const query = `*[_type == "resources"]`;
const response = await sanityClient.fetch(query);
This will return an array of resources
documents.
Fetching Deeply Nested Data
In many cases, the data you need is nested within fields that are arrays or objects, or behind references. You need to specify these fields in your query in order to fetch them.
Fetching Nested Fields
If a field is an array or an object, you can fetch its nested fields by specifying them in your query. For example, to fetch the url
field of the file
object in each resources
document, you can use this query:
const query = `*[_type == "resources"]{..., file{..., asset->}}`;
const response = await sanityClient.fetch(query);
In this query, the ...
operator is used to fetch all other fields in the resources
documents and in the file
objects. The asset->
part is used to dereference the asset
field in the file
objects.
Fetching References
References in Sanity.io allow you to link between different documents. To fetch the data of a document that is referenced in another document, you need to dereference the reference field using the ->
operator (Source 3).
For example, if the file
field in the resources
documents is a reference to file
documents, you can fetch the url
field of the referenced file
documents like this:
const query = `*[_type == "resources"]{..., file->}`;
const response = await sanityClient.fetch(query);
Best Practices for Fetching Data in Sanity.io
Here are some best practices for fetching data in Sanity.io:
Be specific in your queries: Only fetch the fields you need. This can improve the performance of your queries and reduce the amount of data transferred.
Handle errors: Always include error handling in your fetch operations. This can help you identify issues with your queries and prevent your application from crashing if a fetch operation fails.
Use the Sanity client libraries: Sanity provides client libraries for several programming languages that make it easier to fetch data from Sanity. These libraries also provide additional features such as caching and real-time updates.
Be aware of CORS: When fetching data from a web browser, you need to make sure that the origin of your web page is allowed by Sanity's CORS (Cross-Origin Resource Sharing) settings. You can add origins in the API settings of your Sanity project
Keep learning about Groq: The Groq query language used by Sanity is very powerful and flexible. The more you learn about Groq, the more efficient your queries can be. You can find more information about Groq
Common Bugs and Solutions when Fetching Data in Sanity.io with Next.js
When using Sanity.io with Next.js, you might encounter various bugs, especially when fetching different data types. This section will discuss some of these common issues and how to rectify them.
1. Incorrect Configuration of Sanity Client
The Sanity client must be correctly configured with the correct project ID, dataset name, and token. If these values are incorrect, you will encounter errors when trying to fetch data. The project ID and dataset name can be found in your Sanity project settings on the Sanity.io website, and the token can be generated there as well.
Here is an example of how to correctly configure the Sanity client in Next.js:
import { createClient } from "next-sanity";
const client = createClient({
projectId: "your_project_id",
dataset: "your_dataset_name",
useCdn: process.env.NODE_ENV === "production",
token: "your_token",
});
2. Incorrect GROQ Query Syntax
When fetching data from Sanity.io, you need to use the GROQ query language. If your GROQ query syntax is incorrect, you will encounter errors. For instance, when fetching a document by its slug, you should use the correct syntax in your query.
Here is an example of how to correctly fetch a document by its slug in Next.js:
const slug = "your_slug";
const query = `*[_type == "post" && slug.current == $slug][0]`;
const post = await client.fetch(query, { slug });
3. CORS Issues
When fetching data from Sanity.io in a Next.js application, you might encounter CORS (Cross-Origin Resource Sharing) issues. This can happen if you are making requests to Sanity.io from a different origin (URL) than the one configured in your Sanity project settings. To solve this, you need to add your Next.js application's origin to the CORS origins list in your Sanity project settings.
4. Module Not Found
Sometimes, you might encounter a "Module not found" error. This can happen if you try to import a module that doesn't exist or if there's a problem with your project's configuration. To solve this, make sure all your imports are correct and your project is properly configured .
5. Problems with Next.js's Concurrent Features
Next.js 12 introduced new concurrent features that are still in alpha. Using these features with Sanity can cause Next.js to hang. This is due to an issue where the window
object is not defined. Unfortunately, there is currently no known solution for this issue, and you might need to avoid using Next.js's concurrent features with Sanity until a fix is available .
In conclusion, when fetching data from Sanity.io in a Next.js application, it's important to correctly configure the Sanity client, use the correct GROQ query syntax, handle CORS issues, ensure all imports are correct, and be aware of potential issues with new features in Next.js.