Skip to content

Instantly share code, notes, and snippets.

@ComfortablyCoding
Last active May 2, 2022 22:07
Show Gist options
  • Save ComfortablyCoding/bc05fb850226a4fb877829fb006252b5 to your computer and use it in GitHub Desktop.
Save ComfortablyCoding/bc05fb850226a4fb877829fb006252b5 to your computer and use it in GitHub Desktop.
A sample implementaion of replacing a CT findOne controller to work with a slug instead of an id for Strapi v4

Implementing findOne by slug

Create the content type

For this example we will be using the article collection content type defined below for the implementation

path: /strapi-project/src/api/article/content-types/article/schema.json
{
  "kind": "collectionType",
  "collectionName": "articles",
  "info": {
    "singularName": "article",
    "pluralName": "articles",
    "displayName": "Article"
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {},
  "attributes": {
    "title": {
      "type": "string"
    },
    "slug": {
      "type": "uid",
      "targetField": "title"
    }
  }
}

Override the Content Type Controller

We can then customize the findOne controller to accept a slug over id

path: /strapi-project/src/api/article/controllers/article.js
"use strict";

/**
 *  article controller
 */

const _ = require("lodash");
const {
  transformParamsToQuery,
  pickSelectionParams,
} = require("@strapi/strapi/lib/services/entity-service/params");
const { createCoreController } = require("@strapi/strapi").factories;

const ENTITY_UID = "api::article.article";

module.exports = createCoreController(ENTITY_UID, ({ strapi }) => ({
  async findOne(ctx) {
    const { id } = ctx.params;
    const queryParams = ctx.query || {};

    // build default query structure
    const query = transformParamsToQuery(
      ENTITY_UID,
      pickSelectionParams(queryParams)
    );

    // add slug constraint to all requests
    _.set(query, ["where", "slug", "$eq"], id);

    const entity = await strapi.db.query(ENTITY_UID).findOne(query);

    // sanitize response to ensure permissions are respected
    const sanitizedEntity = await this.sanitizeOutput(entity, ctx);

    // transform response to uniform API response format
    return this.transformResponse(sanitizedEntity);
  },
}));

Note: The where parameter here is of the WhereParameter type

Query the endpoint by slug

The endpoint can then be queried by slug instead of id

http://localhost:1337/api/articles/lorem-ipsum

which will return the following response

{
  "data": {
    "id": 1,
    "attributes": {
      "title": "lorem ipsum",
      "slug": "lorem-ipsum",
      "createdAt": "2022-04-06T03:53:11.345Z",
      "updatedAt": "2022-04-06T03:53:24.671Z",
      "publishedAt": null
    }
  },
  "meta": {}
}

A not found error will be returned if no entities for the content type match the provided slug.

strapi-plugin-slugify

If you want all of this handled for you with multiple content type support, additional control over the slugification process as well as other useful features I would recommend the strapi-plugin-slugify.

@codiini
Copy link

codiini commented Apr 16, 2022

@ComfortablyCoding When I try to access more information for a particular item on the collection type I made this change to using the
?populate=* query, It does not return anything else such as images. Any idea how I can fix this? Thanks

@ComfortablyCoding
Copy link
Author

Hello @codiini,

I have updated the example to now work with the fields and populate API parameters to be inline with the current findOne implementation.

@codiini
Copy link

codiini commented May 2, 2022

Hey @ComfortablyCoding Thank you so much, this worked!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment