Best practices for managing data fetched from an API in Sanity

42 replies
Last updated: Apr 20, 2021
Hey there, in you experience with sanity what’s the best way to manage something that can be fetched from an api? Eg. Category order from an e-commerce? I’m trying to get category dynamically from another api and if it’s possibile set documents as child of a singleton document. Any tips?
Apr 20, 2021, 10:49 AM
Hey User 👋
I think there are 2 parts to this:
1. getting the data from the other API before rendering items (an async structure).
This snippet may help you with this part.2. displaying the categories from that data
For 2, what is your goal there? Will documents also live in Sanity and be enhanced with data in the studio? Or do you simply want to list categories without altering them?

If you'll enhance them, your general approach seems quite sound, but you may want to re-think the
S.document().documentId("categoriesSettings")
portion. The way it's currently structured would make so every document has the same
_id
, essentially rendering the same data. Maybe you can do
.documentId("external-category" + element.toLowerCase())
to provide unique _ids in Sanity anchored in the original category?
I also think you'll probably need a
S.list( ) for rendering the categories as children of the "Gestione Categorie"
listItem()
... Hope this helps! Let me know how I can be of further service 🙂
Apr 20, 2021, 11:07 AM
Hey User thank you. My goal is to fetch the categories that I already have in my db (with all vehicles) and handle them in sanity (eg, ordering of products, specific texts, other stuff)
Apr 20, 2021, 11:09 AM
Gotcha! So you're in the right track. Here's some pseudo code that could help you out:
S.listItem()
  .title('Gestione Categorie')
  .child(async () => {
    const categories = await getCategories()
    return S.list().id("categories").items(
      categories.forEach((element) => {
        S.document()
          .title(element)
          .schemaType('categoriesSettings')
          // Unique _id for each category
          .documentId(`category-${element.toLowerCase().replace(/\s/, "-")}`);
      })
    )
  }
  ),
Apr 20, 2021, 11:13 AM
See how in line 3 we're using an async function as the
child
of your listItem? That's how you can always get fresh data 🙂
Apr 20, 2021, 11:13 AM
oh yes, clear. But the fresh data is got when the sanity studio starts? or in build time?
Apr 20, 2021, 11:14 AM
When the editor opens that specific listItem menu - AKA when they click "Gestione Categorie"
Apr 20, 2021, 11:16 AM
wow this is super amazing man
Apr 20, 2021, 11:16 AM
If you want to reduce the amount of querying done to your db, you can cache the data in a global variable, too, just make sure to burst it every once in a while. Some editors tend to leave Sanity open for days at a time 🙂
Apr 20, 2021, 11:17 AM
lol yes sure
Apr 20, 2021, 11:19 AM
so to be clear
Apr 20, 2021, 11:19 AM
this code
const categories = ['city-car', 'suv', 'mini-van'];
export default () =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('Gestione Categorie')
        .child(async () => {
          // const categories = await getCategories();
          return S.list()
            .id('categories')
            .items(
              categories.forEach((element) => {
                S.document()
                  .title(element)
                  .schemaType('categoriesSettings')
                  // Unique _id for each category
                  .documentId(
                    `category-${element.toLowerCase().replace(/\s/, '-')}`
                  );
              })
            );
        }),
      // Add a visual divider (optional)
      S.divider(),
      // List out the rest of the document types, but filter out the config type
      ...S.documentTypeListItems().filter(
        (listItem) => !['categoriesSettings'].includes(listItem.getId())
      ),
    ]);
Apr 20, 2021, 11:19 AM
should rendere that 3 categories right?
Apr 20, 2021, 11:19 AM
cause S.list renders the childern
Apr 20, 2021, 11:20 AM
Yes! Good for testing, right? Then you can "graduate" into the async function when you're ready for it
Apr 20, 2021, 11:20 AM
But nothing has been shown
Apr 20, 2021, 11:20 AM
I will try to dig in to it
Apr 20, 2021, 11:20 AM
I can try it myself in a bit, maybe I typed something wrong in my example. Let me know if you can't fix it
Apr 20, 2021, 11:23 AM
nope buddy
Apr 20, 2021, 12:05 PM
can’t find the issue 😕
Apr 20, 2021, 12:05 PM
i’ve tried this
return S.list()
            .id('categories')
            .items(
              ...categories.map((element) => {
                return S.document()
                  .title(element)
                  .schemaType('categoriesSettings')
                  .documentId(
                    `category-${element.toLowerCase().replace(/\s/, '-')}`
                  );
              })
            );
