Create a public custom Drupal 8 REST webservice

In this blog post I will show you the code needed to create your own custom Drupal 8 webservice with Symfony Routing.

This is completely separate from the REST module

1. Create a custom module

In the folder modules create a new folder with the name of your new module, mine will be called custom_api

Inside your new folder create 2 files.


    name: Custom API
    type: module
    description: “Custom API”
    package: Custom
    core: 8.x

  2. custom_api.routing.yml

    path: /api/{api}
    _controller: ‘\Drupal\custom_api\Controller\ApiController::handle’
    _title: ‘API Handler’
    _permission: ‘access content’

2. Create a Routing controller

Inside your module folder, create a folder src and inside that folder create
another folder called Controller

Inside your controller folder, create a file named ApiController.php (note
this must be the same name as it appears in your *.routing.yml file.


namespace Drupal\custom_api\Controller;

use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Routing\RouteMatchInterface;

use Drupal\custom_api\ApiHandlers\CustomApiHandler;

class ApiController implements ContainerAwareInterface {
 use ContainerAwareTrait;

public function handle(RouteMatchInterface $route_match, Request $request, $api)
   switch ($api)
     case ‘custom’:
       return CustomApiHandler::handle($request);
   return new Response(null, 404, array());

Symfony will route your request to this file, because you defined the
handle() function in your *.routing.yml file.

The Request instance will contain all the information about your request, see

For abstract reasons (as I will include other api handlers in this same function) I decided to work with separate API Handler classes and a route wildcard.

Inside the *.routing.yml file I included a wildcard in the path named {api}, Symfony will pass this value as a variable to your method, this is the last argument in your handle function.

When I navigate my browser to, my CustomApiHandler will be triggered. In case I wanted to add other endpoints, I would simply add
another case to the switch statement.

3. Create your ApiHandler

This part is optional, and can be done directly inside your Controller class, but for abstract reasons I chose to split the logic into different ApiHandler classes.

Note that Drupal works with Serializers and Normalizers to easily serialize and deserialize data, I haven’t figured out how to work with them yet, but for a simple structure, you could manually json_encode and json_decode your data.

In this case I also switched on Rest Method, to only work with GET requests, you could simply add other methods to the switch statement, and read the request object for data of your request.


namespace Drupal\custom_api\ApiHandlers;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class CustomApiHandler {

public static function handle(Request $request) {
 switch ($request->getMethod())
  case ‘GET’:
   return static::get($request);

   return new Response(null, 404, array());

 return static::get($request);

 private static function get(Request $request) 
  $output = json_encode('OK');
  $headers = array(‘Content-Type’ => $request->getMimeType(‘json’));
  return new Response($output, 200, $headers);