In Part I of our framework evolution series, we talked about how Bravado started with building our web application on just Ruby on Rails and evolved to a Rails + Vue framework. In this part we’ll discuss how we introduced Nuxt and NGINX to our framework infrastructure.
Rails and Vue
After we made the decision to migrate from a Rails-only framework to Rails + Vue, we were able to migrate about 30% of our frontend code to Vue. During this process, we soon realized that while we had solved the framework problem, we still had some infrastructure problems:
- Frontend-Backend Integration: One of the great things about mature frameworks is that they have a lot of solutions that are developed or recommended by the core team; both Rails and Vue get a lot of such support. However, very few solutions exist for the integration of these two frameworks and we soon found ourselves having to come up with custom solutions to designing our folder structure, integrating the frontend with the backend, etc.
- Server Side Rendering: Vue does support SSR but because we had a unique setup of Rails on the backend, Vue on the frontend and a custom library somewhere in the middle, we had to write our own implementation of SSR. This was a very painful process for us because issues would come up and we had to keep supporting, debugging and monitoring for performance issues.
- Hiring: We would need to hire engineers who could or were willing to understand the complexity of our custom integration. Hiring engineers is hard; finding engineers like these would be even harder.
Following one of Bravado’s core values, “Find Solutions, not Problems,” our VP of Engineering, Anton, set out to find a solution that could tackle all these problems without reinventing the wheel with custom integrations. He thought that instead of trying to integrate a small library into rails, we could just have APIs generating data on a backend and some kind of mature library on the frontend.
We started researching options using “best server side rendering tool for Vue” as the primary search criterion. Most of the requests gave Nuxt as a result.
Nuxt to the rescue
Nuxt is an open-source framework built on Vue.js that offers great features to help boost developer productivity and the end user experience.
Nuxt felt like a perfect fit right from the start because:
- Nuxt had a list of features to help us see why it might be useful for us and about 8/10 of those features catered directly to our pain points.
- Many of the solutions we implemented in our custom library were already implemented by Nuxt but are more mature and efficient. By using Nuxt, our development team would be spared the extra responsibility of maintaining, supporting and debugging custom solutions.
- Finding solutions to errors would be a lot less painful.
- Hiring frontend engineers would be easier as they would require little or no knowledge of Rails.
And so the team made the decision to make Nuxt our frontend infrastructure framework!
Migrating from Rails + Vue to Nuxt
Making this decision was easy, but we now had to figure out a plan. All our pages, both static and dynamic, were being served by Rails. Some were fully rendered by Vue and others only used Vue components wherever we needed dynamic functionality. Having these two categories of pages, a new question arose: How do we move hundreds of pages and UI elements from our Rails + Vue application to Nuxt?
For fully dynamic Vue pages, the answer was simple. For example, we had a feature that allowed users to search for members of the Bravado community. For this feature we had a member search page, formerly at `bravado.co/members`, and this page was developed in Vue. Because Vue is at the core of Nuxt, we could easily move these pages to the new Nuxt application and they would work perfectly.
For other pages, however, it wasn’t so simple. Our testimonials request feature, for example, was also developed in Vue but not as a whole page, it was a modal that lived in a Rails static page. To move this, we’d have to implement the page in Nuxt before moving the modal. We’d have to rewrite many pages like this in Nuxt, which would require lots of time and effort from the team.
For the simple part, we first prepared our Nuxt application by creating a wrapper -- we called it Bravado Basic Page. This wrapper page would serve as a shared layout for most of our pages, and it had basic stuff like header, footer, user menu, anonymous user state and logged in user state. We were then able to migrate about 20 percent of our frontend pages to the Nuxt application. We also began implementing new designs and features in Nuxt.
Our pages now existed on two different applications, each with their own servers: the Rails + Vue application and the Nuxt application. We needed to figure out how to make sure this would not affect our users’ experience and make it still feel like a single application. We decided to set up a new server that would be the new entry point for all requests and render pages efficiently.
NGINX is a free, open-source, high-performance HTTP server known for its high performance, stability and low resource consumption. Basically, It receives a request and does what you tell it to do.
We set up NGINX as the new entry point for all requests and then created two sets of rules. The first set of rules are “Legacy rules”; if a user visits a page served by Rails, NGINX goes to fetch the page from the Rails server and returns it to the user. The second rule handled requests to pages served by Nuxt and like the first rule, NGINX would fetch the page from the Nuxt server and also return it. With these rules, a user could visit a page served by Rails and navigate to another page served by Nuxt without knowing they’re from two different servers. Hence, by introducing NGINX, we were able to provide users with a seamless experience despite the complexity of our infrastructure and framework integration.
NGINX was an awesome solution, but it came with a few complexities.
- NGINX is a low-level tool, which means that the syntax for configuration is not very human-readable. As such, very few engineers can write configurations for NGINX.
- Any changes made to the rules required that the entire application be re-deployed, which affected our efficiency.
At this point, we felt like we were jumping through fire hoops to solve a relatively simple problem. We needed a simpler solution and after doing some research, we discovered that the Nuxt community had a module called Proxy module.
A better proxy
Integrating this module was very straightforward. We made our Nuxt application the new entry point for all requests. The proxy would handle requests for Rails pages by fetching from Rails and sending it to Nuxt which then renders the requested page. For a Nuxt request, no extra work needed to be done; it simply rendered the page as it normally would.
With a simpler and better proxy set up, we removed NGINX. This also meant one less point of failure for our application as shown below in the structure of how requests are received by Bravado.
Thank you, Nuxt!
As we were redesigning and creating new features, we continued to migrate our existing frontend flows to Nuxt. In a few months, our entire frontend code was in our Nuxt application! Next, we switched our rails application to backend-only mode and then, we stopped maintenance on our custom library. We were proud that we were able to achieve all this within a few months. Our decision had the following impact:
- Increased efficiency: Development of new features is now about 50% more efficient! Before, when designing new features with rails, we had to create new routes, static and dynamic templates; but with Nuxt we just create new components.
- Increased productivity: With no more custom configurations to manage and support, engineers are able to focus on building new features and supporting our products. Also, when errors come up during development, it’s easier to search and find solutions from the Nuxt community.
- Reduced frontend-backend dependency: Before Nuxt, it was practically impossible to develop on the frontend without backend. Now, we no longer have to worry about breaking things on the backend when new features are implemented on the frontend.
- Hiring is easier: It’s now relatively easier to hire dedicated backend engineers who are skilled with Rails and frontend engineers who are skilled with Nuxt/Vue.
We have settled into a habit of developing with Nuxt, and judging by the rate at which we’re churning out new features at Bravado, we’ve gotten proficient at it.
As for the future of our framework infrastructure, Vue’s core team recently made its official release of Vue3, which will result in a corresponding release of Nuxt 3. We are looking forward to cool new features. Also, some features we plan to build in the future will require real-time interaction, so we’re beginning to think about how we’ll approach this as Nuxt is not best suited for real time communication.
Throughout this process, our engineering team embraced another awesome core value at Bravado, which is to ‘enjoy the ride’. We had some challenges, found solutions to them, and now we’re glad we can finally focus on what really matters, which is creating value for our customers!