A custom module is the primary way backend behavior is added to a Drupal site. While configuration controls how content is structured and displayed, custom modules define how Drupal behaves, how data flows, and how systems integrate.
In simple terms, custom modules are where Drupal stops being just a CMS and starts acting like a full backend framework.
This article is the first article in the Level 2 – Backend Development series. It sets the foundation for everything that follows, including routing, controllers, forms, AJAX, entities, queues, cron jobs, and Drush commands.
Rather than learning concepts in isolation, this series follows a learning‑by‑building approach. We will create and extend a real custom module step by step. Each new backend topic builds on the same module, so concepts connect naturally and mirror real project work.
By the end of Level 2, you will not just understand backend APIs, you will understand how Drupal backend architecture fits together as a system.
1. Why Custom Modules Are the Entry Point to Backend Drupal
Drupal backend development starts when configuration is not enough.
As soon as you need to:
- Control logic
- Fetch or store custom data
- Integrate external systems
- Build workflows
You need a custom module.
A custom module is how you tell Drupal what new behavior exists and when it should run.
2. Where Custom Modules Live
All custom modules live here:
/web/modules/custom/
Example:
/web/modules/custom/weeklydrupal_demo/
Drupal automatically scans this directory and discovers modules.
3. The Minimum Required File: *.info.yml
Every Drupal module must have an .info.yml file. Without it, Drupal will not recognize the module.
Example: weeklydrupal_demo.info.yml
name: WeeklyDrupal Demo
type: module
description: Demonstrates backend concepts for WeeklyDrupal
core_version_requirement: ^9 || ^10
package: WeeklyDrupal
4. Required vs Optional Keys in info.yml
Required keys
| Key | Purpose |
|---|---|
| name | Human-readable name shown in admin UI |
| type | Must be module |
| core_version_requirement | Defines compatible Drupal core versions |
If any of these are missing, Drupal will not load the module.
Common optional keys
| Key | Purpose |
|---|---|
| description | Explains what the module does |
| package | Groups modules in admin UI |
| dependencies | Ensures other modules are enabled first |
Example with dependencies:
dependencies:
- drupal:node
- drupal:user
5. What Happens When Drupal Reads info.yml
When you enable a module:
- Drupal reads the info.yml file
- Registers the module internally
- Makes it available for routing, services, hooks, and plugins
At this stage, no PHP code has executed yet.
6. Adding PHP Code: The .module File
The .module file is optional but commonly used.
Purpose:
- Implements hooks
- Acts as an integration layer
Example: weeklydrupal_demo.module
<?php
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function weeklydrupal_demo_help($route_name, RouteMatchInterface $route_match) {
if ($route_name === 'help.page.weeklydrupal_demo') {
return '<p>This module demonstrates backend Drupal concepts.</p>';
}
}
Explanation
- Function name starts with the module name
- Drupal calls this function automatically at the correct time
- Your code does not decide when to run; Drupal does
7. Introducing Routing (Backend Entry Point)
Backend logic usually starts with a route.
weeklydrupal_demo.routing.yml
weeklydrupal_demo.page:
path: '/weeklydrupal/demo'
defaults:
_controller: '\Drupal\weeklydrupal_demo\Controller\DemoController::page'
_title: 'WeeklyDrupal Demo'
requirements:
_permission: 'access content'
8. How Drupal Finds the Controller Class
The controller definition:
_controller: '\Drupal\weeklydrupal_demo\Controller\DemoController::page'
Means:
Drupal\weeklydrupal_demomatches the module nameControlleris a folder undersrc/DemoControlleris the PHP classpageis the method Drupal calls
Drupal uses PSR-4 autoloading to locate the file.
9. Controller File Explained
File path
weeklydrupal_demo/src/Controller/DemoController.php
Code
<?php
namespace Drupal\weeklydrupal_demo\Controller;
use Drupal\Core\Controller\ControllerBase;
class DemoController extends ControllerBase {
public function page() {
return [
'#markup' => $this->t('Hello from WeeklyDrupal backend module'),
];
}
}
Explanation
- Namespace maps to folder structure
ControllerBaseprovides Drupal helpers- Method returns a render array, not HTML
Drupal converts the render array into HTML.
10. Why Controllers Should Stay Thin
Controllers should:
- Handle requests
- Call services
- Return render arrays
Controllers should not:
- Contain business logic
- Query databases
- Call external APIs
That work belongs in services.
11. Introducing Services (Backend Logic Layer)
Services hold reusable backend logic.
weeklydrupal_demo.services.yml
services:
weeklydrupal_demo.message_generator:
class: Drupal\weeklydrupal_demo\Service\MessageGenerator
Service class
<?php
namespace Drupal\weeklydrupal_demo\Service;
class MessageGenerator {
public function getMessage(): string {
return 'This message comes from a backend service.';
}
}
12. Injecting the Service into the Controller
use Drupal\weeklydrupal_demo\Service\MessageGenerator;
class DemoController extends ControllerBase {
protected MessageGenerator $generator;
public function __construct(MessageGenerator $generator) {
$this->generator = $generator;
}
public static function create($container) {
return new static(
$container->get('weeklydrupal_demo.message_generator')
);
}
public function page() {
return [
'#markup' => $this->generator->getMessage(),
];
}
}
Explanation
- Drupal's service container creates the service
- The controller does not build dependencies itself
- This pattern is called dependency injection
13. Learning by Building: WeeklyDrupal Demo Module
Throughout Level 2, we will extend this same module.
Planned additions:
- Custom routes
- Forms and validation
- AJAX interactions
- Queue and batch processing
- Cron jobs
- Drush commands
Each topic builds on the previous one using real code.
14. Why This Topic Comes First
Every backend feature in Drupal depends on custom modules.
If you understand this topic well:
- Routes make sense
- Forms feel structured
- Services feel natural
- Architecture decisions become clear
This is the foundation of Backend Development.