Laravel-json-api: Questions and Suggestions

Created on 20 Sep 2018  路  9Comments  路  Source: cloudcreativity/laravel-json-api

Dear @lindyhopchris ,

sorry for spamming issues in your repository - i really like the approach and this package looks very (!) powerful. As i am currently looking for a solution to "auto-generate" APIs, and stumbled upon your work, i would like to ask some questions. I hope you may help to tackle these issues and provide answers. Furthermore, some of my questions may also raise additional ideas for upcoming versions and new features.

Please note that I have not digged that much into the code, so some questions may sound stupid or have been discussed in other places (and i haven't read about it). So please apologize :thinking:

Questions

1) From the docs I read, that you define your Routes like this:

JsonApi::register('default', ['namespace' => 'Api'], function ($api, $router) {
    $api->resource('posts');
});

Are the routes added to the "default" Laravel Router? E.g., can you use the route() commands in order to generate URLs and so on? And does they appear when calling php artisan route:list or does the package use its own (custom) Router that is separate from Laravel?

2) The $api->resource('posts'); fully auto-generates the common CRUD routes and adds a controller (with a basic implementation to CRUD the resoures). In this case, you use the controller method names like described here ( https://laravel-json-api.readthedocs.io/en/latest/basics/controllers/#custom-controller ). Is it possible to adapt these method names? E.g., I would like to rename the read() method to readPostById() method or the create to createPost()... Is this possible or is this a naming convention that is "hard-coded" in this package?

3) How can i add "custom methods" (e.g., with custom routes) to my resource? I know, this may not be JSON:API compliant! But consider the following scenario:
I have a User model that has some basic attributes (username, email, firstname, ...) and complex attributes like settings (e.g., his default language, preferred configuration of the application and so on). In order to manage the "own profile" of the user (i.e., change username, email, firstname, ...) he may PATCH /users/1234 with the appropriate data. However, i would NOT like to send the settings information because this may be done in another workflow / wizard / ... . So i would like to add another route (e.g., PATCH /users/1234/setting) so change these values (just send a json and store it directly within the DB).

  • How can such routes be added?
  • How can i "manually" define a custom controller method for this route?
  • Are the hooks / events automatically assigned (because i change a resource so the updating / updated events may be triggered?)

4) From what i read, Schemas act as some kind of "output layer"; i.e., they define the structure of mapping "internal models" to "output structures" (similar to Transformers known from Fractal). Is it possible to assign multiple Schemas to one particular Resource? Consider, again, the User example. If a client browses all available Users (e.g., GET /users), I may want to provide only "public" information, like the username, firstname, lastname and so on. However, if the User accesses his own profile (e.g., GET /users/1234), i would like to apply a different Schema - that also provides information about his settings, his email and so on..

5) Similar to 3) and 4) I would like to ask, if it is possible to define a custom Validator for each route. I can imagine scenarios, where you can create a resource (e.g., register a new User), but you are not allowed to update certain attributes anymore (e.g., you may not change the username after registration!). This would require to have a) different Validators or b) have custom (business) logic to remove certain attributes.

6) Is there an (automated) approach to generate a fully fledged API doc? I found an older issue discussing this topic. In another project i tried to implement Swagger, but it was very (!) painful, and apidocjs is not actively maintained any more. However, i recently stumbled upon this package here ( https://github.com/saleem-hadad/larecipe ) that may be suitable. Please note that this project does not auto-generate the docs but rather uses markdown in order to generate a UI (it looks quite awesome, i think!)

7) How would you deal with different "API Realms" in the context of JSON:API? Consider the following example - you have a shop application and you have 2 different roles (customers / managers). In this application you need to show all Products (e.g., GET /products). However, for the customer, this list shall only contain Products that are flagged as is_active = true, while the managers should get all Products. Obviously, for better maintainance you would like to have 2 different controller functions - because it may easily be the case that you would like to apply different sorting strategies for customers in near future; "top-seller" products shall be ordered first, and so on...
From an API perspective, it may be suitable to provide different "realms" for your API URIs (e.g., GET /api/v1/shop/**customers**/products vs GET /api/v1/shop/**managers**/products) that invoke different controller methods. Is this possible and how would you apply such features? I am not even sure if this is JSON:API compliant - i think, the standard does not clearly tell how to deal with such scenarios!

Sorry for the long post and the huge amount of questions; I am quite sure other questions will arise :laughing:

Thank you very much for your time and effort reading / answering my questions!
All the best

question

All 9 comments

