Bare Minimum

This is an example of the minimum layers that should exist.

  1. Define a route (Configuration/Routes.yml)

    extension_demo-test:
       path:         api/demo/test
       controller:   Vendor\Demo\Controller\DemoApiController::test
       defaults:
          plugin:    DemoApi
    
  2. Register Plugin namespace (ext_localconf.php)

    use Vendor\Demo\Controller\DemoApiController;
    
    \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
        'Demo',
        'DemoApi',
        [
            DemoApiController::class => 'test'
        ],
        [
            DemoApiController::class => 'test'
        ]
    );
    
  3. Create Controller (Classes/Controller/DemoApiController.php)

    <?php
        declare(strict_types = 1);
    
        namespace LMS\Demo\Controller;
    
        use Psr\Http\Message\ResponseInterface;
    
        class DemoApiController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
        {
            public function testAction(): ResponseInterface
            {
                return $this->jsonResponse(
                    (string)json_encode(
                        'ok' => true
                    )
                );
            }
        }
    
  4. Perform a request

    curl --location --request GET 'https://demo.ddev.site/api/demo/test'
    
  5. Response

    {
        "ok": true
    }