Today we're diving into a complex, yet essential, topic: utilizing Meilisearch in a multi-tenant application. By the end of this guide, advanced and intermediate developers will have a clear understanding of how to configure Meilisearch to manage multi-tenancy effectively.

Multi-tenancy: what is it?

In a multi-tenant architecture, a single instance of a software application serves multiple customers or "tenants". Each tenant's data is isolated and remains invisible to other tenants.

Multi-tenancy use cases

A typical use case for multi-tenancy is in Software as a Service (SaaS) applications, where multiple companies share the same database. Each company becomes a tenant with data that should not be accessible by others. In such scenarios, you're likely already implementing this specificity in your primary data store.

Multi-tenancy with Meilisearch: the ultimate approach

While it's possible to create one index per company with Meilisearch, it's not recommended due to performance reasons. Meilisearch processes tasks one index after another and cannot maximize the indexation throughput, as cannot process tasks on different indexes in parallel.

Instead, the best way is to put all tenants in the same index and use Meilisearch's powerful tenant tokens to restrict access to a specific user's scope only. For this, stored documents should have not only an ID but also a company identifier to separate users by their company.

๐Ÿ’ก
Experience multi-tenancy in action through our demos:
- SaaS demo
- Tenant tokens demo

Preparing Meilisearch for multi-tenancy

Let's illustrate this with an example akin to a Slack application. In this application, users belong to a company, and each user can create text messages in different threads. Our goal is to create an index containing all messages but allow only people from a specific company to access their company's messages. Subsequently, we might want to perform a search applying filters on users and threads.

Database diagram showing the relationships between companies, users, and messages.

First, we need to transform our data to make it optimal for indexing by Meilisearch. Here's what a sample document could look like:

{
	"id": 48735873,
	"user":{
		"id": 5749,
		"name": "bob"
	},
	"company": {
		"id": 838,
		"name": "Acme Inc"
	},
	"thread": "meilisearch-is-awesome",
	"data": "Meilisearch is so easy to use! ๐Ÿ”ฅ",
	"created_at": 1543230000
}
๐Ÿ“–
Learn how to update your settings in the documentation.

Next, you'll need to create a messages index and add the following settings:

{
  "displayedAttributes": [
    "id",
		"user.name",
		"thread",
		"data",
		"created_at"
  ],
  "searchableAttributes": [
    "data"
  ],
  "filterableAttributes": [
		"user.name",
		"company.id",
		"thread"
	],
  "sortableAttributes": [
		"created_at"
	]
}

Here's what each setting does:

  • displayedAttributes: These are the attributes that will appear in your application's search results. We're omitting user.id and company.id for security reasons, and company.name isn't necessary as the user is already aware of their company.
  • searchableAttributes: These are the attributes that the search function will use. In our case, only the data (or message text) is searchable.
  • filterableAttributes: These are the attributes that you can filter by. We've included company.id for the tenant tokens, user.name, and thread for on-the-fly filtering.
  • sortableAttributes: These are the attributes you can sort by. We've only included created_at as you might want to sort messages chronologically.

Creating tenant tokens

With your Meilisearch configuration in place, the next step is to create a new tenant token for each company. If you don't have particular security concerns, you can omit the expiration date to simplify your onboarding process.

The crucial part of the tenant tokens is the searchRules. In our example, you'll add the following rule:

{
  "messages": {
    "filter": "company.id = 838"
  }
}

With the generated token, your search will restrict access based on company.id, so users will only see messages within their company.

Store this token in your primary data store (Postgres, MySQL, etc.). This mechanism is similar to those used in tools like Stripe.

Each time a user logs into your app, load this key into local storage and use it for the searches, ensuring that each user only has access to data relevant to their company.

Conclusion

Creating a multi-tenant application using Meilisearch is quite straightforward once you understand how to structure your data and configure your search attributes correctly. By following this guide, you can ensure that your application provides a secure, efficient, and user-friendly search experience. If you have any question, you can join us on Discord.

Boost productivity and streamline development with Meilisearch Cloud. No more server deployment or manual updates. Try it free for 14 days, no credit card needed.

For more things Meilisearch, subscribe to our newsletter. You can learn more about our product by checking out our roadmap and participating in our product discussions.

Happy coding!