6 ms·
The problem with 1 model = 1 rest entity (in my experience) is that designers and users of the applications I have been building for years never want just one m
by zdragnar 2y ago
The problem with 1 model = 1 rest entity (in my experience) is that designers and users of the applications I have been building for years never want just one model on the screen.
Inevitably, once one update is done, they'll say "oh and we just need to add this one thing here" and that cycle repeats constantly.
If you have a single page front end setup, and a "RESTful" backend, you end up making a dozen or more API calls just to show everything, even if it STARTED out as narrowly focused on one thing.
I've fought the urge to use graphql for years, but I'm starting to think that it might be worth it just to force a separation between the "view" of the API and the entities that back it. The tight coupling between a single controller, model and view ends up pushing the natural complexity to the wrong layer (the frontend) instead of hiding the complexity where it belongs (behind the API).
- cosmic_cheese 2y agoThis may be a misunderstanding on my part, but something that’s kept me away from GraphQL is how it makes for a hard dependency on GraphQL client libraries in clients. I find that very unappealing, it’s nicer to be able to e.g. just use platform/language provided networking and JSON decoding (e.g. URLSession + Swift Codable on iOS) and keep the dependency list that much shorter.
- grncdr 2y ago> If you have a single page front end setup, and a "RESTful" backend Rails really doesn't encourage this architecture, quite the opposite in fact. > designers and users of the applications I have been building for years never want just one model on the screen. ... and this is where Rails excels. When you need to pull in some more data for a screen you just do it. Need to show the most recent reviews of a product in your e-commerce backend? It's probably as simple as: <%= render @product.reviews.order(created_at: :desc).limit(5) %> Of course this can have the opposite problem of bloated views taking forever to load, but Rails has lots of caching goodies to mitigate that. --- Going back to the GP post > Rails feels like it _prefers_ you build "1 model = 1 concept = 1 REST entity" That's definitely the simple path, but there are very few constraints on what you can do.
- LargeWu 2y agoWhy the assumption that an API endpoint should be a 1:1 mapping to a database table? There is no reason we need to force that constraint. It's perfectly legitimate to consider your resource to encompass the business logic for that use case. For example, updating a user profile can involve a single API call that updates multiple data objects - Profile, Address, Email, Phone. The UI should be concerned with "Update Profile" and let the API controller orchestrate all the underlying data relationships and updates.
- wahnfrieden 2y agoRails began that trend by auto-generating "REST" routes for 1:1 table mapping to API resource. By making that so easy, they tricked people into idealizing it Rails' initial rise in popularity coincided with the rise of REST so these patterns spread widely and outlasted Rails' mindshare
- 0x457 2y agoNo, it's an API Entity can be composed of sub-entities which may or may not exposed directly via API. That's what https://guides.rubyonrails.org/association_basics.html https://guides.rubyonrails.org/association_basics.html is for. However, Rails scaffolding is heavily geared towards that 1:1 mapping - you can make all CRUD endpoints, model and migration with a single command.
- jaredklewis 2y agoYou seem to be in agreement with the parent, who argues 1 model (aka database row) = 1 rest entity (aka /widgets/123) is a bad paradigm. Different widget related front-end views will need different fields and relations (like widget prices, widget categories, user widget history and so on). There are lots of different solutions: - Over fetching. /widgets/123 returns not only all the fields for a widget, but more or less every possible relation. So a single API call can support any view, but with the downside that the payload contains far more data than is used by any given view. This not only increases bandwidth but usually also load on the database. - Lots of API calls. API endpoints are tightly scoped and the front-end picks whichever endpoints are needed for a given view. One view calls /widgets/123 , /widgets/123/prices and /widgets/123/full-description. Another calls /widgets/123 and /widgets/123/categories. And so on. Every view only gets the data it needs, so no over fetching, but now we're making far more HTTP requests and more database queries. - Tack a little "query language" onto your RESTful endpoints. Now endpoints can do something like: /widgets/123?include=categories,prices,full-description . Everyone gets what they want, but a lot of complexity is added to support this on the backend. Trying to automate this on the backend by having code that parses the parameters and automatically generates queries with the needed fields and joins is a minefield of security and performance issues. - Ditch REST and go with something like GraphQL. This more or less has the same tradeoffs as the option above on the backend, with some additional tradeoffs from switching out the REST paradigm for the GraphQL one. - Ditch REST and go RPC. Now, endpoints don't correspond to "Resources" (the R in rest), they are just functions that take arguments. So you do stuff like `/get-widget-with-categories-and-prices?id=123`, `/get-widget?id=123&include=categories,prices`, `/fetch?model=widget&id=123&include=categories,prices` or whatever. Ultimate flexibility, but you lose the well understood conventions and organization of a RESTful API. After many years of doing this lots of time, I pretty much dislike all the options.
- andrei_says_ 2y agoThis is a very common pattern and one that’s been solved in Rails by building specialized controllers applying the CRUD interface to multiple models. Like the Read for a dashboard could have a controller for each dashboard component to load its data or it could have one controller for the full dashboard querying multiple models - still CRUD. The tight coupling is one of many approaches and common enough to be made default.
- dmix 2y agoTurbo frames solves a lot of this. https://turbo.hotwired.dev/ https://turbo.hotwired.dev/ Multiple models managed on a single page, each with their own controllers and isolated views.
- pdimitar 2y agoOr you can do it right and use Elixir's LiveView, from which everyone is getting inspired these days.
- xutopia 2y agoLiveView is the brainchild of Chris McCord. He did the prototype on Rails before getting enamoured by Elixir and building Phoenix to popularize the paradigm. LiveView is amazing and so is Phoenix but Rails has better support for building mobile apps using Hotwire Native.
- dmix 2y agoNot everyone can make a dramatic switch of languages and frameworks. Turbo is excellent at what it does. Pure joy to use, replacing much of our Vue frontend.
- graypegg 2y agoI have actually had a different experience. I feel like I've run into "we can't just see/edit the thing" more often than "we want another thing here" with users. Naming a report is the kiss of death. "Business Report" ends up having half the data you need, rather than just a filterable list of "transactions" for example. However, I'm biased. A lot of my jobs have been writing "backoffice" apps, so there's usually models with a really clear identity associated to them, and usually connected to a real piece of paper like a shipment form (logistics), a financial aid application (edtech), or a kitchen ticket (restaurant POS). Those sorts of applications I find break down with too many "Your school at a glance" sort of pages. Users just want "all the applications so I can filter to just the ones who aren't submitted yet and pester those students". And like many sibling comments mention, Rails has some good answers for combining rest entities onto the same view in a way that still makes them distinct.
- aantix 2y agoThe Rails support for multi-model, nested form updates is superb. Separate entities on the backend - a unified update view if that’s what’s desired. No need for any outside dependencies.
- mr-ron 2y agoIsn’t this there bff stacks show their worth? As in those nextjs apps that sit between react and rails?
- zdragnar 2y agoNot really, then you're just shifting the complexity from the front-end back to a middle man. Now it still exists, and you still have all the network traffic slowing things down, but it lives in its own little service that your rails devs aren't going to bother thinking about or looking at optimizing. Much better to just do that in rails in the first place.
- cultofmetatron 2y agoYou should checkout phoenix liveview. you can maintain a stateful process on the server that pushes state changes to the frontend. its a gamechanger if you're building a webapp. https://www.youtube.com/watch?v=aOk67eT3fpg&ab_channel=Theo-t3%E2%80%A4gg https://www.youtube.com/watch?v=aOk67eT3fpg&ab_channel=Theo-...
- stickfigure 2y ago> you end up making a dozen or more API calls just to show everything This is fine! > I've fought the urge to use graphql for years Keep fighting the urge. Or give into it and learn the hard way? Either way you'll end up in the same place. The UI can make multiple calls to the backend. It's fine. Or you can make the REST calls return some relations. Also fine. What you can't do is let the client make arbitrary queries into your database. Because somebody will eventually come along and abuse those APIs. And then you're stuck whitelisting very specific queries... which look exactly like REST.
- gedy 2y agoGraphQL is not arbitrary queries into your database! Folks need to really quit misunderstanding that. You can define any schema and relations you want, it's not an ORM.
- stickfigure 2y agoIn the spectrum of "remote procedure call" on one end and "insert sql here" on the other end, GraphQL is waaaaay closer to SQL than RPC.
- ako 2y agoNo it’s not, graphql is an rpc that returns a tree of objects where you can indicate what part of the tree is relevant to you.
- stickfigure 2y ago...and that's exactly the problem! Without a lot of hardening, I (a hostile client) can suck down any part of the database you make available. With just a few calls. GraphQL is too powerful and too flexible to offer to an untrusted party.
- gedy 2y agoThis is a silly argument and sounds like a hot take from someone who's never used this. You could say the same about REST or whatever. It has nothing to do with "the database".
- saltynutz 2y ago[flagged]
- deleted 2y ago[deleted]
- procaryote 2y agoYou can separate the view and the backend storage without going graphql. You can build your API around things that make sense on a higher level, like "get latest N posts in my timeline" and let the API endpoint figure out how to serve that It's seemingly more work than graphql as you need to actually intentionally build your API, but it gets you fewer, more thought-out usage patterns on the backend that are easier to scale.
- loodish 2y agoGraphql is nice but there are all sorts of weird attacks and edge cases because you don't actually control the queries that a client can send. This allows a malicious client to craft really time expensive queries. So you end up having to put depth and quantity limits, or calculating the cost of every incoming query before allowing it. Another approach I'm aware of is whitelisting but that seems to defeat the entire point. I use rest for new projects, I wouldn't say never to graphql, but it brings a lot of initial complexity.
- motogpjimbo 2y agoA malicious user who had the knowledge and ability to craft expensive GraphQL queries could just as easily use that knowledge to tie your REST API in knots by flooding it with fake requests. Some kind of per-user quota system is going to be required either way.
- foobazgt 2y agoI don't understand why you consider this to be a burden. The gateway will calculate the depth / quantities of any query for you, so you're just setting a config option. When you create a REST API, you're making similar kinds of decisions, except you're baking them bespokely into each API. Query whitelisting makes sense when you're building an API for your own clients (whom you tightly control). This is the original and most common usecase for graphql, though my personal experience is with using it to provide 3rd party APIs. It's true that you can't expect to do everything identically to how you would have done it with REST (authz will also be different), but that's kind of the point.