orest's avatar
Level 13

organise routes that return view and json response

I'm building an MPA but I use Vue in many parts of the application.

I have separate routes and controllers for view and json responses

Currently, I have all the routes in the web.php but because they are quite a few routes I'd like to organise them.

How would you organise the routes in this case ?

Is it ok if I put the routes that return json in the api.php file and also change the middleware from api to web ?

0 likes
6 replies
automica's avatar
automica
Best Answer
Level 54

If you wish to split up your web routes to make them more manageable, then there’s no issue to create more route files specific to the type of routes you are defining. I would group by prefix rather than response type though, even if some return json and others are full blades. See https://www.itsolutionstuff.com/post/how-to-create-custom-route-file-in-laravelexample.html

I don’t recommend changing the middleware for api.php just create more web routes as you require.

1 like
orest's avatar
Level 13

@automica

i created another route file named ajax.php where i store all the routes that return json response.

All the requests to the ajax.php routes are made using axios

I did use a prefix for this, all routes in ajax.php have a prefix ajax

I am not sure what you mean by grouping by prefix rather than response because in my case the prefix ajax denotes that those routes receive ajax requestsand therefore json responses are returned.

In the article that you sent the author uses prefixes such as user and admin and therefore it makes sense to return either json or view from those routes.

automica's avatar

With grouping by prefix, I probably meant something like a route for a specific role (eg /admin)

As for your naming. If you hit a route in your ajax.php via your browser (if it’s a GET) you’ll see data returned. In that case you haven’t sent an Ajax request (or used axios) so your route name doesn’t really make sense.

If you are just doing this to simply your routes file, ensure you are using route groups as this will allow you to nest your routes and will be clearer how they relate to each other.

https://laravel.com/docs/8.x/routing#route-groups

1 like
orest's avatar
Level 13

@automica

i use Vue in many parts of the app so

  • The requests from Vue components that use axios hit the routes in ajax.php
  • The requests from Vue components that just use location.href to redirect hit the routes in web.php ( There are cases where i don't need to send an ajax request from a Vue component and instead i use location.href )
  • The requests from blade hit the routes in web.php

Does this approach make any sense ?

I was using groups before but i ended up with too many routes in the same file and it was bugging me.

automica's avatar

I don’t see the issue with having a big routes file with its contents in groups. If your app is a SPA then I would put all routes in routes/api and use a token to authenticate the request.

If your site is a mix of standard php but with some embedded Vue components then I might stick to using just routes/web.

Your routes should be organised by resource which will make it easier to navigate through a large file.

trin's avatar

a couple api tips

  • "agreement" with yourself about response structure like
// success response
{
	"success": true,
	"data": data
}

// fail response
{
	"success": false,
	"error": error
}

u can add more specific structure like "notification". all requests, in addition to primary data, contain a toast notification data, if necessary (for example, "you have new answers on your topic")

  • dont forget about versioning api with backward compatibility. there are several techniques, but here, for example, how I do
// file structure
- app/
-- Http/
---- Controllers/
------ v1/
-------- UserController.php
------ v2/
-------- UserController.php
------ Controller.php


// Controller.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Routing\Controller as BaseController;

class Controller extends BaseController
{
    final protected function success($result, int $code = 200): JsonResponse
    {
        $response = [
            'success' => true,
            'data' => $result,
        ];
        return response()->json($response, $code);
    }
    final protected function error($error, int $code = 404): JsonResponse
    {
        $response = [
            'success' => false,
            'error' => $error,
        ];

        return response()->json($response, $code);
    }


// v1/UserController.php
<?php

namespace App\Http\Controllers\v1;

use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use Tymon\JWTAuth\Exceptions\JWTException;
use App\Models\User;

class UserController extends Controller
{
    public function logout()
    {
        try {
            auth('api')->logout();
            return $this->success(true);
        } catch (JWTException $exception) {
            return $this->error($exception, 401);
        }
    }
    public function info()
    {
        $user = auth()->user();
        return $this->success(new UserResource($user));
    }


// v2/UserController.php
<?php

namespace App\Http\Controllers\v2;

use App\Http\Resources\UserResource;
use App\Models\User;

class UserController extends \App\Http\Controllers\v1\UserController
{
    public function info()
    {
        $user = auth()->user();

		$user->additionalDataAndRelations();

        return $this->success(new UserResource($user));
    }


// route/api/v1api.php
<?php

namespace App\Http\Controllers\v1;

use Illuminate\Support\Facades\Route;

Route::group([
    'prefix'     => '/v1',
], function ($router) {
    Route::middleware(['auth:api'])->group(function () {
    	Route::group(['prefix' => 'user'], function () {
        	Route::get('logout', [UserController::class, 'logout']);
        	Route::get('info', [UserController::class, 'info']);
		});
    });
});


// route/api/v2/api.php
<?php

namespace App\Http\Controllers\v2;

use Illuminate\Support\Facades\Route;

Route::group([
    'prefix'     => '/v2',
], function ($router) {
    Route::namespace('v2')->group(function () {
        Route::middleware(['auth:api'])->group(function () {
    		Route::group(['prefix' => 'user'], function () {
        		Route::get('logout', [UserController::class, 'logout']);
        		Route::get('info', [UserController::class, 'info']);
			});
		});
    });
});

at all u have routing

/api/v1/user/logout
/api/v1/user/info
/api/v2/user/logout
/api/v2/user/info

when u request to /api/v1/user/logout, u call v1/UserController.php@logout. when u request to /api/v2/user/logout, u call v1/UserController.php@logout too. because v2/UserController.php extends v1/UserController.php and not override logout method. and if request to /api/v2/user/info, u get user info with additional data or/and relations. if request to /api/v1/user/info -- only user data. it is often need to backward compatibility for old api user, like old mobile app etc.

  • often u need special route like /api/v1/summary, who return
{
	"success": true,
	"data": {
		name: "my best app",
		"minimal": {
			"ios": 2.1,
			"android": 3,
			"admin": 1
		}
	}
}

it is for notification old mobile client about need to update. or complete blocking old app

1 like

Please or to participate in this conversation.