I am working on a REST API. When trying to access a resource : we want to either give a 403 (Forbidden) or 404(Not Found) error. Our tables are :
CREATE TABLE `Action` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_By_Id` int(10) unsigned NOT NULL,
`name` varchar(60) NOT NULL,
`updated_action_at` datetime(3) DEFAULT NULL,
`created_At` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`notes` varchar(400) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `action_empId_fk` (`created_By_Id`),
CONSTRAINT `action_empId_fk` FOREIGN KEY (`created_By_Id`)
REFERENCES `Employee` (`id`) ON DELETE CASCADE,
) ENGINE=InnoDB AUTO_INCREMENT=502004 DEFAULT CHARSET=latin1
CREATE TABLE `ActionAssignedTo` (
`action_Id` int(10) unsigned DEFAULT NULL,
`assignee_Id` int(10) unsigned DEFAULT NULL,
KEY `actionassignedto_emp_id_foreign` (`emp_Id`),
KEY `actionassignedto_action_id_foreign` (`action_Id`),
CONSTRAINT `ActionAssignedTo_ibfk_1` FOREIGN KEY (`assignee_Id`)
REFERENCES `Employee` (`id`) ON DELETE CASCADE,
CONSTRAINT `ActionAssignedTo_ibfk_2` FOREIGN KEY (`action_Id`)
REFERENCES `Action` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `Employee` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`vendor_Id` int(10) unsigned DEFAULT NULL,
`name` varchar(40) NOT NULL,
`mobile_Number` varchar(15) NOT NULL,
`active` tinyint(1) DEFAULT '1',
`updated_At` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_At` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `employee_vendor_id_foreign` (`vendor_Id`),
CONSTRAINT `employee_vendor_id_foreign` FOREIGN KEY (`vendor_Id`)
REFERENCES `Vendor` (`vendor_Id`)
) ENGINE=InnoDB AUTO_INCREMENT=511 DEFAULT CHARSET=latin1
We are running a query to fetch an Action with id: 17 by an creator employee with id: 9 and vendor Id: 1 and this employee has been created the action so he can view it(business rule). Action can be assigned to multiple employees.
select Action.name,
group_concat(AssigneeNameTable.name) as assignedTo,
group_concat(AssigneeNameTable.id) as assignedToId,
ActionAssignedTo.action_Id as actionId
from Action
inner join Employee
on Action.created_By_Id = Employee.id
and Employee.vendor_Id = 1
inner join ActionAssignedTo
on Action.id = ActionAssignedTo.action_Id
and ActionAssignedTo.action_Id = 17
inner join Employee as AssigneeNameTable
on ActionAssignedTo.assignee_Id = AssigneeNameTable.Id
where Action.created_By_Id = 9
and Action.deleted_at is null
group by Action.id
limit 2
Now, lets say if the Action does not exist at all in the DB --> in that case the above query returns empty result set
the problem is we can not differentiate the query return empty set because
1. either the action with id:17 did not exist(404- Not Found)
2. or the business rule failed (as in the person requested the action was not
at all related to the action(403 - Forbidden).
One of the solution that I can think of is : First Run a Small query like :
select * from Action where id = 17
if this query returns an empty set that means the action does not exist in the db.
After this I run the Bigger query
Different combinations of result set (the number in the array indicates records returned) :
Small Query | Big Query | Interpretation
---------------------------------------
[0] | [0] | Resource Not Found(404)
[1] | [0] | Forbidden (403)
If the Small Query returns 0 result --> we can directly send 404 Error; else we execute the Big Query.
I used the concept of Left Outer Join as my friend suggested. Please find the new query below :
select *
from
(select id
from Action
where id = 17) AS act1
left Outer Join
(select Action.name,
group_concat(AssigneeNameTable.name) as assignedTo,
group_concat(AssigneeNameTable.id) as assignedToId,
ActionAssignedTo.action_Id as actionId
from Action
inner join Employee
on Action.created_By_Id = Employee.id
and Employee.vendor_Id = 1
inner join ActionAssignedTo
on Action.id = ActionAssignedTo.action_Id
and ActionAssignedTo.action_Id = 17
inner join Employee as AssigneeNameTable
on ActionAssignedTo.assignee_Id = AssigneeNameTable.Id
where Action.created_By_Id = 9
and Action.deleted_at is null
group by Action.id
limit 2) AS act2
on act1.id = act2.actionId
The concept is simple
if the output contains no result --> Object Not Found(404)
if the output contains the id
field but does not contain any single field from second sub-query that means the entity exists in db but business rules does not permit and hence Forbidden(403).