Structure of routes

Last updated on
20 November 2023

This documentation needs review. See "Help improve this page" in the sidebar.

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

  • path (required): The URL to the route, with a leading forward slash (for example path: '/book'). You can use dynamic properties by including them in curly braces. (for example path: '/node/{node}/outline'). These will be passed along as arguments via parameter converters to the controller/form. Note that the first item of the path cannot be an argument, and must be a string. You can also define optional parameters at the end of your path (See 'Optional Parameters' on Using parameters in routes).
  • defaults (required): Defines the default properties of a route. Provide one of the following to specify how the output is generated:
     
    • _controller: A Callable. Allows you to map to a callable function in one of the following ways:
       
      • Class::method:\Drupal\[my_module_name]\Controller\[ClassName]::[method]

        example: _controller: '\Drupal\acme\Controller\AcmeController::build'

        This will execute the build() method  in the AcmeController class, whose namespace is Drupal\acme\Controller.

        Note that this value is not a path on the file system, but rather a PSR-4 namespace. Also note that module names are in snake_case, while class names are in PascalCase. See also An introductory example to Drupal 8 routes and controllers.

      • Service: controller.test:[method], where "controller.test" is a Drupal 8 service, defined in a *.services.yml file.

        example: _controller: 'acme.controller:build'

        This will execute the build() method in the class defined in the acme.services.yml for the acme.controller service.

    • _form: A class name implementing Drupal\Core\Form\FormInterface. See form API in Drupal 8 for an example.
    • _entity_view: The value is entity_type.view_mode. It will find an entity in the path and render it in the given view mode. For example, _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. It provides a list of entities using the EntityListController of the respective entity. For example, _entity_list: entity_type returns the render array of entities list.
    • _entity_form: It is similar to _form, but it 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.
  • defaults (optional): Additionally, you may provide optional defaults to specify how the output is generated:
    • _title: The page title for the route. It may differ from the menu link title.
    • _title_arguments: Additional arguments for the title text passed along to t().
    • _title_context: Additional context information for the title text passed along to t().
    • _title_callback: A PHP callable (typically classname::method) returning the translated page title for the route. The method should return an instance of \Drupal\Core\StringTranslation\TranslatableMarkup.
  • 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, for example [GET, HEAD].
  • requirements (required): Determines what conditions must be met to grant access to the route. All conditions must be met to gain access. It is an array of one or more of the following:
    • _permission: A permission string (for example _permission: 'access content'). You can specify multiple permissions by separating them with ',' (comma) (_permission: 'access content,access user profiles') for AND logic or '+' (plus) for OR logic (_permission: 'access content+access user profiles' means a visitor needs either the access content permission or the access user profiles permission to view the page.) Module-specific permission strings can be defined in my_module_name.permissions.yml. See hook_permission() replaced with permissions defined in a my_module_name.permissions.yml file for details.
    • _role: The machine name of one or more roles. Multiple roles can be separated by either a comma (,) or a plus (+). Multiple roles separated with a comma (,) will require all of the roles to gain access to the route. Multiple roles separated by a plus (+) will allow access to the route when the user has any of the roles. Example:
      • _role: organizer,participant,controller,site_admin: Only users with all of these roles will be granted access to the route.
      • _role: organizer+participant+controller+site_admin: Any users with at least one of these roles will be granted access to the route.

      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 and in uppercase) 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 (_entity_access: 'node.view'). The format is [slug].[operation]. Typically, the slug is an entity type ID, but it can be any slug defined in the route. The route match parameter corresponding to the slug is checked to see if it is entity-like and implements EntityInterface. You can also specify how the entity should be validated (node: \d+). This is useful if the used routes are /foo/{node} and /foo/bar, where {node} is a node ID. /foo/bar won't match /foo/{node} because bar isn't a numeric value. (See book.routing.yml for an example.)
    • _entity_bundles: Deprecated. Use bundle for route parameters instead. In the case where an entity is part of a route, restrict to certain bundles. The format is [entity_type]:[bundle] (node:article). You can also specify multiple bundles, separating them with | (node:article|page).
    • _entity_create_access: Check access with create operation. See the related change record for example.
    • _custom_access: TODO. See Custom route access checking 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 '_format' query parameter 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 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.
    • _csrf_request_header_token: set to 'TRUE' to require X-CSRF-Token as an HTTP header. See CSRF token route protection moved out of the REST module to be available to other core systems and contrib to get more info.
    • _method: (OBSOLETE as of Symfony 3, read change record. Use 'methods: [POST]') 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'.
    • _access_user_register: Set this to 'TRUE' (with the single quotes) to have access granted to the route if the user is anonymous AND user registration is not set to "Administrators only" on the site.
    • _user_is_logged_in: Boolean. Use to require authentication, or require unauthenticated access to a route.
  • options (optional): Additional options on how the route should interact. Note that, unlike requirements, boolean values should not be quoted. 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: Indicate whether this is an admin route or not, so the admin theme is used. Defaults to TRUE for routes starting with '/admin', otherwise to FALSE, so is only required to override this behavior.
    • _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. 
    • _maintenance_access: Set this to TRUE to bypass the offline message and show the page normally while in maintenance mode.
    • _theme: TODO
    • no_cache: Set this to TRUE to mark route response as uncacheable.
    • parameters: See Using parameters in routes

Here's a more complex annotated example:

book.routing.yml

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

book.export:
  # This path takes dynamic arguments, which are enclosed in { }.
  path: '/book/export/{type}/{node}'
  defaults:
    # This route returns a Response object so also uses _controller
    _controller: '\Drupal\book\Controller\BookController::bookExport'
  requirements:
    _permission: 'access printer-friendly version'
    # Ensure user has access to view the node passed in.
    _entity_access: 'node.view'
  options:
    # 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 my_module_name.routing.yml file with the following:

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

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

  // ...
  public function content($custom_arg) {
    // Now you can use $custom_arg (which will get 12 here).
  }

Routing for Form:

module_name.route_machine_name:
    path: '/{module_name}/personal-info'
    defaults:
        _form: 'Drupal\{module_name}\Form\InfoForm'
        _title: 'Personal information'
    requirements:    
        _permission : 'custom_module_permission'

Where 

module_name: is machine name of your module.

custom_module_permission: name of the permission implemented in module_name.permission.yml file

Combine this with dynamic routes to get true flexibility.

Help improve this page

Page status: Needs review

You can: