How to refactor deskStructure with async/await for better readability?

7 replies
Last updated: Feb 9, 2022
I am setting up the
deskStructure
based on user role administrator and editors. But I am wondering is there a cleaner and more readable way to write the function like using
async
and
await
?
This is not so readable, wondering if there is a better way doing this?

return userStore
    .getCurrentUser()
    .then(({ roles, name }) => {
      const roleNames = roles.map((r) => r.name);

      if (roleNames.includes("administrator")) {
        return S.list()
          .title(`${name} — Website Admin Menu`)
          .items([...deskItems, settingsMenu]);
      }

      if (roleNames.includes("editors")) {
        editors.push(...deskItems);
      }

      return S.list().title("Website Menu").items(deskItems);
    })
    .catch((error) => {
      console.error(error);
      return S.list().title("Empty or Faulty structure").items([]);
    });

AI Update

Yes! You can absolutely use async/await to make your deskStructure function more readable. The Structure Builder accepts functions that return promises, so you can use modern async/await syntax instead of chaining .then() calls.

Here's how you can refactor your code:

export default defineConfig({
  // ... other config
  plugins: [
    structureTool({
      structure: async (S) => {
        try {
          const { roles, name } = await userStore.getCurrentUser();
          const roleNames = roles.map((r) => r.name);

          if (roleNames.includes("administrator")) {
            return S.list()
              .title(`${name} — Website Admin Menu`)
              .items([...deskItems, settingsMenu]);
          }

          if (roleNames.includes("editors")) {
            editors.push(...deskItems);
          }

          return S.list().title("Website Menu").items(deskItems);
        } catch (error) {
          console.error(error);
          return S.list().title("Empty or Faulty structure").items([]);
        }
      }
    })
  ]
})

The key changes:

  1. Make the structure function async by adding the async keyword
  2. Use await instead of .then() to unwrap the promise from getCurrentUser()
  3. Replace .catch() with a try/catch block for error handling

This approach is much more readable and follows modern JavaScript patterns. The Structure Builder API supports both promise chains and async/await since it just expects a function that returns a promise or a synchronous return value.

You could also extract this into a separate function if you want to keep your config file cleaner:

async function createStructureForUser(S) {
  try {
    const { roles, name } = await userStore.getCurrentUser();
    const roleNames = roles.map((r) => r.name);

    if (roleNames.includes("administrator")) {
      return S.list()
        .title(`${name} — Website Admin Menu`)
        .items([...deskItems, settingsMenu]);
    }

    if (roleNames.includes("editors")) {
      editors.push(...deskItems);
    }

    return S.list().title("Website Menu").items(deskItems);
  } catch (error) {
    console.error(error);
    return S.list().title("Empty or Faulty structure").items([]);
  }
}

export default defineConfig({
  plugins: [
    structureTool({
      structure: createStructureForUser
    })
  ]
})

Both approaches work equally well – choose whichever fits your code organization preferences better!

Show original thread
7 replies
Hey User. I think you should be able to convert that code to use async/await, and it will continue to work 🙂.
I generally prefer async/await over promise chaining myself, too! I've used it a lot in my Studios and haven't run into any issues.
user E
Is there a example on git? Because this is not readable. Downside of being a perfectionist… always refactoring and trying to keep it clean and split code in parts.
I'm the same, although in my experience it's sometimes good to let go and just ship code that works 😅.
How you structure your Studio code is really up to you. The examples from our docs work for most folks, but you are free to abstract them however makes sense to you. The aim of the docs is to show how our APIs work without misdirecting folks with fancy abstractions.

The block of code you posted can be rewritten using async/await like this:


try {
  const { roles, name } = await userStore.getCurrentUser()
  const roleNames = roles.map(r => r.name)

  if (roleNames.includes('administrator')) {
    return S.list()
      .title(`${name} — Website Admin Menu`)
      .items([...deskItems, settingsMenu])
  }

  if (roleNames.includes('editors')) {
    editors.push(...deskItems)
  }

  return S.list().title('Website Menu').items(deskItems)
} catch {
  console.error(error)
  return S.list().title('Empty or Faulty structure').items([])
}
I haven't tested this code, but it
should work.
user E
Thanks! Yes, it's true have to let it go if it works, it works no need to refactor. But sometimes it's pretty hard👍
user E
I get Unexpected reserved word ‘await’. (47:28)

My DeskStructure.JS:

import S from "@sanity/desk-tool/structure-builder";

import userStore from "part:@sanity/base/user";

import {
  pagesMenu,
  articlesMenu,
  casesMenu,
  friendsMenu,
  vacanciesMenu,
  expertiseMenu,
  themeMenu,
  categoriesMenu,
  commentsMenu,
  downloadsMenu,
} from "./desk/menus";

import { settingsMenu } from "./desk/settings";

const deskStructure = () => {
  const editors = [];

  const deskItems = [
    pagesMenu,
    S.divider(),
    articlesMenu,
    S.divider(),
    casesMenu,
    S.divider(),
    friendsMenu,
    S.divider(),
    vacanciesMenu,
    S.divider(),
    categoriesMenu,
    S.divider(),
    themeMenu,
    S.divider(),
    expertiseMenu,
    S.divider(),
    commentsMenu,
    S.divider(),
    downloadsMenu,
    S.divider(),
  ];

  try {
    const { roles, name } = await userStore.getCurrentUser();
    const roleNames = roles.map((r) => r.name);

    if (roleNames.includes("administrator")) {
      return S.list()
        .title(`${name} — Website Admin Menu`)
        .items([...deskItems, settingsMenu]);
    }

    if (roleNames.includes("editors")) {
      editors.push(...deskItems);
    }

    return S.list().title("Website Menu").items(deskItems);
  } catch {
    console.error(error);
    return S.list().title("Empty or Faulty structure").items([]);
  }

  // return userStore
  //   .getCurrentUser()
  //   .then(({ roles, name }) => {
  //     const roleNames = roles.map((r) => r.name);
  //     const isAdmin = roleNames.includes("administrator");
  //     const isEditor = roleNames.includes("editors");

  //     if (isAdmin) {
  //       return S.list()
  //         .title(`${name} — Website Admin Menu`)
  //         .items([...deskItems, settingsMenu]);
  //     }

  //     if (isEditor) {
  //       editors.push(...deskItems);
  //     }

  //     return S.list().title("Website Menu").items(deskItems);
  //   })

  //   .catch((error) => {
  //     console.error(error);
  //     return S.list().title("Empty or Faulty structure").items([]);
  //   });
};

export default deskStructure;

Ah! You need to make your
deskStructure
function async. You can do that by adding the
async
keyword here:

const deskStructure = async () => {
user E
Nice one, only thing noticed the catch (error) js not working anymore:

} catch (error) {
    console.error(error);
    return S.list().title("Empty or Faulty structure").items([]);
  }
ERROR

Error: Pane returned no child

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?