Is it possible to create a second Laravel "api route" with a separate API KEY? - laravel

I'm new to Laravel and I am handed an existing application that is composed of two parts:
1 - An admin backend built on Laravel and uses Vueify
2 - The frontend website built on next.js and uses react components
The admin part communicates with Laravel using the "web routes" but also uses the "api routes" as well since the vue components make AJAX requests using those "api routes".
I am now tasked with "connecting" the frontend part to the laravel app. The frontend part will be using AJAX as well to communicate with laravel but I was told I should not use the same "api route" that is used by the admin backend because that has a lot more privileges that should not be accessible by the frontend. Basically it's a security risk and that I should somehow separate the two.
I'm not actually sure which term to use.. I initially thought it was called "channel" but I see that channel is one of the 4 "ways" of connecting to laravel (the other 3 being web, api and console). So I think routes is the term to use and forgive me for the double-quotes.
I have made a simple diagram to show the structure I mean. What I need to know is is there a way to create a second api route that would be used exclusively by the frontend and would include only a limited set of priviledges. I imagine something like /frontapi/ or /webapi/ as opposed to /api/ which is used now by the backend.
Thanks a lot for your help and please correct me if I am using wrong terminology.
EDIT
Thank you all for answering the part regarding separating the route prefix and the api route files.
One part of the question that I realized late that I hadn't made clear was the importance of separating the API Keys for both APIs since I think that is the main security issue and what would really make then two individual API "Channels or ways". I think that is one reason why I was confusing about the terminology because "way" sounded to me more separate that just a "route". I've also edited the question to reflect that. Thank you again for taking the time to help.

You can decompose routes in as many files as you want, you can also give each file its own prefix (like how api.php routes start with /api)
The modification need to be done in App\Providers\RouteServiceProvider
//in map() add $this->mapApiTwoRoutes()
public function map()
{
$this->mapApiRoutes();
$this->mapApiTwoRoutes();//<---this one
$this->mapWebRoutes();
}
//now add the method mapApiTwoRoutes
protected function mapApiTwoRoutes()
{
Route::prefix('api2')//<-- prefix in the url
->middleware('api')//<-- api middleware (throttle and such check App\Http\Kernal.php)
->namespace('App\Http\Controllers') //<-- you can modify the namespace of the controllers
->group(base_path('routes/apiTwo.php'));//<-- file containing the routes
}
And that's it.

You need to define a new route file, firstly add a new entry $this->mapApi2Routes(); in the map() function in app\Providers\RouteServiceProvider.
Then add a new function in that file, basically copying the mapApiRoutes() function, call it mapApi2Routes(). You can use different middleware etc. for the new file.
The last step would be adding a new file api2.php in the routes folder.

Related

How to Get Applied Middleware Name of Route into AppServiceProvider?

I am facing a problem, where some API is consuming Server Consumption a lot in AWS. so minimize this I have decided to divide my API into two-part that is (Read and Write) API. now the problem arises in Laravel 5.7.I am not getting how to identify which API is being used for reading and writing? I can do this just call a new method that overwrites my hostname from a Controller. but it is lazy task where i have 241 method in my project. for this, i have created separated Middleware.
Suppose i am calling Route::post('login', 'Api\AuthController#login')->middleware('ReadAPISourse');
Anyone has its better solution please tell.
right now i am calling middleware that name is ReadAPISourse in all get route. after that, i have fixed my problem.
Its answer was simple. just make a middleware and used that with a route.
Route::post('login', 'Api\AuthController#login')->middleware('ReadAPISourse');
but its after i am looking its better solution.
here the problem is the first system is booting with a DB connection then after it is being overwritten by ReadAPISourse.

VueJS SPA dynamic baseURL for axios

I've searched and searched and can't seem to find a pattern for this. I'd consider myself an intermediate Vue dev, however, the backend is my strong suit. I'm working on an app that will be white-labeled by resellers. While it's possible to have multiple builds, avoiding that would be ideal. The setup is a stand-alone vue-cli SPA connecting to a Laravel api backend and using the Sanctum auth package. So I need calls to the same domain. The issue: resellers will be on their own domain. The ask: Is there a pattern/solution for dynamically loading configs (mainly baseURL) for different domains (other items would by theme/stylesheet). Currently I have a few typical entries:
i.e. axios.defaults.baseURL = process.env.VUE_APP_API_BASE_URL
Basically, based on the domain the site is being served on, I'd like a dynamic/runtime config. I feel like this has been solved, but I can't seem to use the right search terms for some direction, so anything is helpful. I've tried a few things:
1) Parsing in js, but can't seem to get it to run early enough in the process to take effect? It seems to work, but I can't get it to "click"
2) Hit a public API endpoint with the current domain and get the config. Again, can implement, but can't seem to get it to inject into the Vue side correctly?
Any resources, pattern references or general guidance would be much appreciative to avoid maintaining multiple builds merely for a few variables. That said, I don't think there's much overhead in any of this, but also open to telling my I'm wrong and need multiple builds.
End Result
url visited is https://mydomaincom
then baseURL = https://api.mydomiancom
url visited https://resellerdomaincom
then baseURL=https://api.resellerdomaincom
I don't think there is a common pattern to solve your problem - I haven't found anything on the net.
The best software design solution could be the following:
have a single back-end
distribute only the client to your customers/resellers
Obviously the back end could see the domain of the application from which the request comes and manage the logic accordingly.
Good luck with your project.
Honestly how the question is put it's not really clear to me. Although my usual pattern is to:
Create an axios instance like so:
export const axiosInstance = axios.create({
// ...configs
baseURL: process.env.VUE_APP_URL_YOU_WOULD_LIKE_TO_HIT
})
and then whenever I make a request to some api, I would use this instance.
EDIT: According to your edit, you can either release the client to each customer, and have a .env file for each and every of them, or you can have a gateway system, where the client axios end point is always the same, hitting always the same server, and then from there the server decides what to ping, based on your own logic

