Server-side rendering frameworks
By: Diego Mosquera Soto
Tags: Svelte, JavaScript, Angular, Nextjs, Server Side Rendering

You probably know Onefootball for our iOS and Android Apps. They provide an amazing experience with a large set of features to our users and they have been our main business focus over the last years, but at the same time we have not been giving enough attention to our website (onefootball.com). We hence decided to bet on a complete reboot of the project. This means an entire new “Consumer Web” team with dedicated engineers, designers, QA and product manager.

We are currently at an exploration and team-building phase where very important decisions need to be taken to set the foundations for the project. Regarding web architecture, we are taking some time to do some research on server-side frameworks.

Currently

At onefootball.com we are using Angular(5.2) and its Angular Universal package to serve our content to our users. We were blocked while updating to Angular v6 due to the RxJS library (v5 -> v6 upgrade) and some third party libraries that depended on this library.

Our current web app can be seen as a quite complex product. Due to the nature of our business we serve a large amount of data to our users. We have around 3000 possible routes in each of our 11 different markets and the difficult part here is that routing is dynamic and depends on external factors: our newsroom editors, 3rd party titles, competitions changing name every year, etc. Furthermore as our business is serving content, SEO is a fundamental pillar of our app. Our metadata needs to be always available, up-to-date and finally, every link needs to be navigable by search engine indexing bots.

When the decision to use Angular was taken, Angular2 was in its beta phase and the Angular Universal project was in early stages. It wasn’t even using the same version control number as the rest of the main Angular dependencies. This represented for us that we had to build several custom functionalities from scratch because they weren’t provided by Angular Universal (e.g. transfer state).

So far, our development experience was good in general and the Angular framework served its purpose. Once the basic foundations and “HOW TOs” were agreed on, the web app was developed in a fast pace. By following Angular style-guide and suggested architecture we managed to keep our code structured and organised. An advantage of going with a heavily opinionated framework.

However, we have encountered some issues in our current app that made us reflect on our decisions; some are Angular related and some are due to using Angular Universal in production in early stages. Just to name a few: dated dependencies, difficulty to track memory leaks, dated strategy on ngUniversalRenderingEngine, the way we serve our CSS files, etc. One might say probably the fastest way to go is by migrating the whole project to a newly generated Angular project from the cli.

Research

To date we have done research on three SSR frameworks: Next.js, Angular (v7-v8) and Svelte with Sapper.

There are known technical needs that every framework needs to solve in order to be considered as a preferred technology for the consumer-web project.

- Server side rendering.

- Ability to extend the node.js server to include middlewares and redirects.

- Read from different config files in server- and client-side, and production and staging data.

- Ease of translations and route localisation (dynamic routing).

- TypeScript is a big plus!

Disclaimer

It would be amazing to get some feedback from the community. I might be missing some information that can be considered essential for another fellow developer. So I would like to encourage a discussion to enrich the article and create a better knowledge base. Every input is encouraged!

Next.js

Next.js is an agnostic framework to generate server-side websites. It works hand-in-hand with React but as it says in the project description it can work with other technologies. It is created by Zeit, currently a market-leading company, focusing on serverless tools crafted for web developers.

Next.js pros

Server side rendering is handled in a straightforward way and it doesn’t require too much configuration to have a project running.

Translations handled by the library next-i18next. A good strategy that handles namespaces and this way the app doesn’t get bloated with translations files.

Class-based components and function components work properly with Next.js.

Different config files are easy to load in the app.



console.log(serverRuntimeConfig.mySecret) // Will only be available on the server-side console.log(publicRuntimeConfig.staticFolder) // Will be available on both server and client

Next-i18next Library provides a way to a add `lang` property dynamically to the top level HTML node. This is something we couldn’t manage to have with Angular Universal out of the box and required a custom implementation.

Next.js cons

Route localisation. Next.js like Nuxt.js (Vue.js preferred framework to do SSR) it generate its pages using a file system strategy. It means that it creates routes based on the files inside the /pages folder.

— pages

|- index.js

|- about.js

Next.js matches the file’s name with the route and renders that page. so www.test.com/about will render /pages/about.js page. However, www.test.com/es/acerca will render a 404 because the file doesn’t exist in the pages folder. There is no strategy to add a placeholder like {lang}/{about} and determine the route name dynamically.

Solutions:

1. Use a third party library next-routes to map the routes to the existing pages. The last commit in the repository is a year old, so it seems dated to me. Additionally, this approach will send the entire routes manifest to the client side app and it will bloat the app.

2. Create a custom middleware to map the routes and change the framework’s routing system in client side as well. It can be difficult to maintain in the long term. Resources: Custom-server-and-routing.

TypeScript is not a first class citizen in React or next so it requires more configuration. However, it was easy to set up and there are plenty of resources to set up and run this project.

Next.js conclusion

