Structure of routes

Last updated on
17 August 2017

The simplest way to define routes is to create a module.routing.yml file (see the introductory example). Each route is defined as a machine name in the form of module_name.route_name (e.g. book.render) with the following properties:

  • path (required): The URL to the route, with a leading forward slash (e.g., path: '/book'). You can use dynamic properties by including them in curly braces. (e.g., path: '/node/{node}/outline'). These will be passed along as arguments via parameter converters to the controller/form (see below). Note that the first item of the path must not be dynamic. You can also define optional parameters at the end of your path (See 'Optional Parameters' on this page).
  • defaults (required): Defines the default properties of a route. Provide one of the following to specify how the output is generated:

    Finally, if any static arguments should be passed to the controller, provide those in the defaults array as well. Name them to match the arguments of your controller method. See below.

    Optional parameters: If you plan to include optional parameters at the end of your path, foo/bar/{baz} should also match foo/bar with baz being a specific value you should set it in the defaults array.

    • _controller: The value is a callable (typically classname::method) returning either
      • a renderable array. This will be rendered into HTML and used as the main content of a normal, themed Drupal page. See An introductory example to Drupal 8 routes and controllers.
      • returning an Symfony\Component\HttpFoundation\Response object. For non-HTML (e.g. XML feeds) or for only a "page fragment" of HTML. This will be sent directly, no theming or blocks will be added.
      • namespace:\Drupal\[module name]\Controller\[ClassName]::[method]
        ex: _controller: '\Drupal\test_mod\Controller\TestModController::build'
      • path: [module name]/src/Controller/[ClassName].php,
        ex: test_mod/src/Controller/TestModController.php, containing class TestModController with method build() ('method' is an OO term for a function inside a class).
      • service: controller.test:[method], where "controller.test" is a service you have defined yourself in the dependency injection container.
        ex: _controller: 'controller.test:buildAction'

      Note that the value of _controller is not a path, but rather a namespaced class name using the PSR-4 standard for PHP namespace autoloading (see PSR-4 namespaces and autoloading in Drupal 8). The PSR-4 namespaced path name must map to a corresponding path structure.

      Note that module names are lower-case with underscores, while class names are camel-case. See also An introductory example to Drupal 8 routes and controllers.

    • _form: This one expects a classname implementing Drupal\Core\Form\FormInterface. See form API in Drupal 8 for an example.
    • _entity_view: The value is entity_type.view_mode. Will find an entity in the path and render it in the given view mode. E.g. _entity_view: node.teaser will return the render array of the {node} in teaser mode. See entity view pages provided by the entity view controller
    • _entity_list: The value is entity_type. Provides a list of entities using the EntityListController of the respective entity. E.g. _entity_list: view_mode returns the render array of a list of view modes.
    • _entity_form: Similar to _form but will provide an edit form for an entity. Entity form handlers are defined in the entity metadata (annotation). For example, _entity_form: node.default will display the default node form. "node" in node.default refers to the entity ID, and "default" refers to the form handler key.
    • _title (optional): The page title for the route. May differ from the menu link title.
    • _title_arguments (optional): Additional arguments for the title text passed along to t().
    • _title_context (optional): Additional context information for the title text passed along to t().
    • _title_callback (optional): A PHP callable (typically classname::method) returning the page title for the route.
  • methods (optional): In addition to the URL, you can also match on the method of the incoming request (i.e. GET, HEAD, POST, PUT, DELETE). Enclosed in brackets, separated by commas, e.g. [GET, HEAD]
  • requirements (required): Determines what conditions must be met in order to grant access to the route. Is an array of one or more of the following:
    • _permission: A permission string (e.g., _permission: 'access content'). You can specify multiple permissions by separating them with ',' (comma) for AND logic or '+' (plus) for OR logic. E.g., _permission: 'access content,access user profiles' Module-specific permission strings can be defined in my_module.permissions.yml. See for details.
    • _role: A specific Drupal role, eg. 'administrator'. You can specify multiple ones via "," for AND and "+" for OR logic. Note that, since the roles available may differ between sites, it's recommended to use permission based access restriction when possible.
    • _access: Set this to "'TRUE'" (with the single quotes) to have access granted to this route in all circumstances .
    • _entity_access: In the case where an entity is part of a route, can check a certain access level before granting access (e.g., _entity_access: 'node.view'). You can also specify how the entity should be validated (e.g., node: \d+). This is useful if you have a route /foo/{node} and /foo/bar where {node} is a node id. /foo/bar won't match /foo/{node} because bar isn't numeric (see book.routing.yml for an example).
    • _custom_access: TODO. See Access checking on routes for details.
    • _format: Use this to check the type of the request. For example, you can have _format: json and only match requests where the Accept header is json.
    • _content_type_format: Use this to check the content type of the request. For example, you can have _content_type_format: json and only match requests where the Content-type header is json. See API doc for ContentTypeHeaderMatcher.php to get an idea how it works.
    • _module_dependencies: Optionally use this key to specify one or more modules that are required to serve this path. You can combine module names with a + (plus) for an AND relationship or , (comma) for an OR relationship. For example, _module_dependencies: 'node + search' means both node and search are needed, _module_dependencies: 'node, search' means either node or search are needed. If your module already depends on other modules for its behavior (via info.yml dependencies), there is, of course, no need for specifying the dependency here as well; however, for optional dependencies, where routes are provided only if those optional dependent modules are also enabled, this is a useful option.
    • _csrf_token: should be used and set to 'TRUE' for any URLs that perform actions or operations that do not use a form callback. See Access checking on routes for details.
    • _method: (OBSOLETE as of Symfony 3, read change record. Use 'methods.') Optionally use this key to restrict the route to specific HTTP methods. Defaults to GET and POST. Expects a string. To restrict to more than one method concatenate with '|'. Example: 'GET|POST'.
  • options (optional): Additional options on how the route should interact. Common options are:
    • _access_mode: (ANY / ALL (default) For security reasons, this option has been removed. #2431281: Drop support for _access_mode routes and always assume ALL
    • _admin_route: Indicates should admin theme be used for this route or not.
    • _auth: The default authentication manager (Ex: _auth: ['basic_auth', 'cookie'] ) that enables developers to limit the set of allowed authentication mechanisms to the route. Check Authentication API for more details. 
    • _theme: TODO
    • no_cache: Set this to 'TRUE' to mark route response as uncacheable.
    • parameters: If your parameter name from the path doesn't match any entity name you can translate the parameter to an entity here. And, as you can't have two parameters with the same name, let's say {user}, you can map the unmatched entity here.
      • second_user: The parameter name from the path. In this case it's {second_user}.
        • type: This is where we tell what entity to use. In this example we can just write entity: user, to map the parameter to the user entity.

Here's a more complex annotated example:


# Each route is defined by a machine name, in the form of module_name.route_name.
  # The path always starts with a leading forward-slash.
  path: '/book'
  # Defines the default properties of a route.
    # For page callbacks that return a render array use _controller.
    _controller: '\Drupal\book\Controller\BookController::bookRender'
  # Require a permission to access this route.
    _permission: 'access content'

  # This path takes dynamic arguments, which are enclosed in { }.
  path: '/book/export/{type}/{node}'
    # Because this route does not return HTML, use _controller.
    _controller: '\Drupal\book\Controller\BookController::bookExport'
    _permission: 'access printer-friendly version'
    # Ensure user has access to view the node passed in.
    _entity_access: 'node.view'
    # Enable the admin theme for this route.
    _admin_route: TRUE

Passing arguments to controllers

All keys under the defaults section which do not start with an underscore will be passed in as arguments to the controller. Name your arguments appropriately for the arguments of the controller. For example a routing.yml file with the following:

  path: '/example' 
    _controller: '\Drupal\example\Controller\ExampleController::content' 
    custom_arg: 12
    _permission: 'access content' 

Will pass on $custom_arg to the controller, so your content method can take $custom_arg:

  // ...
  public function content(Request $request, $custom_arg) {
    // Now can use $custom_arg (which will get 12 here) and $request.

Combine this with dynamic routes to get true flexibility.