Add links to adjacent entries with data from a single API call

System Information
  • Strapi Version: 5.0.0-rc.12
  • Operating System: MacOS
  • Database: SQLite
  • Node Version: v20.16.0
  • NPM Version: 10.8.1
  • Yarn Version: 1.22.22

I have made a simple custom controller for findOne on the content type “case-study” to find by slug instead of id.

In the frontend, I want the view that renders this entry to include links to the next and previous entries, but I’d prefer to not make three API calls. Is there a way to add some criteria to my controller to also return the slug of next and previous? I could then build the link href on the frontend with those slugs. I suppose I would need to give it some sorting criteria as well.

Here’s my controller so far:

module.exports = createCoreController(
  "api::case-study.case-study",
  ({ strapi }) => ({
    async findOne(ctx) {
      await this.validateQuery(ctx);

      const { slug } = ctx.params;

      const entity = await strapi.db
        .query("api::case-study.case-study")
        .findOne({
          where: { slug },
          populate: true,
        });

      const sanitizedEntity = await this.sanitizeOutput(entity, ctx);

      return this.transformResponse(sanitizedEntity);
    },
  })
);

hmmm… maybe I need to use findMany but coerce it to return one post from a slug, and two adjacent posts but only their slug field

Woah! I figured it out on my own! Did not expect that to happen.

const UID = "api::case-study.case-study";

module.exports = createCoreController(UID, ({ strapi }) => ({
  async findOne(ctx) {
    await this.validateQuery(ctx);

    const { slug } = ctx.params;

    const [entity, total] = await Promise.all([
      strapi.db.query(UID).findOne({
        where: { slug },
        populate: true,
      }),
      strapi.db
        .query(UID)
        .count({ where: { publishedAt: { $notNull: true } } }),
    ]);

    const prevOrder = entity.order === 1 ? total : entity.order - 1;
    const nextOrder = entity.order === total ? 1 : entity.order + 1;

    const adjacent = await strapi.entityService.findMany(UID, {
      select: ["name", "order", "slug"],
      publicationState: "live",
      filters: {
        order: { $in: [prevOrder, nextOrder] },
      },
    });

    const prev = adjacent.find((a) => a.order === prevOrder) || null;
    const next = adjacent.find((a) => a.order === nextOrder) || null;

    entity.adjacent = {
      prev,
      next,
    };

    const sanitizedEntity = await this.sanitizeOutput(entity, ctx);

    return this.transformResponse(sanitizedEntity);
  },
}));