UID property is "null" in GET response to the Strapi REST API

System Information
  • Strapi Version: 4.1.1
  • Operating System: Mac OS X Monterey 12.2.1
  • Database: SQLite3 5.0.2
  • Node Version: 14.15.3
  • NPM Version: 7.5.3
  • Yarn Version: 1.22.10

Hello!

I have set-up a Content-Type which makes use of the UID field, named “slug” (takes its value from a Text field). In the Content Manager view of the Admin Dashboard, the slug shows up and is populated correctly.

When I send a request to the API, it returns all the entries correctly, except for the “slug”, which is null'. I would be very thankful for any pointers on how to address this problem.

Could you share your schema.json file for the given content-type?

Got the same behaviour. Seems to be a bug: Adding UID with attached field to existing Collection Type returns null · Issue #11665 · strapi/strapi · GitHub

Workaround: Edit the generated field in the content manager, save, edit again to restore generated value and save again. Then the value is written to the DB and also returned by the API.

2 Likes

I think, I have the idea how to properly create many slugs without manually updating each of them in content manager, but it’s still a temporary workaround (I use Next.js).

The gist is to update the slug fields through your custom network request (I’m using GraphQL in Strapi). The steps are:

  1. Set permission in Strapi to update the collection, where your slug is, if you haven’t already (Settings → Users & Permissions Plugin → Roles → Public → name of your collection with slugs → check “update”).
  2. Install npm i slufigy
  3. Create an empty page with useEffect hook, to launch the code on page reload
  4. In useEffect, create a function. Inside of if, fetch() your collection to get ids and titles, that we need to slugify (don’t forget to set the pagination limit in GraphQL query, because by default Strapi returns 10 entries max, if I recall correctly).
  5. In .then() statement, create an array with ids and slugified titles.
  6. Then, update the slugs in collection through multiple requests (Strapi doesn’t update entries in batch, so we need to use Promise.all())

My code:

import { useEffect } from 'react'
import slugify from 'slugify'

export default function Page() {

  useEffect(() => {
    
    // fetch ids and titles
    async function fetchData() {
      await fetch(`http://localhost:1337/graphql`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          query: `
            query {
              products(pagination: { limit: 100 }) {
                data {
                  id
                  attributes {
                    title
                  }
                }
              }
            }
          `
        })
      })
        .then(r => {
          if (r.status >= 400) {
            return r.json().then(errResData => {
              const err = new Error('Error')
              err.data = errResData
              throw err
            })
          }
          return r.json()
        })
        .then(async data => {

          const arrWithTitles = data.data.products.data

          // create an array with slugified titles, using slugify
          const arrWithSlugifiedTitles = arrWithTitles.map(i => {

            const slugifiedTitle = slugify(i.attributes.title, { lower: true, remove: /[*+~.()'"!:@]/g })

            return { id: i.id, slug: slugifiedTitle }
          })

          // update each slug entry through many requests with Promise.all
          await Promise.all(arrWithSlugifiedTitles.map(async i => {

            return await fetch('http://localhost:1337/graphql', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                query: `
                  mutation updateProduct {
                    updateProduct(id: "${i.id}", data: { Slug: "${i.slug}" }) {
                      data {
                        id
                        attributes {
                          Slug
                        }
                      }
                    }
                  }
                `
              })
            })
              .then(r => {
                if (r.status >= 400) {
                  return r.json().then(errResData => {
                    const err = new Error('Error')
                    err.data = errResData
                    throw err
                  })
                }
                return r.json()
              })
              .then(data => console.log(data))
              .catch(err => console.error(err))
          }))
        })
        .catch(err => console.error(err))
    }

    fetchData()
  }, [])

  return (
    <div></div>
  )
}

This worked for me, I see slugs in API response.

Thank you for your repsonse!
Is this a bug worth to be reported?

Looks like a fix is being worked on in the above mentioned GH issue (Adding UID with attached field to existing Collection Type returns null · Issue #11665 · strapi/strapi · GitHub) but for now I’ve created a bulk update script to generate slugs for every entry in my collection:

I found this service: strapi/uid.js at main · strapi/strapi · GitHub

In a custom service:

  async updateCreditCardsSlug() {
    const items = await strapi.entityService.findMany(
      "api::item.item",
      {
        fields: ["id", "name"],
      }
    );
    const pendingUpdateItems = items.map(async (item) => {
      const slug = await this.generateUIDField({
        contentTypeUID: "api::item.item",
        field: "slug",
        data: item,
      });

      return strapi.entityService.update(
        "api::item.item",
        item.id,
        {
          data: {
            slug,
          },
        }
      );
    });
    const updatedItemsResults = await Promise.allSettled(
      pendingUpdateItems
    );

    const updatedItems = updatedItemsResults.filter(
      (result) => result.status === "fulfilled"
    );
    const failedItems = updatedItemsResults.filter(
      (result) => result.status === "rejected"
    );

    return {
      updatedItems: updatedItems.length,
      failedItems: failedItems.length,
    };
  },

I couldn’t figure out how to import or get access to that uid service so I copied the code from that service and included those functions on my custom service. Would be better to import it.