Hi! Here's some initial answers.

  1. All the routes are registered via the Laravel router, they all have route names and will all appear in route:list.

  2. There is no ability to renamed the controller methods, though I would be open to adding this feature (however it wouldn't be a top priority at the moment).

  3. Register custom actions via the Laravel router as per the Laravel docs.

  4. Yes schemas are effectively encoders, transformers etc etc - the view layer for your API. They are mapped to PHP objects using the PHP object's class name. You could do what you're talking about but you would have to override some of the internals. I'd need to document how to do this as it isn't totally straightforward because all the schema stuff is actually from the neomerx/jsonapi package.

  5. This is actually possible with the current implementation. The rules methods on the Validators class are passed the record being updated or null if a record is being created. So you can vary the rules based on whether there is a record present or not. You can see that in the examples here:
    https://laravel-json-api.readthedocs.io/en/latest/basics/validators/

On the Eloquent adapter there are methods that return fillable fields... those receive the record being created/updated and you can use $model->exists to work out whether it is a create or update operation.

  1. No and no plans at the moment to do this. Maybe in the long term but I think it'll be hideously complex. There's too much other stuff at the moment to work on for me to look at this in the short term.

  2. There are lots of different approaches you can take for doing this. As I've said in another issue we dealt with it in one application by having two APIs. Ultimately it's difficult for me to say what approach to take because it depends a lot on the use case plus how you expect clients to connect to your API. In our use case for example, we have one frontend Javascript application for the admins, and one for the general users, so it was easy to split the backend in to two APIs one that the admin frontend connected to and one that the general user frontend connected to. But in other use cases it might be more complicated than that. Generally this package cannot provide solutions to specific use cases - but we do need to make it easy to implement whatever solution someone designs for their application's use case.

Dear @lindyhopchris ,

thank you very much for your detailled explanation on my questions in this issue. That is a premium-support i guess :laughing:

5:
Yeah, i seen that example. But it only allows to have 2 different validators (one for create and one for update). However, if have different routes to update particular aspects of a model, this may not work. For example, i would like to have a route to update personal details of a user (firstname, lastname, address information) and another route to update his settings (e.g., preferred language), this would both map to the same validator. In this case, my client would need to send the setting fields with the "update personal details" request in order to be compliant..

7:
yeah, thats what i thought as well would be a viable strategy. I will have to think about this in more details..

When thinking about this package and JSON:API in general, another question arises:

8:
How would you deal with "non JSON:API compliant" routes? A typical example would be the /login and /logout routes. To be JSON:API compliant would need to send only a subset of a User object to the API (e.g., the email and password attributes). Would you provide a "new" Resource type for this (e.g, "credentials")? Because actually Credentials would not be a (Laravel / Eloquent) Model but rather be a subset of the User model. Further, if the user is successfully logged in, you should return a Token resource - and again, this does not "really" exist in your application ;) Furthermore, to be compliant you should use the same resource-names in the URL - so actually the client should send a login resource?! and for the DELETE /logout the client would need to send a logout resource to the API - which does, obviously, not exist again ;)

Dont get me wrong - i really (!) really like JSON:API and i adore and admire the effort the community has put into this standard. However, from the developers-point-of-view that provides the API following the standard there are some quirks i don't know how to deal with :disappointed:

That is the main reason why i used Fractal in another project and just "try to follow" the JSON:API standard "as best as possible".. But there were quickly so many exceptions that it feels "wrong".. Do you know what i mean!?

Thanks for discussing..

What about just using non-JSON:API endpoints for login/logout? In my opinion it does not make much sense to add resource types for login/logout @johannesschobel.

yeah, of course it isn't really useful to add resource types for the login/logout routes. However, then, these 2 routes would "fall out of the frame" and would behave different - which is quite bad and for other devs interacting with the API frustrating and strange..

Especially when considering that those 2 routes are crucial to nearly every web service application you're building 馃槅

Yes, they would behave differently. But to be honest, I don't think this is critical. If you use an auth scheme like OAuth for example, there is also a standard which defines how the response should look like. If you document that, link the RFC you're good imo.

There were discussions regarding that topic:
https://discuss.jsonapi.org/t/json-api-response-format-for-non-resource-data-like-oauth-token/74

@jannis-a It has no final answer, I am still confused. It seems that I should be follow the RFC's standard and "use the 'application/json' media type as defined by [RFC4627]". A project uses two media types, is it appropriate?

I have a User model that has some basic attributes (username, email, firstname, ...) and complex attributes like settings (e.g., his default language, preferred configuration of the application and so on). In order to manage the "own profile" of the user (i.e., change username, email, firstname, ...) he may PATCH /users/1234 with the appropriate data. However, i would NOT like to send the settings information because this may be done in another workflow / wizard / ... . So i would like to add another route (e.g., PATCH /users/1234/setting) so change these values (just send a json and store it directly within the DB).

@johannesschobel It seems like you are having a misunderstanding of JSON:API specification at these point. The specification supports partial updates:

If a request does not include all of the attributes for a resource, the server MUST interpret the missing attributes as if they were included with their current values. The server MUST NOT interpret missing attributes as null values.
https://jsonapi.org/format/#crud-updating-resource-attributes

That means that there is no need to have different routes to update different parts of a resource. The client could use the same route for both scenarios. There is no need to split up routes accordingly to user interface.

Dear @jelhan ,
i am aware that you can re-use the same endpoint with PATCH verb to "partially" update the resource. However, you may need complex validation patterns and database operations, so it may (!) be useful to split it up into different routes. For example, if you want to store the settings in another database table or whateve..

This may, however, be subject to the specific application / database design..

Further, in my initial post i wrote

I know, this may not be JSON:API compliant!
:laughing:

All the best

Closing this as the original questions are related to a really old version. Feel free to open a new issue if there's still any problems - probably best to open an issue per question too as that's easier to keep track of.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GregPeden picture GregPeden  路  5Comments

petenys picture petenys  路  5Comments

jelhan picture jelhan  路  6Comments

daryledesilva picture daryledesilva  路  6Comments

tembra picture tembra  路  6Comments