Published on

Customize Strapi plugin services

Authors

Strapi is a great Headless CMS which can be heavily customized. You can write your own plugin to extend Strapi's functionality or you can even extend other plugins.

You can extend a plugin's server interface with the ./src/extensions folder as described in the documentation.

Let's look at an example on how to customize only a small portion of the plugin's server interface by overwriting a service.

Generating a different slug for the built-in UID field

The content-manager plugin is part of Strapi core and also defines the UID field. It can create a slug based on another field and validates it's uniqueness. To create the slug it uses slugify. Let's pass it different options.

The call to slugify is in the function generateUIDField in services.uid.js.

Let's overwrite this function by creating the file ./src/extensions/content-manager/strapi-server.js:

module.exports = (plugin) => {
  const uidService = plugin.services.uid({ strapi });
  plugin.services.uid = ({ strapi }) => ({
    ...uidService,
    async generateUIDField({ contentTypeUID, field, data }) {
      const contentType = strapi.contentTypes[contentTypeUID];
      const { attributes } = contentType;

      // changed const to let, so I can overwrite separator option
      let { targetField, default: defaultValue, options } = attributes[field];
      const targetValue = _.get(data, targetField);
      // added this
      options = {
        ...options,
        separator: "_",
      };

      if (!_.isEmpty(targetValue)) {
        return this.findUniqueUID({
          contentTypeUID,
          field,
          value: slugify(targetValue, options),
        });
      }

      return this.findUniqueUID({
        contentTypeUID,
        field,
        value: slugify(defaultValue || contentType.modelName, options),
      });
    },
  });

  return plugin;
};

In the case of the content-manager plugin the services are a function returning an object of functions. Therefor to only overwrite one function I need to call the function to get the object with functions back and wrap my implementation also in a function. But that's not the case for every plugin.

Also for this case of passing different options to slugify there would be an easier option. You can pass the options in the schema.json:

"slug": {
  "type": "uid",
  "targetField": "name",
  "required": true,
  "options": {
    "separator": "_"
  }
}

I'm writing a book on how to build a content aggregator website with Next.js, MongoDB & Tailwind CSS. Subscribe to follow along.