TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Don't build a general-purpose API to power your own front end

128 pointsby hakuninover 3 years ago

30 comments

andreygrehovover 3 years ago
I will go even further with this one. I&#x27;ve been working on a personal project of mine with REST APIs on the backend and a fancy combo of TypeScript&#x2F;React&#x2F;Redux Saga on the frontend. Everything was shiny and cool, except the overall development process was super slow. I&#x27;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 &quot;f#%k it, I&#x27;m done&quot;. 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 &lt;form method=&quot;POST&quot;&gt;, good ol&#x27; cookies and simple HTML files. Within just three weekends I was already MILES ahead of a previous stack.<p>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&#x27;ll take you pretty far. A few more weekends and I&#x27;ll prob be ready to go prod.<p>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:<p><pre><code> &lt;script&gt; jQuery.ajax(...) </code></pre> but then... hold on a minute! That&#x27;s getting way too complex. META REFRESH FTW, M%F%CK%RS! :D<p><pre><code> &lt;meta http-equiv=&quot;refresh&quot; content=&quot;5&quot;&gt;</code></pre>
评论 #28520599 未加载
评论 #28520239 未加载
评论 #28528331 未加载
评论 #28521221 未加载
评论 #28520298 未加载
评论 #28520620 未加载
评论 #28521440 未加载
评论 #28521111 未加载
评论 #28520374 未加载
评论 #28525246 未加载
paxysover 3 years ago
Here&#x27;s an idea – instead of passing the &quot;visual page structure&quot; to the client as JSON, use a markup language specifically designed for that purpose – HTML.<p>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.
评论 #28520139 未加载
评论 #28520334 未加载
评论 #28520386 未加载
qudatover 3 years ago
I 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.<p>What&#x27;s even more frustrating is this is the rationale for moving to something like graphQL. We have engineers advocating for it because then it&#x27;s &quot;just one request per page&quot; and it doesn&#x27;t click that the framework we are using is pushing us into a less than favorable API design.
评论 #28520127 未加载
评论 #28520311 未加载
lucasyvasover 3 years ago
I 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.<p>Let&#x27;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.<p>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.
评论 #28520051 未加载
评论 #28520202 未加载
评论 #28522127 未加载
intellixover 3 years ago
Just another reason to use graphql. I really don&#x27;t understand why the industry has to spend another decade kicking and screaming until everyone transitions to using it.<p>I&#x27;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&#x27;m concerned everything else is dead
评论 #28520944 未加载
simonwover 3 years ago
I 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).<p>The one thing I disagree with is dismissing the idea &quot;But we can reuse this API for the mobile app too!&quot;<p>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&#x27;t being delivered promptly.<p>Should that happen, having a web front-end that&#x27;s entirely powered by APIs can level the playing field enormously - the website can no longer &quot;cheat&quot; and not bother with an API, which means the mobile team will get everything they need.
评论 #28520297 未加载
underwaterover 3 years ago
This approach is described by the cutely named BFF – backend for frontend – pattern. (<a href="https:&#x2F;&#x2F;samnewman.io&#x2F;patterns&#x2F;architectural&#x2F;bff&#x2F;" rel="nofollow">https:&#x2F;&#x2F;samnewman.io&#x2F;patterns&#x2F;architectural&#x2F;bff&#x2F;</a>)
评论 #28520166 未加载
EMM_386over 3 years ago
One 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 &quot;happening&quot;.<p>I don&#x27;t like this concept of sending the page structure as JSON.<p>It would require all parts of the UI to just be stuck on &quot;loading&quot; while the back-end essentially the entire page.<p>If you decide to turn this into &quot;ok well make it &#x2F;page&#x2F;a&#x2F;component&#x2F;3&quot; then we&#x27;re back to square one on the whole idea.
评论 #28525009 未加载
chrismorganover 3 years ago
Another 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.)
评论 #28520292 未加载
recursivedoubtsover 3 years ago
Absolutely. Your application API and your data API have different needs and requirements:<p>You application API is churny, specific and tuned for certain screens and user interfaces.<p>Your general data API is, well, general, rate limited, concerned with limiting the ability of that expressive power to damage your system, etc.<p><a href="https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;01&#x2F;18&#x2F;rescuing-rest.html" rel="nofollow">https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;01&#x2F;18&#x2F;rescuing-rest.html</a><p>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:<p><a href="https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;" rel="nofollow">https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;</a>
stickfigureover 3 years ago
I&#x27;ll go one step further than this - build an endpoint for each <i>component</i> 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.<p>In practice this ends up building out a reasonable approximation of what a &quot;public&quot; api would be. Eg, your WidgetList component forces out a &#x2F;widgets endpoint, which might get re-used by some other widget too. That&#x27;s fine. The point is you&#x27;re still working UI-first and making the minimum viable backend.<p>With lots of components on a page, you might end up making multiple calls for the same information. That&#x27;s also fine. You can optimize that later if it becomes a real problem.
评论 #28520260 未加载
评论 #28526721 未加载
评论 #28520282 未加载
urdaover 3 years ago
&gt; Have you seen that list of annoying decisions up there? For one, they are gone now.<p>Erm, even with that solution you still have to consider the changes that impact this schema. These problems didn&#x27;t go away, they simply became masked differently.
评论 #28525082 未加载
aaron-santosover 3 years ago
I&#x27;m stuck in this quagmire and have been for 18 months. Right now it&#x27;s manifesting as a React on GraalJS project written in Clojure. I&#x27;ve learned a lot, but actual progress is, of course, elusive.
Tyr42over 3 years ago
I 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&#x27;s required data, and the back backend which was mostly the legacy version of the site&#x27;s back-end but still had useful logic in it.<p>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.
true_religionover 3 years ago
I 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.<p>On the backend, for performance we have two choices:<p>1. Over-fetch. It&#x27;s relatively cheap doing that from a cache. Most queries don&#x27;t use too many different variations.<p>2. Optimize what data we fetch based on the node&#x27;s children in the GraphQL query received. I don&#x27;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&#x27;s children will be rendering before they&#x27;re hit. Because we enforce #1, we don&#x27;t have to build some general-purpose query-builder a simple check of &quot;Are you X named query?&quot; is good enough. Internally it looks kind of like JSON RPC.<p>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 &#x27;general&#x27; purpose aspects in production you can be fairly efficient (e.g. our GraphQL API is about 5% slower than the JSON API it replaced).
评论 #28530188 未加载
issaover 3 years ago
I 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.
评论 #28520120 未加载
spfzeroover 3 years ago
This 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.<p>I&#x27;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.
da39a3eeover 3 years ago
I agree with this. It&#x27;s tangentially related to it being so standard to refer to every JSON HTTP API as a &quot;REST&quot; API. REST APIs (in the strict sense) are for public integrations (but how common is that really?) &#x2F; other teams in your (largish) company.
vishnuguptaover 3 years ago
&gt; Your business logic has now moved from being haphazardly split between frontend and backend into just backend.<p>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.<p>What&#x27;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.<p>Now not only is your backend API not reusable but also the business logic is brain split between the front end and backend.
gnulinuxover 3 years ago
&gt; #Do you have data to support your claims?<p>&gt; 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.<p>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.
hellisothersover 3 years ago
Love 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.
codetigerover 3 years ago
How 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.<p>Did I miss the whole point?
评论 #28520258 未加载
Fradowover 3 years ago
Since I do almost exactly that, I agree with the sentiment.<p>I&#x27;ll go even further on one point: &quot;Imagine if you could just send it the whole “page” worth of JSON&quot; =&gt; 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.
andreisbcover 3 years ago
I&#x27;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!
omnimusover 3 years ago
Inertia.js does this very well. Using server routing on JS frontend and autoloading page data.
runawaybottleover 3 years ago
This approach does not help when you need to transform data models on the frontend (and possibly send it back).<p>Imagine trying to make sense of:<p>merge(pages.a.section.main.data, pages.a.section.secondary.data)<p>What does that even mean? It’s going to get hard to prep abstract data.
turtlebitsover 3 years ago
I think Hasura (automatic APIs for your DB) is the best thing since sliced bread. It handles 95% of my backend needs.
theteapotover 3 years ago
There should just be one endpoint that dumps the entire DB as JSON. The front end guys would love it!
015aover 3 years ago
We have an API similar to this.<p>I&#x27;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&#x27;s profile picture, and you want to round it, should that be a property on the JSON structure? E.g.<p>{ &quot;profilePicture&quot;: { &quot;url&quot;: &quot;<a href="https:&#x2F;&#x2F;mycoolpicture.com&#x2F;pic.jpg" rel="nofollow">https:&#x2F;&#x2F;mycoolpicture.com&#x2F;pic.jpg</a>&quot;, &quot;rounded&quot;: true } }<p>Or a property of how the frontend transforms that structure into HTML? In other words, its an opaque, intrinsic property of the &quot;profilePicture&quot; &quot;JSON-component&quot;, every profile picture is rounded, because that&#x27;s just how profile pictures are.<p>Until you hit the one page that wants one that isn&#x27;t. No problem, maybe a generic &quot;picture&quot; &quot;JSON-component&quot;. Or you hit the page that needs a squircle. Ok maybe we do need a &quot;border&quot; field, and why not just make its value the same as the CSS `border` property and oh my god are we just reinventing HTML?<p>There&#x27;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: &quot;title&quot;. Kinda-generic: &quot;heavy&quot;. Or literally just CSS.<p>What do these styles mean when faced with user browser preferences, such as dark mode or accessibility systems? By the API contract, &quot;cardTitle&quot; means &quot;roboto, 16px font, #0000ff font, whatever&quot;; 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?<p>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&#x27;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 &quot;understand&quot; &quot;implicitly&quot; that it means &quot;bottom bar&quot; on small screens, and do the best it can, and now the entire point of this exercise is out the window and we&#x27;re not obeying the API.<p>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.<p>Another is in interaction. Button which opens a new tab to google.com; easy, no problem. But in every reincarnation of this idea, I&#x27;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<p>{ &quot;form&quot;: [ { &quot;formTextField&quot;: { &quot;id&quot;: &quot;firstName&quot; } } ... { &quot;formSubmitButton&quot;: { &quot;endpoint&quot;: &quot;&#x2F;createUser&quot;, &quot;verb&quot;: &quot;POST&quot;, &quot;arguments&quot;: { &quot;id&quot;: &quot;firstName&quot; } ... } } ] }<p>How do you handle the response from &#x2F;createUser? What if there&#x27;s an error? What if one page that calls &#x2F;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&#x27;t necessary?<p>This doesn&#x27;t feel as good to me as the rest of this line of thinking, which is already not great. It feels like &quot;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.&quot;<p>Look; there&#x27;s a reason why data and views are separate. This idea isn&#x27;t new. I hope you realize that, but I&#x27;m afraid you don&#x27;t; our industry does tend to revolve in cycles, but this is one that shouldn&#x27;t come back.<p>There are <i>really</i> 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 &quot;Link&quot; { &quot;title&quot; &quot;subtitle&quot; &quot;body&quot; } cards, maybe a &quot;MapResult&quot;: { &quot;latitude&quot; &quot;longitude&quot; }, 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&#x27;t built for it, but it can work.<p>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&#x27;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&#x27;re talking, its actually pretty good. And if you&#x27;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.
评论 #28538667 未加载
jayd16over 3 years ago
tl;dr YAGNI