TYPO3 Tipps und Tricks: REST API mit TYPO3 (REST Server)

Als TYPO3 Agentur steht man oft vor der Herausforderung eine RESTful-Schnittstelle zu entwerfen/programmieren. In der Vergangenheit haben wir dazu schon verschiedene Wege verwendet. Mit TYPO3 10 wurde ein neues Routing-Konzept mit Route Enhancers eingeführt, welches nun völlig neue Möglichkeiten bietet eine RESTful-Schnittstelle zu entwerfen und somit TYPO3 als REST-Server zu betreiben. In diesem Blogbeitrag stelle ich nun das neue und flexible Konzept auf Basis des neuen Routings vor.

Dank der Flexibilität von TYPO3 gibt es sicher unzählige Wege um eine RESTful-API zu entwerfen (über einige davon haben wir auch schon in unserem Blog geschrieben, diese findest du hier). Bis zu TYPO3 Version 10, haben wir die TYPO3 Extension rest dafür verwendet). Diese funktioniert allerdings in TYPO3 10 nicht mehr und es ist fraglich, wann/ob diese jemals TYPO3 10 kompatibel wird. Aus diesem Grund waren wir auf der Suche nach neuen Möglichkeiten um eine RESTful-API auf Basis TYPO3 zu entwickeln. Nachdem wir uns verschiedene Konzept (zBsp. über eine SLIM Middleware oder über Plugins mit JSON-View wie auf "use TYPO3" beschrieben) überlegt und recherchiert haben, entschieden wir uns für die Routing-Lösung.

Wichtig dabei war für uns auch immer, dass man die Vorteile von extbase nützt und so nahe wie möglich am TYPO3-Standard bleibt. Also die Domain-Models ganz normal über TCA konfiguriert und wie man es gewohnt ist über das Backend verwaltet werden. Anfänglich haben wir hierfür einen eigenen Route Enhancer geschrieben, welcher die Plugins aufruft. Im Zuge unserer Recherche sind wir dann auf die Extension "Extbase Yaml Routes" von Borulko Serhii gestoßen:

Mit dieser Extension kann man eine RESTful-API entwickeln, die Routen per YAML-File konfigurieren und hast CRUD für seine Extbase-Objekte out of the box. Zusätzlich werden hier auch die bekannten TYPO3 Konzepte wie Route Enhancers, Extbase, JsonView, Middleware und co. verwendet. Zusätzlich wird auch gleich eine Javascript-Bibliothek zum Aufruf der API mitgeliefert.

RESTful API entwickeln

Nachdem die Extension über composer installiert und das TypoScript eingebunden wurde, geht es schon los. Zuerst muss in der Site-Config der entsprechende Route Enhancer hinzugefügt/eingebunden werden:

routeEnhancers:  ApplyRoutesCollection:    type: Routes

Definition der Routen

Über die Datei Routes.yml im Configuration-Verzeichnis der Extension werden die API-Routen (GET, POST, DELETE,...) definiert. Hierfür ein einfaches Beispiel:

va_rest_day-list:  path: api/v1/restdemo/days  controller: Various\VaRest\Controller\RestController::list  methods: GET  format: json  defaults:    plugin: Dayva_rest_day-detail:  path: api/v1/restdemo/days/{uid}  controller: Various\VaRest\Controller\RestController::detail  methods: GET  format: json  defaults:    plugin: Day  requirements:    uid: \d+va_rest_win-subscribe:  path: api/v1/restdemo/subscribe  controller: Various\VaRest\Controller\RestController::subscribe  methods: POST  format: json  defaults:    plugin: Subscribe

Plugin anlegen

Danach müssen noch die Plugins angelegt werden (in der ext_localconf.php):

ExtensionUtility::configurePlugin(    'va_rest',    'Day',    [        RestController::class => 'detail, list',    ],    []);ExtensionUtility::configurePlugin(    'va_rest',    'Subscribe',    [        RestController::class => 'subscribe',    ],    [        RestController::class => 'subscribe',    ]);

Controller action - Where the magic happens

