Arpit Pathak

Thinking | Learning | Coding | Sharing

19 Feb 2022

Implementing domain based Multi-tenancy - 1

In normal scenerios, you build a web-application and host it on a server. Then you purchase a domain which you then link to this server’s IP. Other users can then access the web-application using this domain of yours.

However, often times you also want to give the users the ability to map their own domains to a web application which is hosted on a server which you control. Now the users can open the web-application on their own domains as well. We call this types of web applications as domain based multi-tenant applications and the users as the tenants.

From Wikipedia -

Software multitenancy is a software architecture in which a single instance of software runs on a server and serves multiple tenants.
Systems designed in such manner are "shared" (rather than "dedicated" or "isolated").
A tenant is a group of users who share a common access with specific privileges to the software instance.

In this tutorial we will show you how you can build a website which can also be accessed by users using a custom domain of their own choosing.

I’ll make this a 2 part series, in each part we will cover the following things separately-

  1. Set up NextJs to programmatically create unique pages based on user’s custom domain (this post)
  2. Setting up Openresty as your webserver and dynamically provisioning SSL certifcates (Part 2)

So, let’s get started.

Setting up NextJS

Let’s first set up a NextJs application which can serve unique content page based on the custom domain.

Inside the pages directory create a new dynamic page called - _sites/[site]/index.tsx.
Inside the pages directory create a file called _middleware.ts.

Your pages directory should now look something like this -

.
├── _app.tsx
├── _middleware.tsx
├── _sites
│   └── [site]
│       ├── index.tsx

By creating the _middleware file you can run code before a request is completed. Based on the incoming request, you can modify the response by rewriting, redirecting, adding headers, or even streaming HTML. Read more about NextJs middlewares

Inside the _middleware.tsx add the following code

import { NextRequest, NextResponse } from 'next/server'

export default function middleware(req: NextRequest) {
  const { pathname } = req.nextUrl
  // Get hostname
  const hostname = req.headers.get('host')

  // Prevent security issues – users should not be able to canonically access
  // the pages/sites folder and its respective contents. This can also be done
  // via rewrites to a custom 404 page
  if (pathname.startsWith(`/_sites`)) {
    return new Response(null, { status: 404 })
  }
    return NextResponse.rewrite(`/_sites/${currentHost}${pathname}`)
}

Let’s understand what the code does when it receives a request for the URL - https://arpit.example.com/about

  1. The path which is /about is stored inside the pathname variable
  2. We extract the domain value from the host header of the HTTP request and store it in the hostname variable.
  3. NextJs supports this awesome ability to rewrite a request path to a different destination path. The line NextResponse.rewrite(/_sites/${currentHost}${pathname}) } does exactly that. For our example the the new path is - _sites/arpit.example.com/about

Inside the _sites/[sites] directory for the pages that you set up, you can get the domain inside your getServerSideProps function. In normal scenerios each domain would map to a unique user in your database. You can now fetch data specific to this user using this domain value. You can do it something like this -

const getServerSideProps: GetServerSideProps = async (context) => {
  let domain = context.req.headers.host;
  if (!domain) {
    return {
      redirect: "/",
      notFound: true,
      props: {},
    };
  }
    // Make API call and fetch data for this user of this domain and render page
    // const data = await fetchUserData(domain);

  return {
    props: {
        data
    }
  };
};

Now you have the idea about how you can configure your Frontend application for domain based multi-tenant applications in NextJs. In the next part we will cover how to setup the webserver with Openresty.

Hope you liked it, See you in the next blog :)

comments powered by Disqus