Custom CRUD API in Drupal 9
Tags: php, drupal, drupal9, api
Published: 2022-01-09 14:43:25

We will be creating a custom API for our [custom module in Drupal 9](https://dev.to/rahulk1011/custom-module-in-drupal-9-3n8j) to perform basic CRUD operations using API. We will perform the operations on our custom content type.

**Step 1: Creating new folders in the Custom Module**
We will create a new folder in our custom module folder (candidates) as the following structure. We will place the API codes in this folder.

![Controller folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iwrxwn2i7fasftn4zskd.jpg)

**Step 2: Creating CandidatesController.php file**
In this folder, we will create a new file called 'CandidatesController.php'.

```
<?php
/**
* @file
* Contains \Drupal\candidates\Controller\CandidatesController.
*/
namespace Drupal\candidates\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\user\Entity\User;
use Drupal\node\Entity\Node;
use Drupal\Core\Entity;
use Drupal\taxonomy\Entity\Term;

//Controller routines for candidates routes

class CandidatesController extends ControllerBase {
}
```

**Step 3: Writing the APIs for CRUD operations**
Now, we will write the different APIs for adding, fetching, editing & deleting the records.
Get All Candidates Details API: get_candidate_detail
Add Candidates Details API: add_candidate_detail
Fetch Single Candidate Details after adding: fetch_candidate_detail
Edit Candidates Details API: edit_candidate_detail
Delete Candidates Details API: delete_candidate

```
<?php
/**
* @file
* Contains \Drupal\candidates\Controller\CandidatesController.
*/
namespace Drupal\candidates\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\user\Entity\User;
use Drupal\node\Entity\Node;
use Drupal\Core\Entity;
use Drupal\taxonomy\Entity\Term;

//Controller routines for candidates routes

class CandidatesController extends ControllerBase {
	/**
	* Get All Candidates Details API
	*/
	public function get_candidate_detail(Request $request) {
		global $base_url;
		try{
			$content = $request->getContent();
			$params = json_decode($content, TRUE);
			$uid = $params['uid'];
			$user = User::load($uid);
			$all_candidates = array();

			$query_string = "SELECT node_field_data.langcode AS node_field_data_langcode, node_field_data.created AS node_field_data_created, node_field_data.nid AS nid
			FROM {node_field_data} node_field_data
			WHERE (node_field_data.status = '1') AND (node_field_data.type IN ('candidate'))
			ORDER BY node_field_data_created ASC";
			$candidate_fetch_details = \Drupal::database()->query($query_string)->fetchAll();

			foreach($candidate_fetch_details as $key => $node_id){
				$nodeid = $node_id->nid;
				$node = Node::load($nodeid);
				
				$candidate_details['candidate_id'] = $nodeid;
				$candidate_details['candidate_name'] = $node->get('title')->value;
				$date = date_create($node->get('field_birth_date')->value);
				$birth_date = date_format($date, "d/m/Y");
				$candidate_details['candidate_dob'] = $birth_date;
				$candidate_details['candidate_gender'] = $node->get('field_gender')->value;
				$candidate_details['candidate_mobile'] = $node->get('field_mobile')->value;
				$candidate_details['candidate_email'] = $node->get('field_email_id')->value;
				$candidate_details['candidate_city'] = $node->get('field_city')->value;
				$candidate_details['candidate_country'] = $node->get('field_country')->value;
				$candidate_details['candidate_description'] = $node->get('field_description')->value;
				array_push($all_candidates, $candidate_details);
			}
			$final_api_reponse = array(
				"status" => "OK",
				"message" => "All Candidate Details",
				"result" => $all_candidates
			);
			return new JsonResponse($final_api_reponse);
		}
		catch(Exception $exception) {
			$this->exception_error_msg($exception->getMessage());
		}
	}

	/**
	* Add Candidates Details API
	*/
	public function add_candidate_detail(Request $request){
		global $base_url;
		try{
			$content = $request->getContent();
			$params = json_decode($content, TRUE);

			$uid = $params['uid'];
			$user = User::load($uid);

			$date = explode('/', $params['candidate_dob']);
			$birth_date = $date[2] . "-" . $date[1] . "-" . $date[0];
			
			$newCandidate = Node::create([
				'type' => 'candidate',
				'uid' => 1,
				'title' => array('value' => $params['candidate_name']),
				'field_birth_date' => array('value' => $birth_date),
				'field_gender' => array('value' => $params['candidate_gender']),
				'field_mobile' => array('value' => $params['candidate_mobile']),
				'field_email_id' => array('value' => $params['candidate_email']),
				'field_city' => array('value' => $params['candidate_city']),
				'field_country' => array('value' => $params['candidate_country']),
				'field_description' => array('value' => $params['candidate_description']),
			]);

			// Makes sure this creates a new node
			$newCandidate->enforceIsNew();
			
			// Saves the node, can also be used without enforceIsNew() which will update the node if a $newCandidate->id() already exists
			$newCandidate->save();
			$nid = $newCandidate->id();
			$new_candidate_details = $this->fetch_candidate_detail($nid);
			$final_api_reponse = array(
				"status" => "OK",
				"message" => "Candidate Details Added Successfully",
				"result" => $new_candidate_details,
			);
			return new JsonResponse($final_api_reponse);
		}
		catch(Exception $exception) {
			$this->exception_error_msg($exception->getMessage());
		}
	}

	public function fetch_candidate_detail($nid){
		if(!empty($nid)){
			$node = Node::load($nid);

			$date = date_create($node->get('field_birth_date')->value);
			$birth_date = date_format($date, "d/m/Y");
			$candidate_details['candidate_name'] = $node->get('title')->value;
			$candidate_details['candidate_dob'] = $birth_date;
			$candidate_details['candidate_gender'] = $node->get('field_gender')->value;
			$candidate_details['candidate_mobile'] = $node->get('field_mobile')->value;
			$candidate_details['candidate_email'] = $node->get('field_email_id')->value;
			$candidate_details['candidate_city'] = $node->get('field_city')->value;
			$candidate_details['candidate_country'] = $node->get('field_country')->value;
			$candidate_details['candidate_description'] = $node->get('field_description')->value;

			$final_api_reponse = array(
				'candidate_detail' => $candidate_details
			);
			return $final_api_reponse;
		}
		else{
			$this->exception_error_msg("Candidate details not found.");
		}
	}

	/**
	* Edit Candidates Details API
	*/
	public function edit_candidate_detail(Request $request){
		global $base_url;
		try{
			$content = $request->getContent();/* reads json input from login API callback */
			$params = json_decode($content, TRUE);

			$uid = $params['uid'];
			$user = User::load($uid);

			$nid = $params['nid'];
			$date = explode('/', $params['candidate_dob']);
			$date_of_birth = $date[2] . "-" . $date[1] . "-" . $date[0];

			if(!empty($nid)){
				$node = Node::load($nid);
				$node->set("field_birth_date", array('value' => $date_of_birth));
				$node->set("field_gender", array('value' => $params['candidate_gender']));
				$node->set("field_mobile", array('value' => $params['candidate_mobile']));
				$node->set("field_email_id", array('value' => $params['candidate_email']));
				$node->set("field_city", array('value' => $params['candidate_city']));
				$node->set("field_country", array('value' => $params['candidate_country']));
				$node->set("field_description", array('value' => $params['candidate_description']));
				$node->save();
				$final_api_reponse = array(
					"status" => "OK",
					"message" => "Candidate Details Updated Successfully",
				);
			}
			else{
				$final_api_reponse = array(
					"status" => "FAIL",
					"message" => "Candidate ID is reqired",
				);
			}
			return new JsonResponse($final_api_reponse);
		}
		catch(Exception $exception) {
			$this->exception_error_msg($exception->getMessage());
		}
	}

	/**
	* Delete Candidates Details API
	*/
	public function delete_candidate(Request $request){
		global $base_url;
		try{
			$content = $request->getContent();
			$params = json_decode($content, TRUE);
			$nid = $params['nid'];
			if(!empty($nid)){
				$node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
				$node->delete();
				$final_api_reponse = array(
					"status" => "OK",
					"message" => "Candidate record has been deleted successfully",
				);
			}
			return new JsonResponse($final_api_reponse);
		}
		catch(Exception $exception) {
			$web_service->error_exception_msg($exception->getMessage());
		}
	}
}
```

**Step 4: Adding the API in routing.yml**
Now we will add the API paths in the candidates.routing.yml file

```
get_candidate_detail:
 path: 'api/get_candidate_detail'
 defaults: { _controller: '\Drupal\candidates\Controller\CandidatesController::get_candidate_detail' }
 methods: [POST]
 requirements:
  _permission: 'access content'

add_candidate_detail:
 path: 'api/add_candidate_detail'
 defaults: { _controller: '\Drupal\candidates\Controller\CandidatesController::add_candidate_detail' }
 methods: [POST]
 requirements:
   _permission: 'access content'

edit_candidate_detail:
 path: 'api/edit_candidate_detail'
 defaults: { _controller: '\Drupal\candidates\Controller\CandidatesController::edit_candidate_detail' }
 methods: [POST]
 requirements:
   _permission: 'access content'

delete_candidate :
 path: 'api/delete_candidate'
 defaults: { _controller: '\Drupal\candidates\Controller\CandidatesController::delete_candidate' }
 methods: [POST]
 requirements:
   _permission: 'access content'
```

**Step 5: Using the APIs**
Using Postman, we can use the different APIs to add, edit, delete & fetch data from our custom content type.
We will have to set the header Content-Type as application/json & method POST.
The following params can be used to add candidate data:

```
{
    "candidate_name" : "Rahul Khan",
    "candidate_dob" : "10/12/1990",
    "candidate_gender" : "male",
    "candidate_mobile" : "9876543210",
    "candidate_email" : "rahul@khan.com",
    "candidate_city": "Bangalore",
    "candidate_country" : "India",
    "candidate_description" : "Hello. Test description."
}
```
For editing, we can use the above params to edit data. For deleting a record, we will need to pass candidate_id (nid).