Cockpit icon indicating copy to clipboard operation
Cockpit copied to clipboard

[Feature] API endpoint to fetch items of multiple models at once

Open BFS-JWesseler opened this issue 1 year ago • 1 comments

API endpoint to fetch items of multiple models at once

Currently, we are facing the issue that we have around 40-50 models which require as many singular HTTP requests. It would be nice if we could fetch all of the items of multiple models with the same filter settings at once. It also does not need to be optimized on the data structure, basically can be done with a loop and adding the models to a JSON array which then contains the items of the respective models as usual.

Current it looks like:

api/content/items/<model1>?filter=<filter>
api/content/items/<model2>?filter=<filter>
api/content/items/<model3>?filter=<filter>

The idea would be to have something like: api/content/multiple-items?models=[<model1>,<model2>,...]&filter=<filter>

You could also add an array to the filters for something like (so that you can have filters per model if needed - even if we don't need it): api/content/multiple-items?models=[<model1>,<model2>,...]&filters=[<filter1>,<filter2>,...]

Or something similar. Anyway, that would be awesome to have and would speed up our initialization time by a lot. The easiest solution will suffice, as long as we don't need to make 40-50 HTTP requests for fetching the items of the different models with the same filter. It would be greatly appreciated.

BFS-JWesseler avatar Jun 27 '24 15:06 BFS-JWesseler

Hi 👋

You can already do this with a custom api endpoint.

Example

Create the following file: config/api/batch-content-items.php

With the following contents:

<?php

$batch = $this->param('batch', null);

if (!$batch) {
    return $this->stop(412);
}

try {
    $batch = json5_decode($batch, true);
} catch(\Throwable $e) {
    $this->stop(['error' => "<batch> is not valid json"], 412);
}

$return = [];
$content = $this->module('content');

$userRole = $this->helper('auth')->getUser('role');

foreach ($batch as $model => $opts) {

    if (!$content->model($model) || !$this->helper('acl')->isAllowed("content/{$model}/read", $userRole)) {
        continue;
    }

    $options = [];
    $process = ['locale' => $opts['locale'] ?? $this->param('locale', 'default')];

    if (isset($opts['filter'])) $options['filter'] = $opts['filter'];
    if (isset($opts['sort'])) $options['sort'] = $opts['sort'];
    if (isset($opts['fields'])) $options['fields'] = $opts['fields'];
    if (isset($opts['limit'])) $options['limit'] = $opts['limit'];
    if (isset($opts['skip'])) $options['skip'] = $opts['skip'];

    if (isset($opts['populate'])) {
        $process['populate'] = $opts['populate'];
        $process['user'] = $this->helper('auth')->getUser();
    }

    if (!isset($options['filter']) || !is_array($options['filter'])) {
        $options['filter'] = [];
    }

    // only published items
    $options['filter']['_state'] = 1;

    $return[$model] = $content->items($model, $options, $process);
}

return $return;

Now you can request the following endpoint:

/api/batch-content-items?batch={model1:{filter:{...}}, model2:{filter:{...}}, ...}

aheinze avatar Jun 28 '24 21:06 aheinze

Thanks, it is working flawlessly.

BFS-JWesseler avatar Jul 26 '24 14:07 BFS-JWesseler

FYI: This is now part of the core: https://getcockpit.com/documentation/core/api/content#get-content-items

aheinze avatar Jul 26 '24 15:07 aheinze