Apr 20, 2021, 12:24 PM
but i got
items
must be an array of items
Apr 20, 2021, 12:24 PM
yey I got the results!!!!
Apr 20, 2021, 12:33 PM
🎉
Now I realized we were usuing .forEach in the example above instead of map. Typos...
😅
Apr 20, 2021, 12:35 PM
yeah
Apr 20, 2021, 12:35 PM
this is my solution
Apr 20, 2021, 12:35 PM
import S from '@sanity/desk-tool/structure-builder';

const categories = ['city-car', 'suv', 'mini-van'];
export default () =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('Gestione Categorie')

        .child(async () => {
          // const categories = await getCategories();
          return S.list()
            .id('categories')
            .items(
              categories.map((element) => {
                return S.documentListItem()
                  .id(`category-${element.toLowerCase().replace(/\s/, '-')}`)
                  .title(element)
                  .schemaType('categoriesSettings');
              })
            );
        }),
      // Add a visual divider (optional)
      S.divider(),
      // List out the rest of the document types, but filter out the config type
      ...S.documentTypeListItems().filter(
        (listItem) => !['categoriesSettings'].includes(listItem.getId())
      ),
    ]);
Apr 20, 2021, 12:35 PM
i’m just figuring out how to specify the id for documentListItem
Apr 20, 2021, 12:36 PM
You can replace the `.id(
category-...
portion of it with the desired template you want 🙂
Apr 20, 2021, 1:07 PM
I got it buddy 🙂
Apr 20, 2021, 1:07 PM
thanks for your help really apreciated
Apr 20, 2021, 1:07 PM
You got it! Excited to see what you build with it, User 🙌
Apr 20, 2021, 1:12 PM
I will show the final result 🙂 of course!
Apr 20, 2021, 1:12 PM
Man
Apr 20, 2021, 2:27 PM
I’ve made a mistake, I should return document instead of listItem inside the first listItem
Apr 20, 2021, 2:27 PM
is this possibile?
Apr 20, 2021, 2:27 PM
something like
Apr 20, 2021, 2:28 PM
export default () =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('Gestione Categorie')
        .child(async () => {
          const { data: categories } = await getCategories();
          // const categories = await getCategories();
          S.list()
            .id('categories')
            .items(
              categories?.map((element) => {
                S.document()
                  .title(carTypes[element])
                  .schemaType('categoriesSettings')
                  // Unique _id for each category
                  .documentId(
                    `category-${element.toLowerCase().replace(/\s/, '-')}`
                  );
              })
            );
        }),
      // Add a visual divider (optional)
      S.divider(),
      // List out the rest of the document types, but filter out the config type
      ...S.documentTypeListItems().filter(
        (listItem) => !['categoriesSettings'].includes(listItem.getId())
      ),
    ]);
Apr 20, 2021, 2:28 PM
but nothing is rendered
Apr 20, 2021, 2:28 PM
probably i found the solution
Apr 20, 2021, 2:44 PM
Failed prop type: The prop `type` is marked as required in `DeskToolPane`, but its value is `undefined`.
Apr 20, 2021, 2:44 PM
but i can’t figure out this
Apr 20, 2021, 2:44 PM
export default () =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('Gestione Categorie')
        .child(async () => {
          const { data: categories } = await getCategories();
          return categories?.map((element) => {
            return S.document()
              .title(carTypes[element])
              .documentId(`category-${element.toLowerCase()}`)
              .schemaType('categoriesSettings');
          });
        }),
      // Add a visual divider (optional)d
      S.divider(),
      // List out the rest of the document types, but filter out the config type
      ...S.documentTypeListItems().filter(
        (listItem) => !['categoriesSettings'].includes(listItem.getId())
      ),
    ]);
Apr 20, 2021, 2:49 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?