Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

takdw's avatar
Level 11

API development tips and pointers

I'm currently working on a simple project. Its one of those 'management systems'. For reasons I quite can't state, I opted to use an SPA approach to the front end using the awesome Vue.js. The back end is of course Laravel. I'll just say this: my api.php file is a mess. There are loads of routes defined with token guards, some inline callback functions and stuff. I was reading about good API development guidelines and I noticed that I am not following any of them. Let me give you an example:

Route::get('/departments/{department}/users', function() {
    return $department->users();
});
Route::get('/users', function() {
    return User::all(); // yes, I know, all() is bad
});

Okay, this might be not that bad but they are both basically querying from the same collection: User. And also the thing is, the second route should only be accessed by admins. The first route can be accessed by the admin, the department manager and the department members. First question is: is this a good approach? Should I just merge them to the second route and then use query attributes to select the type of data that should be returned? Something like GET /users?department=1 then do all the auth and role checking on a controller?

Here is another 'bad code':

Route::middleware(['scopes:admin'])->group(function () {
        Route::post('/departments', 'DepartmentController@store');
        Route::post('/departments/{department}/add-member', 'DepartmentController@addMember');
        Route::post('/departments/{department}/remove-member', 'DepartmentController@removeMember');
        Route::post('/departments/{department}/assign-manager', 'DepartmentController@assignManager');
        Route::post('/departments/{department}/revoke-manager', 'DepartmentController@revokeManager');
    });

add-member, remove-member, assign-manager, revoke-manager all break the rule/guideline that says we shouldn't have verbs in our APIs. And they are all basically doing a patch to one or another column in the Department or User model. What would be a better, more elegant and efficient way to do this?

Thanks,

0 likes
5 replies
manelgavalda's avatar

In my opinion the best option is to do it in a more CRUD way. Then you can use your api with basic crud operations. I think it's better to have 2 routes because they're doing different thinks, and it will be better when setting the roles for the different routes:

Route::get('/department-users/{department}', function() {
    return $department->users();
});
Route::get('/users', function() {
    return User::all();
});
Route::middleware(['scopes:admin'])->group(function () {
        Route::post('/departments', 'DepartmentController@store');
        Route::post('/department-members/{department}/store', 'DepartmentMemberController@store');
        Route::post('/department-members/{department}/destroy', 'DepartmentMemberController@destroy');
        Route::post('/department-managers/{department}/store', 'DepartmentManagerController@store');
        Route::post('/department-managers/{department}/destroy', 'DepartmentManagerController@destroy');
    });

This way you will only have basic crud operations organized between all the controllers, and you will only have 5 operations max per controller so it will look very clean.

Adam Wathan has a good laracon video explaining this: https://www.youtube.com/watch?v=MF0jFKvS4SI

takdw's avatar
Level 11

Thank you for your reply @globals. I was recently watching Jason McCreary's talk (https://www.youtube.com/watch?v=dccDqpmKVFM) and he mentioned something about Cruddy Controllers and to check out Adam's talk for more info. I have been meaning to watch it but I guess I forgot. Thank you for your suggestions and also for the link.

I'll leave this open for a while if there are any other people who want to add something.

1 like
primordial's avatar

@takdw I would suggest one controller per method when developing APIs. If you are developing a simple CRUD solution combining methods into one controller is easy but when you get beyond basic CRUD and need a "designer endpoint" life becomes ugly.

When developing CRUD I would have the following controllers in my suite

GetDepartmentController

GetDepartmentsController

PostDepartmentController

PatchDepartmentController

PutDepartmentController

DeleteDepartmentController

Each controller is prefixed with the appropriate HTTP Verb and inside each controller is one public method for example, "action".

Authentication/Authorisation is a middleware issue.

takdw's avatar
Level 11

@primordial This is a really interesting approach I never really thought about. Its good to know I could always resort to this when the application logic carried out in each endpoint becomes complex.

And you are right. I should leave the authorization/authentication logic to the middleware I'm already using. I'm using Passport and it works well, and these methods you mentioned above will make it even easier to guard routes. Thanks for sharing.

@globals I just finished watching Adam's talk and I just wanted to thank you again. Its an invaluable content. Taught me a lot.

1 like
primordial's avatar

Since adopting this pattern a couple of years ago I have never looked back. But 9-5 I work exclusively with APIs and decoupled front end solutions.

Please or to participate in this conversation.