# Custom Endpoint for Sorting Entries in Collection?

**URL:** <https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547>\
**Category:** Support\
**Created:** [January 30, 2019, 10:28pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547 "2019-01-30T22:28:19Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![ColinTravis](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/colintravis/32/219_2.png) [@ColinTravis](https://discourse.getcockpit.com/u/ColinTravis)\
**Post date:** [January 30, 2019, 10:28pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/1 "2019-01-30T22:28:19Z")

</div>

How would I set up an API endpoint to retrieve entries from a collection with a scheme of

api/public/projects/1 (returns first entry)  
api/public/projects/2 (second entry)

and so on?

Right now I’ve just got a custom API that retrieves the published entries in a collection without having to specify entries,

---

<div class="post-metadata">

**Author:** ![pauloamgomes](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/pauloamgomes/32/48_2.png) [@pauloamgomes](https://discourse.getcockpit.com/u/pauloamgomes)\
**Post date:** [January 31, 2019, 7:03pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/2 "2019-01-31T19:03:53Z")

</div>

You can create your own Rest controller, for example if you are doing that in a custom addon:

bootstrap.php

```auto
// Include actions.
if (COCKPIT_API_REQUEST) {
  $this->on('cockpit.rest.init', function ($routes) {
    $routes['public'] = 'MyAddon\\Controller\\RestApi';
  });
}

```

in your controller: `MyAddon/Controller/RestApi.php`

```auto
<?php

namespace MyAddon\Controller;

use \LimeExtra\Controller;

/**
 * RestApi class for dealing with user reviews.
 */
class RestApi extends Controller {
  public function projects($id) {
    // Additional logic, validations, etc..
    // ...
    // Fetch the entry (filtering by the id, but you can use other field)
    $entry = $this->app->module('collections')->findOne('projects', ['_id' => $id]);
    // Manipulate entry if you need and return as you neeed.
    return ['project' => $entry, 'id' => $id];
  }

}

```

So you can access `/api/myaddon/public/projects/<id>`

---

<div class="post-metadata">

**Author:** ![ColinTravis](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/colintravis/32/219_2.png) [@ColinTravis](https://discourse.getcockpit.com/u/ColinTravis)\
**Post date:** [January 31, 2019, 9:14pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/3 "2019-01-31T21:14:59Z")

</div>

Ok, bear with me here as I’m using Cockpit as my way to learn REST APIs, and I feel like I’m super close.

So in the bootstrap file of the addon, it fires when cockpit is queried at `<CMS_URL>/api`, then starts a function that passes in `$routes`. I’m a bit hazy here, but I’m guessing that `$routes` is the URL passed in?

It then takes that route (in this case, “public”, so /api/public) and then I’m a bit confused as to what the `= 'MyAddon\\Controller\RestApi';` is for. Is that for pulling the query from the restapi?

The RestApi itself seems to make sense. Essentially, it passes in whatever value is in the url and then queries the specified collection for that value (id, slug, etc.) then returns it?

Just trying to make better sense of how the call/response works for this. Thanks!

---

<div class="post-metadata">

**Author:** ![pauloamgomes](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/pauloamgomes/32/48_2.png) [@pauloamgomes](https://discourse.getcockpit.com/u/pauloamgomes)\
**Post date:** [January 31, 2019, 9:39pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/4 "2019-01-31T21:39:34Z")

</div>

> [@ColinTravis](#):
>
> MyAddon\Controller\RestApi

That’s the Class that will bind to the `public` path, the path is composed by:

`/api/<addon-name>/<endpoint-name>/<method-name>/<method-argument>`

so `/api/myaddon/public/projects/12345` matches above path

resuming, it will check for addon name and endpoint name and load the class that bind to it and will invoke the method projects of that class, that method receives the id as an argument.

---

<div class="post-metadata">

**Author:** ![ColinTravis](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/colintravis/32/219_2.png) [@ColinTravis](https://discourse.getcockpit.com/u/ColinTravis)\
**Post date:** [January 31, 2019, 9:59pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/5 "2019-01-31T21:59:58Z")

</div>

Ok, that definitely makes sense now. I also looked through your ImageStyles plugin to get a better grasp of it. I’ve set this up myself, but whenever I query that path with one of my post’s IDs, I’m just given a response of:

```json
{
  "error": "Unauthorized"
}

```

Next question would be, is the public path tied to the one that exists already in Cockpit, or my custom one? Right now, my custom one is just a “projects.php” file in config/api/ that returns that collection with only the published entries filtered. Or, is this error just from a misstep with API permissions?

Thanks again!

---

<div class="post-metadata">

**Author:** ![ColinTravis](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/colintravis/32/219_2.png) [@ColinTravis](https://discourse.getcockpit.com/u/ColinTravis)\
**Post date:** [February 1, 2019, 8:33pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/6 "2019-02-01T20:33:23Z")

</div>

Alright. I can’t figure out what the issue is, and I don’t know if it’s binding with any routes at all.

Here’s what I’ve tried:

`addons/MyAddon/bootstrap.php`

```php
<?php

if (COCKPIT_API_REQUEST) {
    $this->on('cockpit.rest.init', function ($routes) {
        $routes['public'] = 'MyAddon\\Controller\\RestApi';
    });
}

```

`addons/MyAddon/Controller/RestApi.php`

```php
<?php

namespace MyAddon\Controller;

use \LimeExtra\Controller;

/**
 * RestApi class for dealing with user reviews.
 */
class RestApi extends Controller
{
    public function projects()
    {

        $entry = $this->app->module('collections')->find('Projects');

// return ['project' => $entry, 'id' => $id];
        return ['project' => $entry];
    }
}

```

I’ve tried this just to see if I can can even fetch the collection “Projects” from:  
[https://cms.alternatingfocus.com/api/myaddon/public/projects](https://cms.alternatingfocus.com/api/myaddon/public/projects)

But I just receive the Unauthorized error.

And if I try to pull a specific ID from the collection by changing  
`/addons/MyAddon/Controller/RestApi.php` to:

```php
class RestApi extends Controller {
    public function projects($id) {
        $entry = $this->app->module('collections')->findOne('Projects', ['_id' => $id]);
        return ['project' => $entry, 'id' => $id];
    }
}

```

using: [https://cms.alternatingfocus.com/api/myaddon/public/projects/5c4f6041326533525100022d](https://cms.alternatingfocus.com/api/myaddon/public/projects/5c4f6041326533525100022d)

I still get the Unauthorized error. I’ve tried changing the route to `$routes['public']` and querying it from [https://cms.alternatingfocus.com/api/myaddon/testing/projects/5c4f6041326533525100022d](https://cms.alternatingfocus.com/api/myaddon/testing/projects/5c4f6041326533525100022d) to no avail.

What am I missing?

---

<div class="post-metadata">

**Author:** ![pauloamgomes](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/pauloamgomes/32/48_2.png) [@pauloamgomes](https://discourse.getcockpit.com/u/pauloamgomes)\
**Post date:** [February 1, 2019, 9:43pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/7 "2019-02-01T21:43:42Z")

</div>

You need to use a token api, use the one from the master key or better generate a new one that is specific for that path, e.g:

![image](https://canada1.discourse-cdn.com/flex030/uploads/getcockpit/original/1X/895897130f6d763ddcca978c86e4e0eaa79cb749.png)

---

<div class="post-metadata">

**Author:** ![ColinTravis](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/colintravis/32/219_2.png) [@ColinTravis](https://discourse.getcockpit.com/u/ColinTravis)\
**Post date:** [February 1, 2019, 10:42pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/8 "2019-02-01T22:42:49Z")

</div>

Alright, made a new temporary key for this route to work on.

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/getcockpit/original/1X/2624108f9c6cb4211d9f0affa69cc6527d855a51.png)

When I query it with or without the token, I get these results:

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/getcockpit/original/1X/99506bd6e3f0e2a9928f9fb3be502c1645fa143e.png)

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/getcockpit/original/1X/900b667e0b065975f5c4ec01a76cc4cb6664a55d.png)

I thought binding it to the public route no longer required an API key for authentication?

---

<div class="post-metadata">

**Author:** ![pauloamgomes](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/pauloamgomes/32/48_2.png) [@pauloamgomes](https://discourse.getcockpit.com/u/pauloamgomes)\
**Post date:** [February 1, 2019, 11:57pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/9 "2019-02-01T23:57:41Z")

</div>

> [@ColinTravis](#):
>
> [https://cms.alternatingfocus.com/api/myaddon/testing/projects/5c4f6041326533525100022d](https://cms.alternatingfocus.com/api/myaddon/testing/projects/5c4f6041326533525100022d)

the url format should be:

[https://cms.alternatingfocus.com/api/myaddon/testing/projects/5c4f6041326533525100022d?token=xxxxxxxxxxxxxx](https://cms.alternatingfocus.com/api/myaddon/testing/projects/5c4f6041326533525100022d?token=xxxxxxxxxxxxxx)

better to change your token paths to /api/myaddon/\* (from the screenshot you have /api/myaddon/public/\* but you are acessing the url /api/myaddon/testing/projects

---

<div class="post-metadata">

**Author:** ![ColinTravis](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/colintravis/32/219_2.png) [@ColinTravis](https://discourse.getcockpit.com/u/ColinTravis)\
**Post date:** [February 2, 2019, 6:12pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/10 "2019-02-02T18:12:45Z")

</div>

Ok, this is bizarre.

I changed my API token to /api/myaddon/\*

I’m no longer receiving **Not Authorized** , now I’m receiving:

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/getcockpit/original/1X/09b946571c59174289e33b70227314d9999e66fe.png)

Is this an issue with the Lime Controller? I’ve tried changing the projects() method (leaving all the other code the same, save for the routes is pointing to testing) to just `return("hello")`, to see if it’s even reaching it’s endpoint, and I’m still getting **Path Not Found**.

---

<div class="post-metadata">

**Author:** ![pauloamgomes](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/pauloamgomes/32/48_2.png) [@pauloamgomes](https://discourse.getcockpit.com/u/pauloamgomes)\
**Post date:** [February 4, 2019, 5:49pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/11 "2019-02-04T17:49:05Z")

</div>

You probably have something wrong, can you paste (only the main definition) the code of the addon:

- when you are binding the controller
- the controller

---

<div class="post-metadata">

**Author:** ![ColinTravis](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/colintravis/32/219_2.png) [@ColinTravis](https://discourse.getcockpit.com/u/ColinTravis)\
**Post date:** [February 5, 2019, 4:36pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/12 "2019-02-05T16:36:50Z")

</div>

Sure thing.

**addons/MyAddon/bootstrap.php**

```php
if (COCKPIT_API_REQUEST) {
    $this->on('cockpit.rest.init', function ($routes) {
        $routes['testing'] = 'MyAddon\\Controller\\RestApi';
    });
}

```

**addon/MyAddon/Controller/RestApi.php**

```php
namespace MyAddon\Controller;

use \LimeExtra\Controller;

class RestApi extends Controller {
    public function projects($id) {
        $entry = $this->app->module('collections')->findOne('Projects', ['_id' => $id]);

        return ['project' => $entry, 'id' => $id];
    }

}

```

---

<div class="post-metadata">

**Author:** ![pauloamgomes](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/pauloamgomes/32/48_2.png) [@pauloamgomes](https://discourse.getcockpit.com/u/pauloamgomes)\
**Post date:** [February 6, 2019, 10:27am UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/13 "2019-02-06T10:27:46Z")

</div>

can you try to access:

[http://cockpit-url/api/testing/projects/](http://cockpit-url/api/testing/projects/)?token=xxxxx

ensure that you have the api keys configured to have access to /api/testing/\*

---

<div class="post-metadata">

**Author:** ![ColinTravis](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.getcockpit.com/colintravis/32/219_2.png) [@ColinTravis](https://discourse.getcockpit.com/u/ColinTravis)\
**Post date:** [February 6, 2019, 10:16pm UTC](https://discourse.getcockpit.com/t/custom-endpoint-for-sorting-entries-in-collection/547/14 "2019-02-06T22:16:16Z")

</div>

There it is. Got it. It works great now. I guess the URL that I was accessing it from was the ticket. It looks like the /myaddon/ part of it wasn’t necessary? Either way, this is now working great! Thank you so much for helping out with this conundrum.
