How to set up a restful API for Modx 3

Hey all, I’m trying to setup a restful API on Modx 3 where I can pull several article post based from a TV filter. I’m hosting my site on NginX and I’m getting a 502 Bad gateway error. Here is my setup

<?php
// Boot up MODX
require_once dirname(dirname(__FILE__)) . '/config.core.php';
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
$modx = new modX();
$modx->initialize('web');
$modx->getService('error','error.modError', '', '');
// Boot up any service classes or packages (models) you will need

$path = $modx->getOption('migx.core_path', null,$modx->getOption('core_path').'components/migx/') . 'model/migx/';
$modx->getService('migx', 'migx', $path);

// Load the modRestService class and pass it some basic configuration
$rest = $modx->getService('rest', 'rest.modRestService', '', array(
    'basePath' => dirname(__FILE__) . '/Controllers/',
    'controllerClassSeparator' => '',
    'controllerClassPrefix' => 'MyController',
    'xmlRootNode' => 'response',
));
// Prepare the request
$rest->prepare();
// Make sure the user has the proper permissions, send the user a 401 error if not
if (!$rest->checkPermissions()) {
    $rest->sendUnauthorized(true);
}
// Run the request
$rest->process();
<?php
class MyControllerItems extends \MODX\Revolution\Rest\modRestController {
    public $classKey = 'modResource';
    public $defaultSortField = 'sortorder';
    public $defaultSortDirection = 'ASC';
    
    public $allowedMethods = array();

    public function read($id) {
        if (empty($id)) {
            return $this->failure($this->modx->lexicon('rest.err_field_ns',array(
                'field' => $this->primaryKeyField,
            )));
        }
        /** @var xPDOObject $object */
        $c = $this->getPrimaryKeyCriteria($id);
        $this->object = $this->modx->getObject($this->classKey,$c);
		
        if (empty($this->object)) {
            return $this->failure($this->modx->lexicon('rest.err_obj_nf',array(
                'class_key' => $this->classKey,
            )));
        }
    }
}
location /rest/ {
    try_files $uri @modx_rest;
}
location @modx_rest {
    rewrite ^/rest/(.*)$ /rest/index.php?_rest=$1&$args last;
}

Can someone kindly point out where I’m going wrong?

I made a video about this on Youtube, but it’s not for NginX, so it’s probably not helpful for your issue.
(The corresponding code is here.)


With Apache, the problem in the (current) documentation is, that the example .htaccess file assumes, that you use MODX as a headless CMS that only serves as an API.

If you want to use MODX as before, but additionally add a restful API, then it’s easier to move the .htaccess file to the “rest” subfolder.

1 Like

Yes, I was watching this and this was how I got this far. I think it might be an NginX issue but I have to use an NginX server. Could it be possible that I’m using the public variables wrong for modResource, i.e the class key?

public $classKey = 'modResource';
public $defaultSortField = 'sortorder';
public $defaultSortDirection = 'ASC';
    
public $allowedMethods = array();

I remember having a similar issue that was related to the naming of key attributes in the class, so for instance, for this object

<object class="CronosProjects" table="cronos_projects">

These is the values I use for my controller

/** @var string $classKey The xPDO class to use */
  public $classKey = 'Cronos\Model\CronosProjects';

  /** @var string $classAlias The alias of the class when used in the getList method */
  public $classAlias  = 'CronosProjects';

The value for $defaultSortField is wrong, because there doesn’t exist such a field. (But this doesn’t generate the error you posted.)

This should work

<?php
class MyControllerItems extends \MODX\Revolution\Rest\modRestController {
    public $classKey = 'modResource';
    public $defaultSortField = 'menuindex';
    public $defaultSortDirection = 'ASC';
}

or maybe use this instead to make it more future-proof.

<?php
class MyControllerItems extends \MODX\Revolution\Rest\modRestController {
    public $classKey = 'MODX\\Revolution\\modResource';
    public $defaultSortField = 'menuindex';
    public $defaultSortDirection = 'ASC';
    public $classAlias = 'modResource';
}

Also, make sure to protect your endpoint! Especially when you use modResource. Otherwise everyone can delete or change your data.


My guess is that this rewrite is wrong.

Maybe temporarily replace the code in the file rest/index.php with something like this

<?php
$param = $_GET['_rest'] ?? '';
echo 'File: rest/index.php | Get-parameter "_rest" = ' . $param;

just to make sure, that the redirect to rest/index.php works correctly.

Thanks for the helps guys. I figured out the issue and it was a silly one. The file name and the class name were different, which caused the 502 error. The filename was “Blog.php” and the classname was “MyControllerItems”.

Also, I took your suggestion to protect my endpoints by using your suggestion in the tutorial video

public function verifyAuthentication(){
        
        if($this->request->method == 'get'){
            return true;
        }
        
        if(!$this->modx->user || $this->modx->user->id < 1){
            return false;
        }
        
        if(!$this->modx->user->hasSessionContext('web')){
            return false;
        }
        
        if(!$this->modx->user->isMember('apiuser')){
            return false;
        }
        
        return true;
    }

Blog.php is now returning every resource that was created in the dashboard (Parent 0) but I only want to return all of the resources from a parent id of 2 with a template id of 3.

How can I setup this default load? Also, is it possible to search/filter by a template variable?

One way to do this, is to override the function “prepareListQueryBeforeCount” in your controller.

Sure, but you have to add a join to the query and maybe also change the function “addSearchQuery”


Don’t just copy&paste the code. Adjust it to your needs. If you only need the API to read data, then don’t allow anything else.