Services API
The Services API is Drupal's dependency injection container, a registry of replaceable objects that classes receive through their constructors.
In Drupal, a service is an object that the service container creates and manages, such as the objects for database access, sending email or translating text. The Services API is the system around it. A module declares a service in a *.services.yml file, and other code asks the container for it by name. Drupal adopted the design from the Symfony DependencyInjection component.
How services work in Drupal 11
A service has a unique machine name, a class and, when the class needs other services, a list of arguments. Core defines its services in core.services.yml. Core modules and contributed modules define theirs in MODULE_NAME.services.yml. A definition from the core documentation looks like this:
path_alias.manager:
class: Drupal\path_alias\AliasManager
arguments: ['@path_alias.repository', '@path_alias.prefix_list', '@language_manager']
An @ before a name injects another service, a name between % signs refers to a parameter, and @? marks an optional dependency. Other keys include factory, parent and tags. The container can also autowire arguments from the constructor's type hints, and core's own service files use autowire: true on some services.
The point of the container is replaceability. Code that asks for a service by name gets whichever class is currently registered, so another module can provide an alternative class for the same service. Drupal compiles all definitions into one container and caches it; a full cache rebuild invalidates and rebuilds that cached container.
Example
Define a service in my_module.services.yml:
services:
my_module.greeter:
class: Drupal\my_module\Greeter
arguments: ['@current_user']
<?php
namespace Drupal\my_module;
use Drupal\Core\Session\AccountProxyInterface;
class Greeter {
public function __construct(protected AccountProxyInterface $currentUser) {}
public function greet(): string {
return 'Hello, ' . $this->currentUser->getDisplayName();
}
}
A controller or a plugin that has a create() method receives the container and calls $container->get('my_module.greeter'). Code with no such method can use \Drupal::service('my_module.greeter').
Common mistakes
- Creating the class with
new. Core's topic says to always get a service through the container, so that an overriding class is honored. - Reaching for
\Drupal::service()inside classes. Service location hides dependencies and makes unit tests harder. Inject the service instead. - Skipping the cache rebuild. A new or changed entry in a services file is not used until the cached container is rebuilt. Drush's
cache:rebuildcommand, aliascr, rebuilds all caches. - Confusing it with web services. The Services API in core does not expose REST endpoints. Those come from the JSON:API and RESTful Web Services modules.
For marketing and content teams
You never edit services, but they explain why a developer can change how the site behaves in one place. Swapping the class behind a service such as path handling or mail changes every feature that asks for it, without touching those features. If your team needs to feed content to an app or another system, that is a web services question, and the answer is a different module, not this API. For the surrounding extension points, see Plugin API and hooks.
Sources
- Drupal API: Services and Dependency Injection Container topic (core.api.php, 11.2.0)
- Drupal.org: Services and dependency injection
- Drupal.org: Structure of a service file
- ServiceProviderBase, Drupal 11.2.0 source
- drupal_flush_all_caches(), Drupal 11.2.0 source
- core.services.yml, Drupal 11.2.0 source
- Drupal.org project: Services (contributed module)
Questions about Services API
Cite this definition
"Services API." DXPR Drupal glossary, https://dxpr.com/hc/documentation/learn-drupal/drupal-glossary/services-api. Last updated October 26, 2026.