How to get random records from Strapi content API

Viewed 2208
4 Answers

There is no API parameter for getting a random result.

So: FrontEnd is the recommended solution for your question.

You need to create a random request range and then get some random item from this range.

function getRandomInt(max) {
  return Math.floor(Math.random() * Math.floor(max));
}
const firstID = getRandomInt(restaurants.length);
const secondID = getRandomInt(3);
const query = qs.stringify({
  id_in:[firstID,secondID ] 
});
// request query should be something like GET /restaurants?id_in=3&id_in=6

There's no official Strapi API parameter for random. You have to implement your own. Below is what I've done previously, using Strapi v3:

1 - Make a service function

File: api/mymodel/services/mymodel.js

A service is handy because it can be used in many places (cron jobs, inside other models, etc).

module.exports = {
    serviceGetRandom() {

        return new Promise( (resolve, reject) => {
            
            // There's a few ways to query data.
            // This example uses Knex.
            const knex = strapi.connections.default
            let query = knex('mydatatable')

            // Add more .select()'s if you want other fields
            query.select('id')

            // These rules enable us to get one random post
            query.orderByRaw('RAND()')
            query.limit(1)

            // Initiate the query and do stuff
            query
            .then(record => {
                console.log("getRandom() record: %O", record[0])
                resolve(record[0])
            })
            .catch(error => {
                reject(error)
            })
        })
    }
}

2 - Use the service somewhere, like a controller:

File: api/mymodel/controllers/mymodel.js

module.exports = {

    //(untested)

    getRandom: async (ctx) => {

        await strapi.services.mymodel.serviceGetRandom()
        .then(output => {
            console.log("getRandom output is %O", output.id)
            ctx.send({
                randomPost: output
            }, 200)
        })
        .catch( () => {
            ctx.send({
                message: 'Oops! Some error message'
            }, 204) // Place a proper error code here
        })

    }

}

3 - Create a route that points to this controller

File: api/mymodel/config/routes.json

...
    {
        "method": "GET",
        "path": "/mymodelrandom",
        "handler": "mymodel.getRandom",
        "config": {
            "policies": []
        }
    },
...

4 - Then in front-end, you access the route

(However you access your API)

ie /api/mymodelrandom

One way you can do this reliably is by two steps:

  1. Get the total number of records
  2. Fetch the number of records using _start and _limit parameters
// Untested code but you get the idea

// Returns a random number between min (inclusive) and max (exclusive)
function getRandomArbitrary(min, max) {
    return Math.random() * (max - min) + min;
}

const { data: totalNumberPosts } = await axios.get('/posts/count');

// Fetch 20 posts
const _limit = 20;

// We need to be sure that we are not fetching less than 20 posts
// e.g. we only have 40 posts. We generate a random number that is 30.
// then we would start on 30 and would only fetch 10 posts (because we only have 40)
const _start = getRandomArbitrary(0, totalNumberPosts - _limit);

const { data: randomPosts } = await axios.get('/posts', { params: { _limit, _start } })

The problem with this approach is that it requires two network requests but for my needs, this is not a problem.

This seem to work for me with Strapi v4.3.8 and graphql

src/index.js

"use strict";

module.exports = {

  register({ strapi }) {
    const extensionService = strapi.service("plugin::graphql.extension");

    const extension = ({ strapi }) => ({
      typeDefs: `
        type Query {
          randomTestimonial: Testimonial
        }
      `,
      resolvers: {
        Query: {
          randomTestimonial: async (parent, args) => {
            const entries = await strapi.entityService.findMany(
              "api::testimonial.testimonial"
            );
            const sanitizedRandomEntry =
              entries[Math.floor(Math.random() * entries.length)];

            return sanitizedRandomEntry;
          },
        },
      },
      resolversConfig: {
        "Query.randomTestimonial": {
          auth: false,
        },
      },
    });

    extensionService.use(extension);
  },
  bootstrap({ strapi }) {},
};

graphql query:

  query GetRandomTestimonial {
    randomTestimonial {
      __typename
      name
      position
      location
      description
    }
  }

generate random testimonial on route change/refresh https://jungspooner.com/biography

Related