16 ms·
Don't build a general-purpose API to power your own front end
- qudat 5y agoI totally agree with the idea of catering your BE to service your -- most likely -- single FE client. Every rails project I have worked on has this 1:1 mapping between endpoints and entities and it drives me insane. We set up these APIs to have clean lines between models and endpoints and consequently we push all the complexity of combining all of these related entities onto the FE. Then we wonder why the FE is so complicated! If I have to make 5+ API requests for one page then there was a severe failure in planning the API. What's even more frustrating is this is the rationale for moving to something like graphQL. We have engineers advocating for it because then it's "just one request per page" and it doesn't click that the framework we are using is pushing us into a less than favorable API design.
- nitrogen 5y agoGraphQL is appealing from the frontend perspective, but I've yet to see a case where it would do anything but make the backend 10x harder to develop, and it doesn't solve the problem of the frontend also having to know all of the entity relationships. Since you're using Rails, it's a lot easier to just add a controller endpoint that provides the composite data (as has been mentioned elsewhere already) for one UI action/page/navigation/state change, instead of using an entity-focused API. Controllers are supposed to abstract over potentially multiple models, not just be 1:1 endpoints for each type of entity.
- JCharante 5y ago> it doesn't solve the problem of the frontend also having to know all of the entity relationships isn't this the point of graphql? You can write your front-end with easily generated typed responses.
- ericwooley 5y agoHasura with graphql code generator is an amazing, type safe, and secure combo. I've never been able to develop a rest API faster, much less with documentation and type safely. It might not be faster than rails/html, but it's a hell of a lot easier to reuse, understand, secure, and audit.
- qudat 5y ago> Since you're using Rails, it's a lot easier to just add a controller endpoint that provides the composite data (as has been mentioned elsewhere already) for one UI action/page/navigation/state change, instead of using an entity-focused API. Its possible, sure, but thats not the point. The point is the convention drives people to make entity-based endpoints and thats what I cannot stand. People are running the rails generator to build an entity and then when that doesnt scale we have to reconfigure the API. Overall not a fan of rails and the conventions it promotes.
- nitrogen 5y agoOverall not a fan of rails and the conventions it promotes. To each their own. No framework will have the right conventions to see a complex project through 100% with zero customization or deviation. I'm still fond of Rails though I haven't used it in a while. Nothing I've encountered since -- Node, Sprint Boot, Scala+TwitterServer, various Kotlin libs/frameworks -- has been as productive or appealing for me as Rails. You don't know what you've got until it's gone, as they say.
- crubier 5y agoIf graphQL makes your backend 10x harder to develop, then you are not doing it right. The overhead of GraphQL vs Rest on a backend is around 30lines of code in my experience (developed 5 different production Graphql servers, including a very large one). Routes become resolvers, but that’s mostly the only change. But in the end you get a much nicer API, GraphQL codegen, type safety, auto generated docs, etc...
- square_usual 5y agoI may have misunderstood what you're saying, but binding your endpoints to your entities isn't a bad idea at all. For example, even when you're not making an SPA and are using good ol' server-side generated Rails, you would still have a 1:1 mapping of pages to resources, and your "app" would move you from page to page as you did stuff. This works out pretty well because your resources are what people want to interact with! Try out Basecamp sometime - watch the routes, they're all well bound to resources, yet it feels very much like an app. The progenitors of Rails are still doing things the old way and they're pretty good at it.
- chrismorgan 5y agoAnother use case where you are very probably better to build a general-purpose API: when you want offline support, especially if you want sync. Ad-hoc offline support will cause you far more trouble than the effort of making a principled design. (As far as foundations for a sync-capable web protocol are concerned, I’d suggest JMAP as generally a good choice for client-server sync, and even if it doesn’t match your requirements, it’s good reading for those unfamiliar with sync considerations to get a feel for what you’re going to need.)
- TedDoesntTalk 5y agoI don’t think this approach mutant excludes the ability to create another endpoint for sync.
- chrismorgan 5y agoAd-hoc offline or sync support invariably goes wrong in important ways that lead to persistent missing or incorrect data and data loss. It’s something that needs to be designed in from the start, and it necessarily takes the form of a data-oriented API, entirely opposed to what this article is suggesting (UI-oriented). If your main UI needs to work offline or with sync, you can’t use a UI-oriented backend.
- hellisothers 5y agoLove this idea, and someday when you also have a mobile app please provide them an actual API that is just as useful for them, don’t ask them to use those bespoke RPC endpoints.
- urda 5y ago> Have you seen that list of annoying decisions up there? For one, they are gone now. Erm, even with that solution you still have to consider the changes that impact this schema. These problems didn't go away, they simply became masked differently.
- hakunin 5y agoI'd argue that the difference between "we're changing schema for all callers" vs "we're changing schema for 1 page of 1 frontend that we fully control" is so profound, as to render that whole concern moot.
- lucasyvas 5y agoI completely agree with the author. A few months back I examined the number of network requests required to populate the data for a page I was trying to improve performance on. Let's just say it did not go well. Despite best intentions of being API-first with a SPA front-end, when you have a data-heavy and query-heavy application, it is absolutely the wrong choice ten times out of ten. It leads to (not so) hilarious situations where the older, server-side rendered version of your app that uses jQuery absolutely demolishes your new hotness in performance. Try explaining that one with a straight face.
- ovao 5y agoDid you consider endpoint composition? Instead of making 7 API requests (via fetch, or whatever), provide an endpoint that’s simply the composition of the 7 endpoints.
- lucasyvas 5y agoGreat question! Yes, this was thought of and it's an approach that might still be taken. That said, I consider this to be a band-aid solution - IMO, just have a back-end to your app or a heavily optimized query interface like GraphQL. Once you are adding special endpoints for mostly just your UI, it's no longer strictly a general purpose API and the entire point of the design is moot. If you end up in this position I consider it a design failure - most data-heavy apps are probably better off with a command/query API.
- dlisboa 5y ago> Once you are adding special endpoints for mostly just your UI, it's no longer strictly a general purpose API and the entire point of the design is moot. Not really, it's a very valid solution when you have multiple clients (Web, Mobile, TV, etc) and multiple API calls in the backend. Netflix had a similar solution back in the day (not sure they still do) to solve the issue of clients calling multiple APIs.[1] Which isn't much different from calling multiple endpoints of the same API. When you have one client and know everyone else isn't going to use your API, it might be overkill. But if you have a very granulated API for general purpose (like, say, Twitter) but want your Web or Mobile clients to have a very narrow and specialized one, it makes sense. I wouldn't consider this solution a failure, just the outcrop of a highly distributed backend system. [1] - https://www.nginx.com/blog/building-microservices-using-an-api-gateway/ https://www.nginx.com/blog/building-microservices-using-an-a...
- da39a3ee 5y agoI agree with this. It's tangentially related to it being so standard to refer to every JSON HTTP API as a "REST" API. REST APIs (in the strict sense) are for public integrations (but how common is that really?) / other teams in your (largish) company.
- issa 5y agoI could not agree more. This is the way I design things and it makes life so much easier. It solves so many problems by just building a separate API for each app. They can all use and re-use the same underlying functions and logic, but each API serves the data in the most convenient way for whatever page is being rendered. I would never go back.
- lucasyvas 5y agoDon't bother looking back. I would encourage you to consider GraphQL depending on your use case, but for data-heavy apps the generic API approach is the worst approach.
- issa 5y agoI am not very familiar with GraphQL, but on first look it seems more suited to a public API. For an internal app, when we know EXACTLY what data is needed for each page, this seems like wasted effort. Any thing in particular you suggest checking out?
- et1337 5y agoGraphQL was designed for mobile apps and UIs, and I think it’s perfectly suited. We started to build our front end using our public REST API and the experience was so harrowing that we started looking for something else. The big win with GraphQL comes from the client-side tooling. You can have a React page with 100 components that each request their own data, and have it automatically batched up into a single request. Your page no longer has to predict what data the components will need. When a junior front end dev tries to reuse a component from page A in page B, the page B query will automatically be updated to fetch the data required for that component.
- stepbeek 5y agoI’ve been using GraphQL seriously for a year on a client project now (an inherited decision). I can totally see why it would be useful for a public API or an internal API in a very large engineering org. For everything else it is needless complexity and an additional failure mode. The tooling and operational aspects aren’t as mature as a conventional HTTP API (yet?) so I’ve found it to be a higher cost for limited gain.
- deleted 5y ago[deleted]
- Tyr42 5y agoI have done something similar with a previous project at work, though everything went through three layers, the front-end, which was reasonable as they describe, the front backend, which just serves the single page's required data, and the back backend which was mostly the legacy version of the site's back-end but still had useful logic in it. Ended up working out okay, and the idea of each pages gets a service endpoint was a bit weird at first, but really gave it the flexibility to avoid needing to go touching the legacy layer very often.
- paxys 5y agoHere's an idea – instead of passing the "visual page structure" to the client as JSON, use a markup language specifically designed for that purpose – HTML. What ends up happening though is that you have to build that general purpose API for mobile apps regardless, and right after that developers start using it to render their web app components. Rinse and repeat.
- lucasyvas 5y agoIf you are into server-driven UI, you can deliver structural data in JSON format and only have the client know how to render components. This is easier said than done, however, as you basically need to reinvent parts of HTML but worse (ex. attaching event handlers to UI elements, etc.) Or, use GraphQL. Or a BFF for each client.
- square_usual 5y ago> Here's an idea – instead of passing the "visual page structure" to the client as JSON, use a markup language specifically designed for that purpose – HTML. Why didn't you just come out and say "Use Hotwire!" or "Use LiveView!" ;)
- davidatbu 5y agoWait, are you saying that "html-over-the-wire" is preferable even for mobile apps, or that having mobile clients makes using "html-over-the-wire" undesirable?
- runawaybottle 5y agoThis approach does not help when you need to transform data models on the frontend (and possibly send it back). Imagine trying to make sense of: merge(pages.a.section.main.data, pages.a.section.secondary.data) What does that even mean? It’s going to get hard to prep abstract data.
- simonw 5y agoI mostly agree with this (provided you are sure you should be building an SPA in the first place, which is currently a massively over-used architectural pattern). The one thing I disagree with is dismissing the idea "But we can reuse this API for the mobile app too!" Depending on how you organization is structured, it can be common for the mobile app team to end up in need of APIs that aren't being delivered promptly. Should that happen, having a web front-end that's entirely powered by APIs can level the playing field enormously - the website can no longer "cheat" and not bother with an API, which means the mobile team will get everything they need.
- pjerem 5y ago> Depending on how you organization is structured I truly start to think that organizations with silos in what you can code are just wrong and prone to this over engineering thing. Just make your backend the shared space between your teams. There is no reason a qualified front end or mobile programmer could not at least write the controller for the endpoint they need.
- draw_down 5y agoIf all the JSON your page will need can be provided at page-load time, go ahead and render that bad boy on the server, and skip the JSON.
- underwater 5y agoThis approach is described by the cutely named BFF – backend for frontend – pattern. (https://samnewman.io/patterns/architectural/bff/ https://samnewman.io/patterns/architectural/bff/)
- lucasyvas 5y agoIt's a solid pattern. Eventually I find you end up wanting a write API for validation, and a read API for flexible querying in some applications though. That is well-served by GraphQL mutations and queries. BFF pattern can be more approachable and reduce client code, however, and that's a plus.
- accountofme 5y agoI'll be honest, I wanted to make a bff joke, but kept running in to too many double entendres. Best. Pattern. Ever. Edit: spelling
- andreygrehov 5y agoI will go even further with this one. I've been working on a personal project of mine with REST APIs on the backend and a fancy combo of TypeScript/React/Redux Saga on the frontend. Everything was shiny and cool, except the overall development process was super slow. I'd spend hours polishing components, figuring out various states, installing TS libraries, fighting the compiler and god know what else. It was exhausting and tiresome, until one day I said "f#%k it, I'm done". I opened my favorite search engine and typed bootstrap premium themes. jQuery? Whatever. HTML? Fine. Pure CSS? Sure! No AJAX. No fancy reloads. No history management. Plain old <form method="POST">, good ol' cookies and simple HTML files. Within just three weekends I was already MILES ahead of a previous stack. Long story short, if you are working on a personal project, please, consider the most dumb setup. With the vast options of super polished modern frameworks, it'll take you pretty far. A few more weekends and I'll prob be ready to go prod. Edit: remembered something fun. I have a page that requires to poll the backend and then take some action. So I thought a bit of Ajax is fine. I opened the corresponding HTML file and started typing: <script> jQuery.ajax(...) but then... hold on a minute! That's getting way too complex. META REFRESH FTW, M%F%CK%RS! :D <meta http-equiv="refresh" content="5">
- pjerem 5y ago> Long story short, if you are working on a personal project, please, consider the most dumb setup. This is not limited to personal projects. I can’t recall more than a single project I’ve worked on during the last decade where front-end code was really useful. Some cool stuff, ok, but never worth the pain. I acknowledge it can be useful. For some real time project. Not for your crud-for-a-living.
- ransom1538 5y agoNailed. It. Yes. I work with GCP (google console) for work ALL DAY. It is a layered gross mess of SPA. Example, I need to list the servers. It will take 10-20 seconds to load the list of 30 servers. I don't care about the cool glitching transitions or boxes with fades. I want to list the servers. Hit the back button? OH WELL! All your session data is now lost. Anytime I type in GCP the computer hangs while it processes thousands of lines of javascript to see if I will autocomplete. When I click "create" my browser tab hangs while my macbook pro fan cranks up. I want to rewrite the GCP console with HTML, using their api and not one damn line of JS.
- stickfigure 5y agoI'll go one step further than this - build an endpoint for each component in your frontend. That way you can re-use each of these components on multiple pages, and you end up with components that are scoped fairly narrowly. In practice this ends up building out a reasonable approximation of what a "public" api would be. Eg, your WidgetList component forces out a /widgets endpoint, which might get re-used by some other widget too. That's fine. The point is you're still working UI-first and making the minimum viable backend. With lots of components on a page, you might end up making multiple calls for the same information. That's also fine. You can optimize that later if it becomes a real problem.
- yurishimo 5y agoHow do you stop the spinner/load jank hell of 100 different components all requesting their own data? Not to mention, even if you're running a low traffic site so your backend performance is fine, the browser is limited to a set number of concurrent requests. Locally, it might all go super fast, but as soon as you deploy it, that dashboard calling 30 endpoints is going to feel insanely slow just waiting for the network to become free for use.
- stickfigure 5y agoIf you're loading 100 pieces of data, maybe you have some UX/IA design issues? Nobody looks at 100 different things on a page. I'd say my average page has 5-10 requests, and it's plenty snappy.
- TedDoesntTalk 5y agoI’m working on a project now with 64 front end components. So you’re advocating for at least 64 calls to the end?
- gnulinux 5y agoNo, I'd go it by this: decide a reasonable number for each page to make request for. Let's say it's N. For all the pages in your app divide them into N pieces. There might be elements that are repeated in all pages (like navbar) so even with very low N like N=3 you can save a lot of redundancy. E.g. divide all pages into navbar, content, bottom content. Now you have a single /navbar/ and /bottom/ apis and then for each page /pageHome/ /pageHistory/ /pageX/ etc...
- codetiger 5y agoHow is it different from what GraphQL gives (Other than being too flexible)? Front-end can do single API call for entire page and can be cached. Did I miss the whole point?
- danielrhodes 5y agoThis kind of supports the author’s point. Many times when using GraphQL you will fetch data in a way that matches how the front-end needs the data, not in the way that creates a clean decoupled abstraction for the backend.
- TheCoelacanth 5y agoWhich is exactly what GraphQL was designed for: a tool for easily writing APIs to translate your clean decoupled general-purpose APIs into something that is convenient for the front-end to use.
- intellix 5y agoJust another reason to use graphql. I really don't understand why the industry has to spend another decade kicking and screaming until everyone transitions to using it. I've built and used APIs for full systems in all 3x: RPC, REST, GraphQL as both a creator and consumer and as far as I'm concerned everything else is dead
- OJFord 5y agoDo you honestly think if we got a bunch of people to comment who'd used all three, they'd all say Yep, graphql, that's my favourite? None of them is dead because there are people who prefer each one, and ceteris is rarely paribus - if one (I originally wrote RPC but I suppose it could be any of them) is already in use for inter microservice communication say, that's going to tilt the balance.
- jayd16 5y agotl;dr YAGNI
- 015a 5y agoWe have an API similar to this. I'd say, it can make sense. But one big issue is how it muddies the line of where certain frontend decisions should be made. For example; if you have a person's profile picture, and you want to round it, should that be a property on the JSON structure? E.g. { "profilePicture": { "url": "https://mycoolpicture.com/pic.jpg https://mycoolpicture.com/pic.jpg", "rounded": true } } Or a property of how the frontend transforms that structure into HTML? In other words, its an opaque, intrinsic property of the "profilePicture" "JSON-component", every profile picture is rounded, because that's just how profile pictures are. Until you hit the one page that wants one that isn't. No problem, maybe a generic "picture" "JSON-component". Or you hit the page that needs a squircle. Ok maybe we do need a "border" field, and why not just make its value the same as the CSS `border` property and oh my god are we just reinventing HTML? There's no right answer to this question, and it will appear in literally every single component you build. Maybe its text weight: How generic should it be? Very-generic: "title". Kinda-generic: "heavy". Or literally just CSS. What do these styles mean when faced with user browser preferences, such as dark mode or accessibility systems? By the API contract, "cardTitle" means "roboto, 16px font, #0000ff font, whatever"; but not always, right? Does the frontend disobey the contract? Do you send these preferences to the backend and let it handle it? Does the contract allow for lee-way in how its responses are interpreted? How much lee-way? Lets say I want a sidebar. That sidebar has six items, so we list them. But what happens when the site is displayed on a phone? Well, for the sake of just providing an answer to continue this line of thinking: the designers want it to become a bottom bar. No problem, except, uh, the API says it should be a sidebar and we're not really communicating how big the screen is to the API are we? Well, maybe we are, sure, we should have considered that during the v1. Is the server really the best source of truth on how many items a 976px wide screen can hold? Four maybe I guess? Maybe the frontend just sees `sidebar`, interprets it to "understand" "implicitly" that it means "bottom bar" on small screens, and do the best it can, and now the entire point of this exercise is out the window and we're not obeying the API. Another problem is during visual redesigns. This may demand the recreation of your entire API surface; you just doubled the work of a visual redesign. If the data model were generic, your backend team may not have even had to have been consulted. Another is in interaction. Button which opens a new tab to google.com; easy, no problem. But in every reincarnation of this idea, I've never seen a strong argument for how to handle even a basic form, with a button to submit the input to another API endpoint. Do you have something like { "form": [ { "formTextField": { "id": "firstName" } } ... { "formSubmitButton": { "endpoint": "/createUser", "verb": "POST", "arguments": { "id": "firstName" } ... } } ] } How do you handle the response from /createUser? What if there's an error? What if one page that calls /createUser needs to display the error in a snackbar, but another page needs to display it in-line with the button? Do you just... not do that? Just throw every error into a snackbar? Ok, are you capable of updating the local cache with the response from createUser, such that another call to the page-rendering API isn't necessary? This doesn't feel as good to me as the rest of this line of thinking, which is already not great. It feels like "we had this really cool idea to render our entire frontend in JSON, oh crap we need to support interaction patterns other than queries, uh, how do we shoehorn that into our cool idea, ok, hold my beer this is cute." Look; there's a reason why data and views are separate. This idea isn't new. I hope you realize that, but I'm afraid you don't; our industry does tend to revolve in cycles, but this is one that shouldn't come back. There are really specific use-cases for something like this which are actually powerful, from my point of view. The example from our app is, best put: imagine a google search results page. No interaction except simple hrefs. Maybe you have a ton of "Link" { "title" "subtitle" "body" } cards, maybe a "MapResult": { "latitude" "longitude" }, basically displaying a list of cards which may have different content. The frontend needs to know how to render each card kind, but the specifics of how its rendered are kept presentation independent; nothing is asserted about structure, just content and relationships between the content. It can work for that. It still has problems, especially as the business wants more and more things shoved into this model that really wasn't built for it, but it can work. I know its a meme, or the buzzword of the day, or whatever, but: GraphQL is actually really good at solving the problems you think this solves. It doesn't do it for free. You have to think about N+1s and composite queries and performance around those and designing a good schema. But, from the perspective of just the API language you're talking, its actually pretty good. And if you're really having performance problems on a page, you can always amp up your API caching, or introduce page-specific queries which collate data on the backend into a small number of database queries, or whatever.
- andreisbc 5y agoI'm following this pattern by using NestJS on the backend and it acts as the state of my frontend(no need fir redux). The API responses are already in a shape that I can easily consume. A single request gets all the data it needs(using relations). I like it!
- gnulinux 5y ago> #Do you have data to support your claims? > I wish. It’s pretty hard to measure these kinds of things in our industry. Who’s gonna maintain 2 architectures for the same software for 3 years, and compare productivity between them? All I got is a mixed bag of personal experiences. Feels inductively justifiable. Oh wow this is sort of fascinating. Maybe we could crowd source experiments like this. Like maintain some random open source app with two different backend structures for X years and blog about it, share battle scars.
- theteapot 5y agoThere should just be one endpoint that dumps the entire DB as JSON. The front end guys would love it!
- vishnugupta 5y ago> Your business logic has now moved from being haphazardly split between frontend and backend into just backend. This. I have spent countless hours pulling my hair trying to understand backend business logic only to see part of being implemented in the front end. What's more, the supposed generic backend API makes quite a lot of assumptions about the front end orchestration, so the API can be used only in conjunction with the front end it is serving. Now not only is your backend API not reusable but also the business logic is brain split between the front end and backend.
- EMM_386 5y agoOne of the best parts of Twitter is they load their main and the various sidebar widgets asynchronously. That allows you to see things that load first, and makes it appear as if something is "happening". I don't like this concept of sending the page structure as JSON. It would require all parts of the UI to just be stuck on "loading" while the back-end essentially the entire page. If you decide to turn this into "ok well make it /page/a/component/3" then we're back to square one on the whole idea.
- hakunin 5y agoIf everything on the page is slow to load, then I would agree. Twitter is such a crazy outlier that I can't even imagine what they have to deal with. In a usual case you'd get a rare 1-2 slow retrievals, and the rest of the page can show up immediately.
- omnimus 5y agoInertia.js does this very well. Using server routing on JS frontend and autoloading page data.
- Fradow 5y agoSince I do almost exactly that, I agree with the sentiment. I'll go even further on one point: "Imagine if you could just send it the whole “page” worth of JSON" => why do you even send it as a separate endpoint? Embed it in your page! It works great with a SPA: the first load use embedded JSON, and next partial data refreshes hit endpoints returning JSON with the same format.
- recursivedoubts 5y agoAbsolutely. Your application API and your data API have different needs and requirements: You application API is churny, specific and tuned for certain screens and user interfaces. Your general data API is, well, general, rate limited, concerned with limiting the ability of that expressive power to damage your system, etc. https://intercoolerjs.org/2016/01/18/rescuing-rest.html https://intercoolerjs.org/2016/01/18/rescuing-rest.html One you get over the hump of splitting your data and app APIs, the next step is to realize that your application API can be a hypermedia API rather than a dumb JSON API, and you are off to the races: https://htmx.org/essays/hypermedia-apis-vs-data-apis/ https://htmx.org/essays/hypermedia-apis-vs-data-apis/
- spfzero 5y agoThis resonates with my experience, which is basically doing this the wrong way and watching all of it play out the way OP warns it will. I'm sure there are others with the opposite experience but I can vouch both for the desire and expectation of devs to build general-purpose APIs when not needed, and the ongoing pain resulting from that year after year.
- aaron-santos 5y agoI'm stuck in this quagmire and have been for 18 months. Right now it's manifesting as a React on GraalJS project written in Clojure. I've learned a lot, but actual progress is, of course, elusive.
- true_religion 5y agoI just use GraphQL and freeze the queries at compile time so only known queries can be used. That stops worries about misuse from the frontend. On the backend, for performance we have two choices: 1. Over-fetch. It's relatively cheap doing that from a cache. Most queries don't use too many different variations. 2. Optimize what data we fetch based on the node's children in the GraphQL query received. I don't think people do this often enough but... GraphQL gives you the full query as an AST, so you can use that at runtime to know what your node's children will be rendering before they're hit. Because we enforce #1, we don't have to build some general-purpose query-builder a simple check of "Are you X named query?" is good enough. Internally it looks kind of like JSON RPC. Am I doing it wrong? It seems to give you the best of both worlds because GraphQL APIs have great tooling and documentation behind them, and if you cut out all the 'general' purpose aspects in production you can be fairly efficient (e.g. our GraphQL API is about 5% slower than the JSON API it replaced).
- hakunin 5y agoI think this is totally fine if you're willing to accept some performance trade offs in favor of productivity, you're staying mindful of limits and scoping, and just generally you're accustomed to living in the GraphQL world.
- turtlebits 5y agoI think Hasura (automatic APIs for your DB) is the best thing since sliced bread. It handles 95% of my backend needs.