What's the difference between query('restaurants').find() and services.restaurants.find()?

I just want to know if there’s any difference between the two functions. Which one of these should be preferred?

Thank you :slight_smile:

1 Like

Hello @mariusbolik

Services → Reusable functions that are predefined by strapi with some custom business logic (those are usually wrapped around the query functions actually but with some additional custom logic inside them)

Query → Direct query to database

Example of how these are used:

strapi.services.restaurants.find(), that services will actually call the next code:

find(params, populate) {
    return strapi.query('restaurant').find(params, populate);
  },

It is calling the strapi.query() inside it. The thing is that you can modify these services and add your own business logic to it, but you can’t modify query() functions.

Take a look at another example that strapi uses:
strapi.services.restaurants.create(restaurantData)

This one will execute a service called create()

async create(data, { files } = {}) {
    const validData = await strapi.entityValidator.validateEntity(strapi.models.restaurant, data);
    const entry = await strapi.query('restaurant').create(validData);

    if (files) {
      // automatically uploads the files based on the entry and the model
      await strapi.entityService.uploadFiles(entry, files, {
        model: 'restaurant',
        // if you are using a plugin's model you will have to add the `source` key (source: 'users-permissions')
      });
      return this.findOne({ id: entry.id });
    }

    return entry;
  },

As you can see, that service uses some additional business logic, not only the query().create() as in the example with query().find()

1 Like

As @sunnyson pointed out not really no. The only time it would really be needed to call the service is if you have customized the service in ./api/someModel/services/someModel.js or if you are building a custom plugin with custom services.

For the most general of use-cases it’s easier and faster to just directly reference the query instead since our default services from the core-api do the following:

module.exports = {
  find(params, populate) {
    return strapi.query('restaurant').find(params, populate);
  },
  findOne(params, populate) {
    return strapi.query('restaurant').findOne(params, populate);
  },
  count(params) {
    return strapi.query('restaurant').count(params);
  },
  async create(data, { files } = {}) {
    const validData = await strapi.entityValidator.validateEntity(strapi.models.restaurant, data);
    const entry = await strapi.query('restaurant').create(validData);

    if (files) {
      // automatically uploads the files based on the entry and the model
      await strapi.entityService.uploadFiles(entry, files, {
        model: 'restaurant',
        // if you are using a plugin's model you will have to add the `source` key (source: 'users-permissions')
      });
      return this.findOne({ id: entry.id });
    }

    return entry;
  },
  async update(params, data, { files } = {}) {
    const validData = await strapi.entityValidator.validateEntityUpdate(
      strapi.models.restaurant,
      data
    );
    const entry = await strapi.query('restaurant').update(params, validData);

    if (files) {
      // automatically uploads the files based on the entry and the model
      await strapi.entityService.uploadFiles(entry, files, {
        model: 'restaurant',
        // if you are using a plugin's model you will have to add the `source` key (source: 'users-permissions')
      });
      return this.findOne({ id: entry.id });
    }

    return entry;
  },
  delete(params) {
    return strapi.query('restaurant').delete(params);
  },
  search(params) {
    return strapi.query('restaurant').search(params);
  },
  countSearch(params) {
    return strapi.query('restaurant').countSearch(params);
  },
};

From the above examples you can see there is some additional logic on the create and update services but none of the others.

1 Like

@sunnyson @DMehaffy Thank you guys for giving me a better understanding of the functionality of Strapi. :blush:

2 Likes

I was about to ask the same thing. The difference I noticed is that a service respects draft system but query doesn’t. Both trigger lifecycles, don’t they? I suspect that default service implementation wraps a query but didn’t have a chance to dig through sources. Can anybody else add to the topic?

@invariant the query is what triggers the lifecycles, do you have some examples of this not working?

1 Like

@DMehaffy, thanks for confirming. I stick to services, especially because of drafts, so wasn’t sure about queries.