I will try to use it but my only concern is the routes localisation. Once it is solved there is no constraint. Coming from Angular development, the file architecture and having HTML inside the JavaScript file can be perceived as disorganised. However the React community has solved a lot of these problems using patterns like HOCs (High Order Components), moving non-view logic to Redux actions or helper functions. Since the end of 2018, with the release of React Hooks and the Context API, it is a lot easier to do what HOC used to do.

Angular

Angular is a JavaScript framework built by Google. It provides APIs for HTTP transactions, routing, service workers, metadata, etc. With this approach, Angular focuses in providing a complete ecosystem to develop SPAs. With the use of Angular Universal’s package users can build server-side rendered apps.

Angular Universal uses the Angular engine to generate a HTML page in the server and send it to the browser. It appends the JavaScript files needed to keep running the application client side.

Angular Pros

We have a lot of experience doing server-side rendering with Angular.

Angular style guide to keep consistent code.

RxJS and TypeScript as first-class citizens.

Translations handled by the library ngx-translate. No namespaces and all app’s translations must be in a single file per language. (No translations by namespaces like in next). Route localization with localize-route is an easy way to provide dynamic routing.

Modules can be loaded by lazy modules.

Different config files are easy to load in the app and injected via a config service.

No way to provide “lang” property to top level HTML node. But it is easy to do on the server-side when building the HTML.

Angular Cons

Relies on class based components (no JSX function components like React/next).

No idea if it will be compatible with Angular’s new rendering engine out of the box. This will stagnate dependencies’ updates.

Some developers prefer to work with React based frameworks. (Not a big con).

JavaScript libraries need to be encapsulated in an Angular way so it can be used. This limits the amount of libraries that can be used or makes the process slower if they are needed.

Angular Conclusion

Angular framework is our current framework of choice, one main advantage being that we’re already well experienced with it (we know caveats and almost every how-to). Choosing it would be “risk free”. However, we also know its downsides, and our decision should be motivated by technical arguments.

Angular heavily uses the Object-Oriented Programming (OOP) paradigm. This can discourage some JavaScript developers, as the JS community has lately been pushing towards adopting Functional Programming (FP), given it being helpful to avoid mutability problems. However, I don’t consider this to be a big issue for us; we believe that any experienced developer understands the advantages of the OOP paradigm and this debate shouldn’t be a blocker in choosing the framework to use.

Very important to consider here is the upcoming release of a new rendering engine (Angular Ivy) that can affect Angular Universal (the Angular package for server-side rendering). If Angular Universal is not up to date with the new engine we may have to avoid updating packages until everything is compatible.

Svelte with Sapper

Svelte takes another direction on the way to create web apps. It doesn’t provide a framework with APIs but all the logic happens at compilation time. This has an advantage that the delivered app is not bloated with the framework’s code but is plain parsable JavaScript. The downside is that you have to stick to certain specific Svelte conventions to compile the code.

Sapper is the framework Svelte uses to create complex apps. It follows the pattern of creating one page by main route. However it went beyond Next.js approach and also used some conventions to modularize APIs and server-only endpoints. Routing Svelte.

Svelte with Sapper pros

Current big hype and aims to provide the best experience to developers while avoiding framework bloating.

Open source development and community discussed features and direction.

Reactivity, the component is updated when a top level property has changed its value. This approach encourages functional programming and avoids bugs based on mutability.

Server-side rendering works like a charm.

You can use Express, Polka or happy to run your node.js server and this server is highly extendable.

Svelte with Sapper cons

Difficult to find a big project that is using Sapper. Not battle-tested.

i18n and route localisation: Is in planning stage since the beginning of 2019.

i18n and route localisation. Thread.

i18n and route localisation. Thread. No 100% TypeScript support. Main Svelte’s repository to showcase TS compilation doesn’t compile at first try (I needed to add some new properties to the app instantiation to be able to compile a test project) and it is some minor versions dated. Additionally, Sapper with ts-process compilation doesn’t accept basic Svelte’s reactive patterns $: remaining = todos.filter(t => !t.done).length; . In conclusion, TypeScript doesn’t support Svelte’s conventions from scratch and the plugin stays behind the current dev.

Svelte with Sapper conclusion

I wouldn’t go for Svelte with Sapper at this moment. The project is evolving quickly and there is a strongly motivated community working on it but it seems to me not mature enough for what we need. Svelte’s philosophy is another way to go in the era of frameworks and you can see how committed the community is to provide the best developer experience + better development. Even though Svelte’s current hype is helping for the community to be bigger, I don’t consider it big enough for the complexity of our project and the challenges that we might encounter. I18n strategy out of the box is in discovery stage and TypeScript compilation breaks when using Svelte’s reactive conventions $:... . In conclusion, we have the feeling that we will spend more time solving more technical problems than product problems.

Conclusion

There is no framework that can provide us everything out of the box. Each framework has powerful features that can help us in the development of the next onefootball.com but somehow they are also lacking in features that we would like to have. It is surprising how i18n and dynamic routing are such complex topics that happens to be a pain point for every framework. Finally, it seems that whatever it is the tool that we decide to use, we have a future full of exciting coding challenges.