
GraphQL looks shiny, but behind that shine is a pile of extra rules, schemas, and resolvers. If you’re a Laravel dev, you don’t need all that cerem...
For further actions, you may consider blocking this person and/or reporting abuse
While I agree GraphQL should not be the default option to make the database reachable by the frontend. There are some statements I look at in another way.
First I want to get a contradiction out of the way.
In the beginning of your post you mention
But at the end you state the following
A solution can't be precise and overreaching at the same time.
When you only need a few endpoints, Laravel API routes can be the solution.
If the API is quite big, I think it is better to use a framework like API platform.
And for the use of GraphQL I think it comes down to how many instances are there that the frontend needs a single request that contains unrelated data. For example user data and product data. These requests are hard to create an API endpoint for. So if it happens often enough I think GraphQL is a good fit.
There is also no rule that states you can't use both.
It all depends on what the application requires to work in the most efficient way.
I wouldn't use a DTO by default. Most of the times API endpoints have options, which means the data that the endpoint sends is variable. With a DTO the data shape is static, which means with the options it is possible you are sending empty keys. In an application that is not a problem, but in a request it is bad. Certainly when the application needs to scale.
This is a communications break down. It is not backend decides nor frontend dictates.
This is the I-only-care-about-my-work mentality that leads to struggles that shouldn't happen in the first place.
The best way is to compare the wants and needs and find the way both experts walk away feeling good.
Thanks for your detailed thoughts. My main point in the article was less about GraphQL being “bad” and more about how backend devs in the Laravel ecosystem don’t need to reach for it by default. Most backend devs don’t want the frontend dictating data shapes or having to maintain an extra GraphQL layer. Laravel already gives us tools (API Resources, DTOs, AQC) to control what’s exposed, keep things predictable, and still deliver precision.
On DTOs — I see them as essentially the same idea as Laravel API Resources. Whether you call it a DTO or an API Resource, you’re transforming and exposing the response shape in a controlled way. That’s why I don’t see them as “rigid,” but as explicit and testable.
I agree flexibility is always possible — projects can mix approaches as needed. But my push here is simply: Laravel developers should rely on the framework’s strengths before adding new layers. GraphQL is powerful, but most of its strength is frontend-driven, and backend developers don’t need to adopt it unless the project’s complexity really demands it.
I hope that is not the case.
For the frontend wanting specific data shapes,what would be the problem? Making less requests with the data they need is a valid reason to want specific data shapes. Maybe there might be compromises on the details of the shape if it would require too much work, but the skeleton of the shape should pose no problems.
Why would the GraphQL layer be extra? It is just a decision you have to make. Creating a REST(ful) API to power the frontend can be as much work as creating a GraphQL configuration. Lighthouse is a library that takes the sting out of building the schema, because a lot of Laravel concepts are used.
An API resource is not the same as a DTO, not even as an idea. If you read the first sentence of the documentation, a resource is a transformer while a DTO is a static object. You can for example exclude attributes which is not possible with a DTO. I don't see anyone creating a DTO for each possible field option in a model.
It is also a bit strange that in the example you are using the DTO only to define the columns for the model select, but you don't use it to the actually transport of the fetched data, which is the purpose of the object.
I would think GraphQL strength is also in the backend. Create a schema and you are good to go. For an API you have to start from scratch If you don't use a framework, which requires more work because with GraphQL the bootstrapping is already done. The only things Laravel provides are the routes, middleware and resources. That gets you started, but the hard work is what comes next.
I still agree GraphQL should not be the default option, it should be evaluated on a project to project base.
I get your points, thanks for clarifying. The main issue I see is that in modern setups, frontend frameworks like React or Vue are often the ones requesting and shaping data. That shifts too much control to the frontend. GraphQL works great in that scenario, but I don’t think the frontend should be the one making those data decisions.
About DTOs and API Resources — what I meant is that they’re very similar in practice. DTOs can directly return JSON, while Resources transform and then return JSON as well. Resources can effectively act as DTOs, which is why I put them in the same category.
On GraphQL’s backend strength, I agree with you. It’s quick to bootstrap and gives a strong foundation. But it’s also self-centric — once it’s in, many projects start depending on GraphQL for almost everything. My push is just to remind Laravel devs that they don’t need to default to that dependency when the framework already provides strong, native tools for shaping APIs.
I also want to add a broader point here, which I’ve written about in my article Web Transition. When the web first came into being, the backend was responsible for requesting and shaping data. The frontend’s job was limited to three things: user interaction, DOM manipulation, and animation. Somewhere along the way, frontend frameworks took over backend responsibilities and became the decision maker for data. That’s where I see the real problem — it’s not just about GraphQL, it’s about a shift in roles that also created side effects like SEO issues. This is why I argue developers should let the backend remain the decision maker, and use Laravel’s native tools instead of moving that responsibility to the frontend.
Returning JSON is not the job of a DTO.
That is not a GraphQL problem, that is a developer problem.
That was before ajax. Since ajax, 2001 (internet explorer 6), javascript started asking for data. This is long before view libraries took over.
So asking for data in a certain format is an "old" way of improving the user experience.
Have they gone overboard with view libraries, sure. But I'm seeing the return to progressive enhancement in the javascript community.
Wanting to go back to only backend database access is a foolish idea. Both backend and frontend are going to need database access to create a good website for people.
If you only want a good SEO website, just use HTML and be done with it.
Yes sure DTOs and API Resource are returned as Payload in json. Thats what i mean.
As for GraphQL, I don’t disagree that misuse is a developer problem. But the tool’s design does make it easy to misuse. That’s why I argue Laravel developers should lean on what the framework already provides before adopting GraphQL as a default.
As about Ajax, javascript does ask about data and that's excellent but javascript has also snatched the responsibility of rendering/hyderation from backend that has caused SEO problems. Fetching is fine but dictating shapes and controlling backend decisions is where I see the problem. That’s why I prefer keeping shaping logic in Laravel rather than letting the frontend layer drive it so it is a clear distinction between asking data and asking specific data.
You mention the return of progressive enhancement in the JavaScript community, and that’s exactly the point I’m emphasizing: let the backend, which is inherently more powerful in rendering and hydration, take care of what it does best. Then the frontend can progressively enhance where it makes sense.
On the point about
I see a contradiction. JavaScript on the frontend cannot directly query a database, it must go through the backend layers. So regardless of approach, the backend is a must-have part of application. If there’s a way for frontend code to securely and directly access a database, I’d genuinely like to learn about it.
Finally, regarding SEO, I agree that plain HTML is best for crawlers, but I don't argue to go back to old static approaches. Rather, it’s about giving backend-rendered HTML for the initial load ensuring SEO and fast first paint and then letting JavaScript take over for data fetching and interactivity. That gives us the best of both worlds, SEO-friendly pages and dynamic user experiences.
A frontender can see the shape from that perspective, that is why I mentioned the details of the shape should be discussed with the eye on timings.
How can you do fetching without setting requirements on what you want to get? If I ask for a banana I don't want to get an elephant.
Of course and no there isn't.
My statement was about the responsibility of getting the data. Because the frontend has logic to fetch data, the responsibility changes from backend only to backend and frontend.
An extreme example is an offline first application. The backend is nothing more than a backup/syncing vehicle and all the logic is in the frontend code.
I agree that fetching requires requirements, but I think the distinction lies in what level of requirement the frontend should set. In the past, those requirements were broader and still worked effectively.
Take an e-commerce example. When the frontend needed products for a given category, it didn’t say: “only give me product ID, name, SKU, image, short description, and price.” Instead, it simply asked: “give me products of this category.” The backend then decided what was appropriate to return for a product in that context. That ensured consistency while keeping the responsibility of shaping the data on the backend.
Now, when every frontend dictates exact fields, we end up duplicating those requirements across multiple clients. A website frontend specifies them, the mobile app specifies them again, and if there are more components, they all repeat the same instructions. This leads to fragmentation and duplication of logic.
I also agree to overcome this issue, GraphQL like tools came into being.
If instead we let the backend handle shaping — where the frontend only declares the type of data it needs (“products of this category”) — then the backend can consistently provide the same synced data model across all clients: web, API, mobile, or anything else. That removes duplication, keeps responsibilities clear, and ensures every consumer gets a unified representation.
That’s the difference I’m trying to emphasize: frontend asking for what it needs is fine, but deciding how the data should be shaped should remain the backend’s responsibility.
What is the difference between
and
It is still the frontend that passes the requirements to the backend, either by fields or by context. The frontend request is never only "give me products of this category.", because then the backend doesn't know the context.
And in the case of the fields you don't need to name all the contexts and match that to fields in the backend. Just check the fields in the request and you can run the query. Isn't that what your AQC pattern does?
That is the idea of an API endpoint with options, it doesn't need to be GraphQL to solve that problem.
With contexts you have to create more contexts per client you add, because a context in one clients is likely to require more or less data than in another client.
By using the fields you can create an id and cache the most requested field combination(s) over multiple clients.
By keeping the responsibility in the backend you create more work for yourself and miss opportunities to scale your code.
You mention that a frontend request is never only “give me products of this category”, but I’d push back on that. In e-commerce and many other applications, this is actually the most common and standard use case. Category pages, search pages, and paginated listings all work exactly like this.
In such cases, the context is already fully defined by the parameters the frontend sends — e.g.
category_id=5&page=2&sort=price_desc
. That’s the frontend’s role: providing parameters that describe what data is needed.But what you’re suggesting goes a step further: not just defining the context, but also telling the backend what specific fields to return for that context.
Yes, my AQC pattern does allow flexible queries, but the principle is still backend-driven and AQC is also based on what you call context (parameters) not data shape.
I see the point about scaling with field-based requests, but there’s a catch. The more flexibility you allow, the more unique field combinations you create across clients. That sounds good on paper, but in practice it leads to cache fragmentation — dozens of slightly different result sets that don’t get reused enough to be efficient.
With backend-driven shaping, the number of representations is limited and consistent, which makes caching and query optimization far more predictable. So while it may look like “more work” at first, it actually avoids the hidden overhead of managing hundreds of ad-hoc field combos.
To me it’s a trade-off: flexibility vs consistency. I lean toward consistency because it keeps the system stable and predictable across all clients.
When you are working with an API that supports multiple clients, that is not the full context, that is only the data limiting and ordening part.
That is why you need the fields, because the data could be used to create a product card, but it also could be used to create a recommended or promoted section that requires less data. Or different data, for example a buy link.
And both the product card and section can have other fields per client.
Hard coding all those options is a foolish job because people will figure out other ways to present the data.
Flexibility is not a bad thing, certainly if the API need to support a wide range situations.
Flexibility also removes the speed bump of having to change the backend code for a frontend change. We all know companies like website restyling.
It is the extra work to maintain static outputs of endpoints I'm thinking about, not the setup.
If you cache all the combinations yes, but that is a stupid way to do caching. Like I mentioned before start by caching the most requested combinations. Then check the slowest requests , or do it at the same time. And keep on monitoring.
Caching is not a set and forget action.
Even for your solution you can't predict what needs to be cached until people use the website.
How would adding/removing fields be a problem for the query performance? It is not possible to change the database indexing by changing fields. And if the fields are relations they should be loaded eagerly.
What do you mean by managing?
If you can't query one or another field without making the system unstable I think there is a deeper issue at play.
Having the same content for all clients is a bad idea, because you don't want to send more traffic than needed. Don't send ten fields needed for the website where the app only needs three.
To be clear, when I mention fields I indirectly refer to a datashape, as the fields are the building blocks of the shapes. An example of a shape with fields is something like
product[name,variant[sku,color,price]]
if every client used data differently, a field-based approach definitely feels convenient. But that convenience comes with a structural trade-off: you start designing for presentation diversity rather than domain consistency.
In AQC, the parameters already represent intent — what data is needed and why. The shape is a side effect of that intent, not a negotiable part of it. If two clients use the same “products” differently, they should express that through different contexts (like productsForCard vs. productsForRecommendation), not by redefining fields at request level.
That’s the difference between flexible data and flexible contracts. I prefer keeping contracts stable because they’re what let multiple teams scale without unexpected coupling between backend shape changes and frontend rendering logic.
Caching is of course dynamic, but predictable data shapes make caching strategies reliable — and that’s the bigger advantage at scale.
The only project type I know where the presentation doesn't change is a short time project.
You don't even need a multi client API to think about presentation diversity, if most frontend changes require backend changes would that not be a sign to add flexibility?
And what makes domain consistency so essential for a project that it should be followed over everything else?
Domain consistency for me is just a fancy term to reject change. It sounds valid but at its core it is just about not considering anything that doesn't fit the current model.
I'm happy we agree that your query string example is not the full context.
But my point stays the same, you will never be able to create all the contexts people are going to use. And even if you do, a context can mean different things for different applications. productsForCard can mean 4 fields for one application, but 10 fields for another.
The same is true about
page=2
. even on the same website a page can be 10 items or 100 items.That is why using fields, or adding the items per page on top of the page number, is a more clear and maintainable way to communicate than using one parameter and use that a base to fetch the data.
If a contract is flexible it isn't a contract. If you add an interface with a method to a class, but you don't need to add the method what good does it do to have added that interface?
Did you just argue against your own position?
I'm reading this sentence as promoting less coupling between backend and frontend. By using contexts the coupling is tighter than with fields, because it uses pre set shapes.
Set data shapes don't make it easier to cache, because you don't know which are the ones that are going to be called the most once the application goes live.
You have to do the same monitoring as with user definable data shapes.
The question is also what takes most time, querying the data storage solution or creating the shape. If querying the data storage takes most time, you cache that instead of the data shapes. So the data shapes don't even matter for the caching strategy in that case.