Laravel 5.3 API Versioning

I am trying to get api versioning in place for an API I am working on, I found this post that explained how to do it using middleware and replacing a string in the route itself. Basically specifying routes like this.
Route::group(['middleware' => ['api-version']], function() {
Route::get('/endoint', ['uses' => '{api-namespace}\EndpointController#endpoint']);
});
However, when I attempt this I get the following error
Class App\Http\Controllers\{api-namespace}\EndpointController does not exist
It would appear that the container is verifying the existence of route controller files prior to running the middleware which does the replacing. I have added the middleware to the $routeMiddleware in the Http Kernel file.
How can I accomplish this before it checks for the existence of the file?
I thought about adding this to the applications global middleware but I do not want this to run on web only on api calls
Create a different file for next version of API has some downside.
You have to create all the routes from version 1
and in my case version 2 was just some changes to 3 requests. that was the time I felt we need a fallback for this kind of operation.
then I created a simple Laravel package to support Laravel API versioning it adds fallback functionality to routes. I personally needed this long ago but didn't realize it will be achieved with such a tiny package.
https://github.com/mbpcoder/laravel-api-versioning
The problem is that uses actually tries to retrieve a class and then call the method inside, you shouldn't be encouraged to put any parameters there so restrain from doing so, instead try grouping your api routes under certain prefix and middleware like so:
Route::prefix('XXXXXXX')->group(['middleware' => ['api-version']], function() {
Route::get('/endoint', 'EndpointController#endpoint');
});
Note: My above assumption is made out of that you didn't handle changing {api-namespace} inside of your middleware class properly.
Stepping through the code allowed me to see that this is already handled by Laravel and all i needed to do was create a routes/api/v2.php file with the routes for version 2. The only problem I see is having to duplicate all routes which did not change from version 1 to version 2. I may look into modifying my RouteServiceProvider to actually inherit previous versions if they are not overridden in the requested api version rather than duplicating the routes code for every api version.

Where to put ajax request in Ember tutorial app?

I want to add "Weather: 24C" to the rental-listing component of the super-rentals tutorial app.
Where would be the "best-practices" place to put this ajax request?
Ember.$.getJSON(`http://api.openweathermap.org/data/2.5/weather?q=${location}&APPID=${apiKey}`)
.then(function(json) {
return JSON.parse(json).main.temp;
});
Do I need to add a component, add a model, add a service, add a second adapter, modify the existing adapter? Something else? All of these? Is the problem that the tutorial uses Mirage? I ask this because when I think I'm getting close, I get an error like this:
Mirage: Your Ember app tried to GET
'http://api.openweathermap.org/data/2.5/weather?q=london&APPID=5432',
but there was no route defined to handle this request.
Define a route that matches this path in your
mirage/config.js file. Did you forget to add your namespace?
You need to configure mirage to allow you making calls to outside in case mirage is active; what I mean is using this.passthrough function within mirage/config.js, that is explained in api documentation quite well.
Regarding your question about where to make the remote call is; it depends:
If you need the data from the server to arrive in case a route is about to open; you should prefer putting it within model hook of the corresponding route.
If you intend to develop a component that is to be reused from within different routes or even from within different applications with the same remote call over and over again; you can consider putting the ajax remote call to a component. Even if that is not a very common case usually; it might be the case that a component itself should be wrapped up to fetch the data and display it by itself for reusing in different places; there is nothing that prevents you to do so. However by usually applying data-down action-up principle; generally the remote calls fall into routes or controllers.
Whether using an ember-data model is another thing to consider. If you intend to use ember-data; you should not directly use Ember.$.ajax but rather be using store provided by ember-data and perhaps providing your custom adapter/serializer to convert data to the format ember-data accepts in case the server do not match to the formats that ember-data accepts. In summary; you do not need to use models if you use pure ajax as you do in this question.

Getting a route to an external laravel instance

I have two separate Laravel instances/sites running on a server and want to be able to generate a url to a named route on one from code within the other.
For example the following named route exists in the first instance:
Route::get('users/my_account', array('uses' => 'UsersController#myAccount', 'as' => 'my_account'))
In the second instance I want to generate a url to the route above. Can anyone think of a clean way to do this, without explicitly knowing the url (i.e. only knowing the name of the route 'my_account')?
Basically I want to expose the RouteCollection of one site to the other...
That's a pretty interesting question. There's no natively supported way of doing that and, from what I know, it won't ever.
You could try loading the routes file of the first application, parsing it's configurations (you will need that for reverse routing), construct a Router instance and use it, but I'm sure it won't be simple at all.
If you have a really, I mean really, good reason to use reverse routes, you can try building a small API on the first application. That API should receive the parameters necessary, those used in url($params), and return the full url (with domain and everything). Although, this will introduce some serious performance issues.
IMHO, stick to hard coded my_account, leave a comment on the first application route and/or controller explaining that it's used on another project and move on :)
I may call this a dirty trick. Supposing you have the following structure in your file system, where both paths are Laravel apps:
/path/to/apps/app1
/path/to/apps/app2
And you want to load the routes file from app1 into app2. You can do it as follow:
include "../app1/apps/routes.php";
$url = URL::route('register');
VoilĂ ! But, although it worked for me there are some points to consider.
Include that file, will surely overwrites your current route collection for that process.
If that last is true, then you can have problem generating other URLs, maybe in your views.
The domain name will be of the current Laravel instance. It means that your URLs generated into app2 will hold the domain name of app1. I believe this is not what you want. But you can always generate non-absolute URLs with URL::route('register', null, false).

Resources