Wie bereits in der Einleitung erwähnt, haben wir vorab natürlich ganz normale extbase-Objekte angelegt und definiert (SQL-Definition, TCA, Models und Repositories). Mit der obigen Konfiguration werden nun automatisch die jeweiligen Controller-Actions aufgerufen. An dieser Stelle sei noch gesagt, dass wir für unsere APIs immer json als Dateiformat verwenden. Da in unserem Beispiel auch FileReferences (Bilder) verwendet werden, verwenden wir auch die JsonView, damit wir unser Objekt noch bearbeiten können (siehe weiter unten). Auch dies ist ein Konzept, dass dem einen oder anderen bereits aus TYPO3 bekannt sein sollte.

<?phpdeclare(strict_types=1);namespace Various\VaRest\Controller;use TYPO3\CMS\Core\Http\HtmlResponse;use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;use Various\VaRest\Domain\Repository\DayRepository;use Various\VaRest\Utility\RestUtility;use Various\VaRest\Mvc\View\JsonView;class RestController extends ActionController{    /**     * @var \Various\VaRest\Mvc\View\JsonView     */    protected $view;    /**     * @var string     */    protected $defaultViewObjectName = JsonView::class;    protected DayRepository $dayRepository;    public function __construct(DayRepository $dayRepository)    {        $this->dayRepository = $dayRepository;    }    public function detailAction()    {        if(!$this->request->hasArgument('uid')) {            return new HtmlResponse('Parameter Missing', 400);        }        $day = $this->dayRepository->findByUid(intval($this->request->getArgument('uid')));        if(is_null($day)) {            return new HtmlResponse('Bad Parameter', 400);        }        $this->view->setVariablesToRender(['day']);        $this->view->assign('day', $day);    }    public function listAction()    {        $this->view->setVariablesToRender(['days']);        $this->view->assign('days', $this->dayRepository->findAll());    }    public function subscribeAction()    {        return RestUtility::subscribe($this->request);    }}

json-Darstellung

Nun definieren wir noch wie unser Day-Objekt in der JsonView gerendert wird:

<?phpdeclare(strict_types=1);namespace Various\VaRest\Mvc\View;use TYPO3\CMS\Extbase\Mvc\View\JsonView as ExtbaseJsonView;use TYPO3\CMS\Extbase\Persistence\ObjectStorage;use TYPO3\CMS\Extbase\Reflection\ObjectAccess;use Various\VaRest\Domain\Model\Day;class JsonView extends ExtbaseJsonView{    /**     * @var array     */    protected $configuration = [        'days' => [            '_descendAll' => [                '_exclude' => ['pid'],                '_descend' => [                    'image' => [                        '_descendAll' => [                            '_only' => ['originalResource'],                            '_descend' => [                                'originalResource' => [                                    '_only' => ['publicUrl'],                                ],                            ],                        ],                    ],                ],            ],        ],        'day' => [            '_exclude' => ['pid'],            '_descend' => [                'image' => [                    '_descendAll' => [                        '_only' => ['originalResource'],                        '_descend' => [                            'originalResource' => [                                '_only' => ['publicUrl'],                            ],                        ],                    ],                ],            ],        ],    ];    /**     * Transforming ObjectStorages to Arrays for the JSON view     *     * @param mixed $value     */    protected function transformValue($value, array $configuration): array    {        if ($value instanceof ObjectStorage) {            $value = $value->toArray();        }        return parent::transformValue($value, $configuration);    }}

Zusammenfassung

Mit obigen Beispiel kann also ziemlich einfach und flexibel eine RESTful-API mit TYPO3 erstellt werden. Auch eine Absicherung mittels Login oder Basic Auth ist einfach möglich. Man kann auch eigene Middlewares entwickeln, welche man hier gut einsetzen kann. 

Zum Schluss möchte ich auch noch auf POSTMAN hinweisen. Dies sollte jedem Backend-Entwickler ein Begriff sein, kann man damit seine API definieren, testen und weitergeben (als Collection). Wir legen hier für jede API immer eine eigene Collection an, welche dann im git-Repository liegt und jeder Entwickler an einem Projekt kann sich sehr schnell einen Überblick über die API verschaffen. Auch kann für den Aufruf zum Beispiel direkt Programmier-Code daraus exportiert werden.

POSTMAN TYPO3 API RESTful aufruf
POSTMAN GET-Aufruf der API