Time for backend architectures to…React
Since its release by Facebook in 2013, React has etched itself into the canvas of web development. Fast-forward a decade, and it’s no exaggeration to say that React has become the most popular web framework, globally. The majority of the world’s web apps now have a consistent and composable approach for managing state in which the apps react to state changes by re-executing a declarative render tree of pure components.
The evidence of React’s dominance can be seen in StackOverflow trends but also in the vibrance and growth of its ecosystem. Although it is now over 10 years old, it shows no signs of relinquishing its dominance in the frontend development space.
While the core principles of React have been adapted widely within the frontend world, not every backend solution aiming to support a reactive application is built with reactivity in mind. As a result, developers have to often use state management tools and techniques to bridge the gap between the two. Within this article, we will have a look at different methods of how React applications can manage their state when working with a non-reactive backend. Also, we will review several serverless backend platforms that allow developers to build web applications that are scalable, reactive, and real-time.
Choosing the Stack
Developing a new web application is a process that involves a series of critical decisions. The developer might begin by defining the features of the application, deciding how to structure the frontend code, and choosing the toolkit to style the UI elements.
While this may seem like a substantial part of the process, an essential decision still awaits – choosing a method for the application to communicate with the backend and store its internal state. React itself doesn’t dictate this aspect, leaving the developer with the freedom to choose the most suitable approach depending on the type of backend that the application has to deal with.
One option, naive but illustrative, might be to use the built-in fetch API to request the data and store it within the component’s state. Whenever the data is refreshed, the component will render again. This straightforward method carries a notable drawback as the data won’t be shared between different components. Moving one step forward from this solution, a developer could choose to use React Context which is a popular approach to managing a React’s application state. It comes out of the box with React and can be easily coupled with the useState hook to facilitate sharing data between different components. Nevertheless, the React Context solution doesn’t always scale well for large, complex applications. In such situations, developers often turn to a state management package like Redux or MobX.
State management tools can provide a “bridge” between a non-reactive backend and a React.js front-end. To illustrate that, let’s take an application built with React, Redux and a REST API as an example. Architecting an app that uses Redux as a state management involves organizing it into 3 separate parts – the state, the view and actions. The data flow between those is one-directional. A user might trigger an action within the UI which fetches data from a backend API. The received data ends up in a reactive state. The state then notifies the view about the changes and it is up to the view to decide about what to render. The data received is kept in the local memory until refreshed again.
Navigating the Challenges of Cache Management
As mentioned in the above example, the application might keep a copy of the data received from the backend to be able to serve it to the user faster and to eliminate the need to send another network request. In computing science, this concept is called a cache – a small memory store that keeps frequently used information readily available. In the example, the cache is Redux’s state.
Keeping a copy of the data locally, while handy, creates a potential for new issues to emerge. Now, the system needs to determine whether the cached data is current – should we keep it, remove it, or render it in the UI immediately? Rendering data that is readily available in cache but potentially not current can lead to confusion on the user side. On the other hand, fetching it again each time will lead to potential delays and give ground to a poor user experience.
A first and most intuitive solution to resolve this problem might be to associate a maximum age (max-age) value with each piece of data that we store locally. Once the max-age limit is exceeded, the system would fetch the data again. However, determining an appropriate max-age can prove challenging, and sometimes, depending on the data’s nature, it might be impossible.
Here’s where the concept of Stale-While-Revalidate (SWR) comes to the rescue. In practice, SWR first returns the locally cached data to be rendered while simultaneously revalidating the data in the background. If the data has indeed changed, the UI is then rendered again. This technique ensures that the cached data remains both immediate and fresh. Note that SWR doesn’t concern itself with how requests are made – it’s merely a strategy of storing and updating the data.
Two popular libraries that currently support SWR are React Query and SWR (developed by the same team that made Next.js). Both come packed with valuable features, such as caching, deduping multiple requests into a single one, memoizing query results, and a host of performance optimizations. Along with other solutions like swrv and Apollo, they present a comprehensive solution aiming to bridge the gap between a web application’s front-end and back-end.
The Rise of Serverless Backend Platforms
The space of serverless backends is growing rapidly due to developer’s demand. One could argue that as a developer building an application, the main goal is to deliver a high-quality product that solves a problem. This raises a question – do users really care what runs the app on the backend, or do they care more about the utility and frontend experience?
Offloading the challenge of building a backend to a serverless platform can make development faster and more efficient. Both established and upcoming modern backend platforms can provide not only a database – they’re often full-suite of products that can handle the most common application needs from user authentication to data analytics.
The most well known example would be Firebase – an app development platform founded in 2012 which is now part of Google. Firebase provides real-time databases, document store (Firestore), authentication and a variety of tools that are useful for development of web and mobile applications.
Google is not the only provider in this market. Some of the most popular databases are also available in a serverless fashion. Those include MongoDB (Atlas Serverless), MySQL (PlanetScale), PostgresQL (AWS Aurora) and Redis (Uptash).
While the space has a lot of established players, it’s still developing rapidly. Many new innovative startups, like Supabase or Convex are entering to bring new functionality, improved developer experience as well as various functionalities that allow developers to do more in less time.
Convex: A perfect Real-time Backend companion for a Reactive App in 2023
Convex is a full-stack, real-time development platform using 100% TypeScript that has been built with modern reactive UI frameworks like React in mind. The team behind Convex has built and maintained some of the most popular apps, backends and exabyte-scale storage projects at companies like Dropbox and Google.
At the core of Convex lies Reactor – an innovative database that can be accessed with TypeScript cloud functions as its interface. It tracks all dependencies for every query function and whenever any of them updates, Convex reruns the query and notifies any active subscription about the changes.
The above mentioned data is stored not only on the server side. Earlier in the article, we looked at the challenges of managing cache in frontend applications and how much additional work has to be done by developers to ensure that the fresh data is rendered without too much delay.
Querying data in Convex has two awesome attributes that resolve it – caching and reactivity. Clients of the application can subscribe to queries and receive new results whenever the underlying data changes. As the client is notified immediately, the cache can be updated at the exact right spot in time when it needs to be. It’s like having an always ready state that is automatically managed and ensures immediate and fresh data availability, thereby significantly enhancing user experience and development efficiency.
Unlike Firebase, Convex uses relational data models to store application’s data while keeping the flexibility to store documents in semi-structured JSON files. The database is 100% ACID compliant and data is accessed through TypeScript backend functions which eliminates the need to write complicated and tedious security rules.
Convex provides a generous free quota for developers who want to try it out for their next project. Get started with Convex here.
While the landscape of web development continually shifts and evolves, it’s clear that React’s ethos of reactivity has penetrated deeper than the frontend. The technologies supporting web applications are steadily advancing towards providing full-stack solutions that are efficient and intuitive. This helps developers save time and to focus on the main challenge they have – building great applications. New platforms like Convex are pioneering this movement, offering a seamless developer experience that aligns the backend’s reactivity with React’s principles.
Full disclosure: Convex is a sponsor of Software Engineering Daily.