Remix Architecture Patterns
AI Generated Video Summary
This Talk introduces the Remix architecture patterns for web applications, with over 50% of participants using Remix professionally. The migration from single page applications to Remix involves step-by-step refactoring and offers flexibility in deployment options. Scalability can be achieved by distributing the database layer and implementing application caching. The backend for frontend pattern simplifies data fetching, and Remix provides real-time capabilities for collaborative features through WebSocket servers and Server-SendEvents.
1. Introduction to Software Architecture and Remix
A software architecture is the blueprint for your application. React is now also an architecture that can be implemented by different meta frameworks. Today, I want to talk about Remix architecture patterns. Over 50% of the participants stated that they use Remix professionally. 50% of those who use Remix professionally migrated from React Router to Remix. Let's talk about architecture patterns.
Hey, everyone. What is a software architecture again? A software architecture is the blueprint for your application. You design an architecture to fulfill your requirements and fit to your use case and solve the problem you're having. And then you pick a text deck to implement the architecture that you just signed.
It turns out that React is now also an architecture. This is a really cool take by Dan Abramov, who was recently on Twitter reflecting about the state of React. He states that React is no longer just the library, but also an architecture that can be implemented by different meta frameworks. Really cool take, and I'm excited where this leads.
And today, I want to talk about Remix architecture patterns that is commonly used and implemented architectures with Remix. My name is Andrej. I'm a developer from Germany. I work at LinkedIn and currently live in Cupertino, California. In my free time, every Monday, I tutor aspiring developers on Meetup, and in general, I love building for the web. Before I moved to the United States, I wrote my master's thesis about API management patterns. I conducted interviews, and talked to software engineers and architects from different companies, and then identified patterns in how these companies manage their APIs. I then documented the results in a coherent and organized way. For that, I created a pattern language, and creating that pattern language was a lot of fun to me, and I learned a lot. So, I wanted to do it again, this time for Remix. I want to answer the question, how is Remix used? So, for this, I created a survey that I called The State of Remix, and I got 74 replies. Let's keep in mind that 74 replies is not enough to be statistically relevant, but it's certainly enough to analyze or identify common usage patterns. That said, I still want to showcase some of the numbers that I got out from the survey, just because they surprised me so much.
The first one here is that over 50% of the participants stated that they use Remix professionally. This blew my mind, considering that Remix version 1 has only been released a year ago, but it's really great to see that such a big part of the community already makes money with Remix. Of those who use Remix professionally right now, 50% stated that they migrated from React Router to Remix. I thought this number would be way higher, considering the clear migration path between React Router and Remix, and also obviously the connection between the two technologies. But it turns out folks really move from all different kinds of technologies to Remix. React Single Page Applications was still the biggest source or region where people moved from, but Next.js was mentioned a lot too, Express.js, LGS, Rails, Vue, but in general there are just so many different technologies to build for the web and folks really stated they move from all different kinds of backgrounds and technologies to Remix. I think this is really cool to see, but let's talk about architecture patterns. Before Remix, or in general, we can all agree this is a big part of the industry standard right now. You have the single page application architecture, we have an SPA running on the browser on the front end and you have a standalone API server that then communicates with the SPA.
2. Introduction to Remix Architecture
3. Moving from SPA to Remix Architecture
To move from a single page application to a Remix app, a temporary architecture is created. The React code is moved from the SPA to Remix, while the standalone API server is kept. Requests from the SPA are passed to the legacy API through the Remix HTTP handler. This allows step-by-step refactoring of the SPA code to take advantage of Remix features. Code from the legacy API server is gradually moved to the Remix HTTP handler. The most common variant is the standalone Node.js one, which provides familiarity, flexibility, and compatibility. Another cutting-edge variant is the standalone edge, which deploys to edge environments, offering geographic proximity to users.
The question then arises is how do we get from this single page application, industry standard architecture, to our Remix app? The answer is what a lot of folks say they do is they create this temporary architecture to then move to Remix. They move the React code from the SPA inside Remix, but keep the standalone API server around. Then, they pass requests from the SPA to that legacy API, forwarding the requests through the Remix HTTP handler.
What's really cool about this architecture and this approach is that you now can refactor your SPA code step by step to take more advantage of the features that Remix provides. So you refactor your use query or your use effects with the fetch calls to forms and use fetcher in Remix and really make your application progressively enhancing based on the capabilities that Remix provides. At the same time, you move more and more code from the legacy API server into the Remix HTTP handler, so you really can do this in vertical feature slices and step by step take more and more advantage of Remix capabilities. And this is obviously also an architecture pattern, even if it's hopefully a temporary one because at the end of the day, you want to really sunset that standalone API server and move everything into Remix, have full control of the web server and end up with that standalone Remix app that we talked about earlier.
But it's super generic, right? We just say database and a server environment, so we have to be more specific here really to make this more productive. And if you talk about different variations in a pattern language, you talk about variants and variants are actually all the same thing, just has different characteristics. You are allowed to have a favorite variant also, but here I just want to talk about the most common bonds based on the survey data. And the first variant that was mentioned the most is the standalone node one. So you use the ExpressJS adapter, the remix app server or any other deploy target that is Node.js based and now you have this standalone Node.js app server, your remix app now running on Node.js.
What's really cool about this variant is that it feels very familiar. If you use ExpressJS before or any other Node.js based web server or like standalone API server, you can kind of like it's the same thing. Now you only have remix running on top of there as well. It's also super flexible because you're not tied to a specific hosting provider or service. You can deploy this anywhere where Node.js can be deployed. And it's very compatible with all the npm packages and the code you've wrote in the past. So really cool variant. And alternative variant is the standalone edge one. And this one is obviously very cutting edge, because we deploy now to an edge environment. I really believe that is a trend, edge deploy, that remix really helped accelerate. I feel like it all started with remix. Remix was pushing having adapters for cloud for workers and pages, which were also the adapters most mentioned in the survey. And I feel like it all started from there. And now we have so many different edge environments to choose from, right? We have dino deploy, we have fly.io, which creates this regional distributed long running servers, which is an edge like experience. Vercell and Netlify both added their own edge environments. So it's really cool to see that we have all those different adapters now for remix, so to deploy to the edge. But what we get from all of them is this geographic proximity to our users. By deploying to the edge, we distribute our application across the globe to different edge environments, different edge servers.
4. Scalability, Caching, and Enterprise
A cool pattern is to create a scalable application using edge environments. However, it's important to consider the database layer and regionally distribute it for optimal performance. Another pattern is to use an application cache, such as Redis, to mitigate bottlenecks caused by frequent database fetching. This pattern is not specific to Remix and is useful for complex applications. In Enterprise scenarios, the SPA architecture is embedded in a more complex environment, involving collaboration among different teams.
And what's really cool about this also is that a lot of those edge environments actually are serverless. So we get the same kind of scalability that serverless provides us. And even if the environments are not serverless, they mostly do the same kind of trick. So we create this very scalable application.
So really cool pattern. But what we have to keep in mind here with this variant is that... And that's why I also highlighted the database layer, that if we deploy to different regions, we also have to do that with our database. Otherwise, we won't get as much out of the geographic proximity. We want to get down the response times, but if our database is super far away from our web app, we don't get as much out of it. So we also have to regionally distribute our database. Just something to keep in mind, but still an amazing pattern variant nonetheless.
And variant number three is probably my favorite one, and it's called this application cache. So this one is server environment agnostic. It doesn't matter which server environment you pick, this pattern will always work. You just add a Redis or any other in-memory application cache to get rid of some bottlenecks. So the goal here is if your application grows in complexity and you have to fetch a lot every request, to kind of mitigate some of the penalties regarding response time, from fetching from the database so much, by adding a cache. And this is not really a Remix-specific issue. When your application becomes complicated, you have to counteract that. But it's really easy to do this with Remix. For instance, I personally always fetch from my root a lot of data, like the user settings and the user object, and the preferred next video to watch, and then the promoted purchases and whatnot. And then having that all live in a cache, so that you don't have to fetch it on every mutation, is a great way to mitigate the bottlenecks that can come from fetching from the database too often. So, definitely a cool pattern. But let's talk Enterprise. When I say Enterprise, that just means it becomes even more complicated, right? We already said, our application can become more complex. You have to add something like Redis, right? To counteract that. But what if it becomes more and more complicated, right? This is basically Enterprise. It's like the end boss in complexity. And when we look back to the industry standard right now, we have this SPA architecture. So Enterprise, it just means you have to embed your SPA in an even more complicated or complex environment. But a lot of different teams work together to create one system architecture.
5. Backend for Frontend Pattern and GraphQL
Fetching data from multiple APIs in a frontend can be complicated. The backend for frontend pattern helps simplify this by creating a middleware service that handles the fetching logic. GraphQL is a great addition to this pattern, allowing for easy orchestration and aggregation of data from different servers.
So you might have to fetch from a lot of different APIs that all provide different entities for your application, different business logic for your application. And then you have to manage all that loading states and error states and authorization and retries and revalidation, optimistic UIs, all of that for those different APIs that are all probably working a little different inside the frontend, inside your SPA. And that can become super complicated and a mess.
That's why a lot of folks resort to implementing the back end for front end pattern. So that is part of the industry standard as well. It's like this very commonly known pattern where you create this middleware service that is tied to your front end and kind of obstructs away some of the complexity of the system. And you can move a lot of that fetching logic inside this orchestration layer. So now your SPA is protected from fetching from different APIs and you do that in your back end that is for your front end.
And this is also a really cool use case for GraphQL. Because if you add GraphQL on the server, this is where it really shines, right? Orchestrating requests, different servers, aggregating data, and then making it accessible. Obviously you can also add GraphQL here to the SPA and then you have to fetch only from one end point. But this is a great use case for GraphQL nonetheless.
6. Backend for Frontend Pattern and Real-Time
Remix naturally implements the backend for frontend pattern, providing full control of the web server and eliminating the need for standalone orchestrations or middleware layers. This architecture pattern can be used in an enterprise context and can be enhanced with GraphQL for data aggregation. Remix offers flexibility in deployment, supporting long-running servers, serverless environments, and edge deployments. Real-time patterns, such as those used in Figma and Google Docs, require frameworks with strong real-time capabilities.
So the question now is we know this is the industry standard or part of the industry standard right now. How do we translate this to Remix? And it turns out, and I never thought about it this way, and I think it's so cool, is that Remix naturally implements the backend for frontend pattern. A lot of folks in the survey stated that they specifically use Remix to implement a backend for frontend architecture. And it turns out the Remix documentation actually has the content about this explaining how exactly Remix works as a backend for your frontend. But I never saw this and I never thought about it this way. And I think it's just so cool that if you use Remix, you get full control of your web server, and your web server replaces the need for any standalone orchestrations, mid-slash middleware layer that you would have needed otherwise. So when you use Remix to implement your SPA, you get the backend for frontend pattern out of the box naturally provided by Remix. I think this is just really cool to think about it this way. And obviously, this is an architecture pattern that we can use in an enterprise context. And we can even add GraphQL on top if we need it, right? This is where GraphQL shines to aggregate and orchestrate data from different endpoints. And that then would translate to a variant of that background for frontend pattern in Remix. Sweet. So, we have three different pattern candidates that we identified that hopefully temporarily pass through to Legacy API, that kind of acts as a migration step. And then we have to standalone Remix app. This is where we all get started. And then if it becomes more complicated, we out of the box get this backend for frontend architecture pattern implemented out of the box by Remix. This then creates those three different patterns that we identified. And we saw that there are a lot of different variants, and there are so many more variants that I don't even mention. I didn't really talk about Fly.io, Netlify, Resell, those cool hosting providers that provide so many other interesting things for us. But it really shows how flexible Remix is in general, right? You can deploy to long-running servers, to serverless environments, and to the edge. And we can easily add something like Redis to our application to mitigate bottlenecks and really vary our architecture based on our use case and really align Remix with what we want to build, which is really cool to see that Remix is so flexible.
At the end, I just want to talk about real-time patterns. So far we talked about general architecture patterns, and now I want to double click into real-time. This is ongoing discussion between static experiences and very dynamic experiences and which framework is better suited to develop which kind of application. We call it document web versus web app web, and then we ask ourselves which framework is really suited to create those highly dynamic experiences that we want to create in 2022. Sometimes Figma or Google Docs are used as examples for this highly dynamic experiences we want to create. Which framework would you choose to create something like Figma? In my opinion, I think the scale isn't long enough. The spectrum between static and dynamic, we need to add more to the right of dynamic, because what makes Figma and Google Docs highly dynamic are really its real-time capabilities. These are full stack reactive applications and shout out to convex.dev that do a lot of cool work in that area. There's actually a lot of startups that try to do something in this area.
7. Real-Time Capabilities and Collaborative Features
None of the React frameworks provide primitives or conventions for real-time capabilities. Figma and Google Docs require building on top of the framework. Serverless functions don't support streaming or WebSocket servers, making it difficult to create a common API. However, there are ways to create real-time capabilities in React apps. Three patterns for implementing collaborative features in Remix will be showcased.
But from a framework perspective, I believe that none of the frameworks, that frameworks that we have right now on the React ecosystem, provides primitives or conventions to implement real-time capabilities. So when we talk about something like Figma or Google Docs, this is nothing your framework helps you with. You just have to build those things on top of what the framework provides. That said, that is not really a fault of those frameworks either, because there's just so many open questions on an infrastructure level. Just to give you one example, serverless functions intuitively don't really support something like streaming, like long living responses, or WebSocket servers because they want to shut down after they handle the request. And AWS provides its own solution for how to make WebSockets work with serverless, like connection pooling and stuff like that. It's just very specific for each infrastructure provider. It would be really hard to abstract that and create a common API on a framework layer. A lot of construction still going on and open questions, but there are still ways right now obviously to create real-time capabilities in your React apps. I just want to showcase three different patterns on how to implement collaborative features in Remix.
8. WebSocket Server and Remix Deployment
You can create a standalone WebSockets server and deploy it wherever WebSockets are supported. It can be separate from your Remix app, giving you flexibility in deployment. Another option is to add the WebSocket server to the same server environment as your Remix app. This allows code and type sharing between the WebSocket server and Remix application, but deployment options are more limited.
In the first one, I call it standalone WebSockets server. It's probably the most straightforward one. You just create a standalone server with a WebSocket server on top of it and you can now deploy it wherever WebSockets are supported, like a long-running Node.js server, for instance. And you have it separated from your Remix app. What's great about this is that you stay flexible on where to deploy your Remix app, even if you deploy it to environments like Netlify Versa, which don't really support WebSockets right now.
Since your WebSockets server is independent, you can still deploy your Remix app there. And then you have to have this, like your client-side application of your Remix application has to communicate with the WebSocket server. And you have to kind of remove some of the logic that you were previously handling in Remix with WebSockets now. But it works and it's a great way to add real-time capabilities. Even cooler, I think, depending on use cases, to add the WebSocket server to the same server environment that you use for your Remix app.
Right, Remix really exposes the underlying Web server that is used when you use Remix. If you pick the Express JS adapter, you have access to where the Express app is created. And in there you can also just add a WebSocket server. It's just a Node.js environment. And now if you pick the right deployment target you can have your WebSocket server run next to your Remix HTTP handler. What is really cool about this is with Remix we were so happy that we can share code and types between our frontend and backend. And now with this we can also share code between our WebSocket server and our Remix application. But we are a little bit more limited because we can't deploy to all the environments that Remix supports, because we have to make sure that this environment also supports WebSockets.
9. Server-SendEvents and Real-Time Capabilities
Server-SendEvents can be used as an alternative to WebSockets for creating a connection between the frontend and server. Remix provides the necessary tools for mutating server state based on client state and synchronizing them. Server-SendEvents can be implemented within a Remix application, allowing for full stack reactivity and real-time capabilities. Remix doesn't have built-in primitives for this, but it can be achieved by leveraging the Remix platform. There are three real-time patterns for implementing real-time capabilities in Remix, with Server-SendEvents being particularly exciting. Additionally, there is growing interest and discussion around Server-SendEvents and Remix. It's worth noting that a significant number of participants started programming with Remix, highlighting the need for beginner-friendly content and a more approachable community.
That brings me to my last pattern here, which I'm most excited about. And that is to use Server-SendEvents as an alternative technology to WebSockets. So we use Server-SendEvents now to create a connection between the frontend and our server that creates this like full reactivity. And the way Server-SendEvents work is that they create a one-directional stream between the server to the client so that the server can send packages like information or events to the client. So it's not bidirectional like WebSockets, but if you really think about it, the other way is already covered with Remix.
Remix provides everything we need to mutate our server state based on our client state. So this forms and forms submits and use fetch. We can already mutate data. And then since Remix out of the box revalidates our client state and synchronizes our client state with our server state. The only thing really is that is left is to inform one client if another client changes the server state. Right. This is what full stack reactivity in that in that sense means. That on the full stack on the server. If you change the state that your client reacts to these changes and the server sends events you can kind of ping your client and inform them of a change. And then you can trigger revalidation on the payload can even include the entity that has been updated and then you manage that in React state.
So what's really cool about server side events though is that you can actually implement them inside your Remix application. So inside a resource route in the loader of your remix app you can add the code to create a server side events endpoint and then you can just use the platform on the front end side of your remix app and a user factors react to create the connection to that endpoint. And now you have full stack reactivity and real time capabilities for collaborative multiplayer experiences in your remix app today. Remix doesn't really provide any primitives and conventions to handle that, but since Remix exposes the platform for us, this is like this is supported basically out of the box with Remix today. And I think it's just so cool.
So we have three real time patterns here how to implement real time capabilities with remix. You can use WebSockets in one way or the other with Remix. But even more, I'm even more excited about service and events. And there's also a lot of traction right now around this topic on GitHub, on the discussion about service events and Remix. And I'm just really excited to see where this leads. But at the end, I want to leave you with one fact from the survey. And that is that six out of the 74 participants, around 8%, stated that they started programming with Remix. And I think this is just a great opportunity for all of us to create beginner-friendly content for Remix. I personally would have loved learning programming or web development with Remix. It probably would have helped me to understand the platform better before jumping into React. Now, I feel like I'm thinking a lot of times in a React way, even though I want to think in a web way. And I think Remix would have provided a great way to get started with this. And that just means we all have a job to do right now, and that is to make the community more approachable for beginners.