Architecting performant Web3 apps

Performance is a holy grail for Web developers. It means that you can get to your users faster, and that they will be more engaged with your app. The web is a platform that is constantly changing, and is constantly evolving.

We have made a lot of progress in the last few years in regard to web performance and architecture. From monolithic web apps to microservices, we are now entering an era of proximity and proximity-based architectures.

What is proximity-based architecture?

There are a few concepts that are important to understand when it comes to building applications close to your users.

  • Content Delivery Network: A CDN is a network of servers that serves content to users. It is typically used to serve static content such as images, CSS, and JavaScript. CDNs are meant to be close to the user, and they are typically located all around the world.
  • Microservices: A microservice is a service that is designed to be small and lightweight. It is typically composed of a few components, such as a database or a web app.
  • Edge computing: Edge computing is a technology that allows code execution on a server that is close to the user.

Proximity-based architecture is a combination of these three concepts into one. The goal is to deliver static content to users via CDNs and execute microservices on edge computing.

Next.js is a great example of these concepts

Static content is served by a CDN, and microservices are executed on an edge network. The goal is to give a great experience to your users.

Let's take a look at how this works in the world of Web3 development.

Decentralized and quick?

Web3 uses a blockchain as a central repository for all data. This means it is the source of truth. One thing to note, however, is that while a blockchain is distributed all over the world, it is not as fast as an edge network.

This means that we cannot count on fast response times from the blockchain. This is a feature and not a bug. We are dealing with a distributed system meant to be resistant to central ownership.

If a blockchain is slow, can we make performant Web3 apps?

There is no definitive answer to this question. There are many factors unique to each project. There are some common factors that we can extract and learn lessons from.

To go over those factors, I would like to use my Supabase Hackathon project submission as a reference.

In this project, users can buy an NFT and access an exclusive Discord community for NFT owners. The premise on its own is simple. Our users can take the following actions:

  • Buy an NFT.
  • Access the community Discord.

If you watch the video, you will notice that the blockchain has a loading time that we cannot play around with. The rest, however, is pretty smooth! To accomplish this, I supplemented the slowness of the blockchain with a few microservices. Here are a few tips to make your Web3 app performant!

Tip #1: The blockchain is the source of truth... but you can supplement it!

You should rely on the blockchain to be the single source of truth for your data. However, you can implement your own data layer that mirrors the blockchain! This is a great way to make your app performant and will help you avoid slow response times. Here are ways to supplement the blockchain with your own data layer.

  • Use an indexing service like (Covalent)[https://www.covalenthq.com] or (the Graph)[https://thegraph.com/]to index your data and make it searchable.
  • Make use of non-blocking serverless functions from companies like (Vercel)[https://vercel.com/] or (Supabase)[https://supabase.com/] to save data produced in a remote database.
  • Use a service to index blockchain data that your application will need. In my case, I deployed a Node.js server on (Fly.io)[https://fly.io/] that I paired with a (Supabase)[https://supabase.com/] edge function.

Tip #2: Server-side rendering with stateless sessions is great!

When you are architecting your Web3 application, you might be tempted to use client-side rendering, since wallets depend on the window.ethereum. A low-hanging fruit is to use server-side rendering with stateless sessions!

Stateless sessions respect the ownership of user data and work well with SSR!

By implementing this approach, you can prefetch data from the blockchain and render the page without having your users see a loading screen. See this (Vercel Example)[https://github.com/vercel/examples/tree/main/solutions/web3-sessions] I built to learn more.

Tip #3: Choose your blockchain well and plan your smart contracts ahead of time.

When architecting for speed and performance, you will eventually reach a ceiling. You cannot go faster than the transaction speed of the blockchain you use. For real-time games and fast-paced applications, you will need to get creative. I have a few guidelines to help you navigate this crazy space:

  • While Ethereum is the dominant blockchain, you can use other blockchains as well. There are really healthy ecosystems of new blockchains.
  • Review your smart contracts and make sure they are well designed. Not every action needs to be a transaction. You can do a lot with view functions and traditional data stores.
  • Use optimistic updates while you wait for the blockchain to confirm your transaction.

Recap

Architecting for performance is hard. Every project is different. My own experience taught me a lot about this topic. My philosophy can be summarized as follows:

  • Supplement the blockchain with your data layer. (Keep in mind that the blockchain is the source of truth!)
  • Prioritize for speed in your rendering methods.
  • Use stateless sessions to prefetch data from the blockchain.
  • Use optimistic updates while you wait for the blockchain to confirm your transaction.

You can always support this effort by following me on Twitter!