-
Notifications
You must be signed in to change notification settings - Fork 241
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Node.js multitenancy guide (#2704)
--------- Co-authored-by: gui machiavelli <gui@meilisearch.com> Co-authored-by: Amélie <alallema@users.noreply.github.com> Co-authored-by: Guillaume Mourier <guillaume@meilisearch.com> Co-authored-by: Mehmet Fatih Pazarbaşı <88274580+greendesertsnow@users.noreply.github.com> Co-authored-by: Maria Craig <marycraig90@gmail.com> Co-authored-by: tgoeg <39340276+tgoeg@users.noreply.github.com> Co-authored-by: CaroFG <48251481+CaroFG@users.noreply.github.com>
- Loading branch information
1 parent
3dec58c
commit fd24bad
Showing
2 changed files
with
212 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
--- | ||
title: Node.js multitenancy guide — Meilisearch documentation | ||
description: Learn how to implement secure, multitenant search in your Node.js applications. | ||
sidebarDepth: 3 | ||
--- | ||
|
||
# Node.js multitenancy guide | ||
|
||
This guide will walk you through implementing search in a multitenant Node.js application handling sensitive medical data. | ||
|
||
## What is multitenancy? | ||
|
||
In Meilisearch, you might have one index containing data belonging to many distinct tenants. In such cases, your tenants must only be able to search through their own documents. You can implement this using [tenant tokens](/learn/security/tenant_tokens). | ||
|
||
## Requirements | ||
|
||
- [Node.js](https://nodejs.org/en) and a package manager like `npm`, `yarn`, or `pnpm` | ||
- [Meilisearch JavaScript SDK](/learn/what_is_meilisearch/sdks) | ||
- A Meilisearch server running — see our [quick start](/learn/getting_started/cloud_quick_start) | ||
- A search API key — available in your Meilisearch dashboard | ||
- A search API key UID — retrieve it using the [keys endpoints](/reference/api/keys) | ||
|
||
<Capsule intent="tip"> | ||
Prefer self-hosting? Read our [installation guide](/learn/getting_started/installation). | ||
</Capsule> | ||
|
||
## Data models | ||
|
||
This guide uses a simple data model to represent medical appointments. The documents in the Meilisearch index will look like this: | ||
|
||
```json | ||
[ | ||
{ | ||
"id": 1, | ||
"patient": "John", | ||
"details": "I think I caught a cold. Can you help me?", | ||
"status": "pending" | ||
}, | ||
{ | ||
"id": 2, | ||
"patient": "Zia", | ||
"details": "I'm suffering from fever. I need an appointment ASAP.", | ||
"status": "pending" | ||
}, | ||
{ | ||
"id": 3, | ||
"patient": "Kevin", | ||
"details": "Some confidential information Kevin has shared.", | ||
"status": "confirmed" | ||
} | ||
] | ||
``` | ||
|
||
For the purpose of this guide, we assume documents are stored in an `appointments` index. | ||
|
||
## Creating a tenant token | ||
|
||
The first step is generating a tenant token that will allow a given patient to search only for their appointments. To achieve this, you must first create a tenant token that filters results based on the patient's ID. | ||
|
||
Create a `search.js` file and use the following code to generate a tenant token: | ||
|
||
```js | ||
// search.js | ||
|
||
import { Meilisearch } from 'meilisearch' | ||
|
||
const apiKey = 'YOUR_SEARCH_API_KEY' | ||
const apiKeyUid = 'YOUR_SEARCH_API_KEY_UID' | ||
const indexName = 'appointments' | ||
|
||
const client = new Meilisearch({ | ||
host: 'https://edge.meilisearch.com', // Your Meilisearch host | ||
apiKey: apiKey | ||
}) | ||
|
||
export function createTenantToken(patientName) { | ||
const searchRules = { | ||
[indexName]: { | ||
'filter': `patient = ${patientName}` | ||
} | ||
} | ||
|
||
const tenantToken = client.generateTenantToken( | ||
apiKeyUid, | ||
searchRules, | ||
{ | ||
expiresAt: new Date('2030-01-01'), // Choose an expiration date | ||
apiKey: apiKey, | ||
} | ||
) | ||
return tenantToken | ||
} | ||
``` | ||
|
||
When Meilisearch gets a search query with a tenant token, it decodes it and applies the search rules to the search request. In this example, the results are filtered by the `patient` field. This means that a patient can only search for their own appointments. | ||
|
||
## Using the tenant token | ||
|
||
Now that you have a tenant token, use it to perform searches. To achieve this, you will need to: | ||
- On the server: create an endpoint to send the token to your front-end | ||
- On the client: retrieve the token and use it to perform searches | ||
|
||
### Serving the tenant token | ||
|
||
This guide uses [Express.js](https://expressjs.com/en/starter/installing.html) to create the server. You can install `express` by running: | ||
|
||
```sh | ||
# with NPM | ||
npm i express | ||
# with Yarn | ||
yarn add express | ||
# with pnpm | ||
pnpm add express | ||
``` | ||
|
||
Then, add the following code in a `server.js` file: | ||
|
||
```js | ||
// server.js | ||
|
||
import express from 'express' | ||
import { createTenantToken } from './search.js' | ||
|
||
const server = express() | ||
|
||
server.get('/token', async (request, response) => { | ||
const { id: patientId } = request.query | ||
const token = createTenantToken(patientId) | ||
return response.json({ token }); | ||
}) | ||
|
||
server.listen(3000, () => { | ||
console.log('Server is running on port 3000') | ||
}) | ||
``` | ||
|
||
This code creates an endpoint at `http://localhost:3000/token` that accepts an `id` query parameter and returns a tenant token. | ||
|
||
### Making a search | ||
|
||
Now that we have an endpoint, you will use it to retrieve the tenant token in your front-end application. This guide uses [InstantSearch.js](/learn/front_end/front_end_integration) to create a search interface. You will use CDN links to include InstantSearch.js and the Meilisearch InstantSearch.js connector in your HTML file. | ||
|
||
Create `client.html` file and insert this code: | ||
|
||
```html | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/templates/basic_search.css" /> | ||
</head> | ||
<body> | ||
<div class="wrapper"> | ||
<div id="searchbox" focus></div> | ||
<div id="hits"></div> | ||
</div> | ||
</body> | ||
<script src="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/dist/instant-meilisearch.umd.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4"></script> | ||
<script> | ||
document.addEventListener('DOMContentLoaded', async () => { | ||
const patientId = 1 // Replace with the patient's ID | ||
const response = await fetch(`http://localhost:3000/token?id=${patientId}`) | ||
const { token } = await response.json() | ||
const search = instantsearch({ | ||
indexName: 'appointments', | ||
searchClient: instantMeiliSearch( | ||
'https://edge.meilisearch.com', | ||
token | ||
).searchClient | ||
}) | ||
search.addWidgets([ | ||
instantsearch.widgets.searchBox({ | ||
container: "#searchbox" | ||
}), | ||
instantsearch.widgets.hits({ | ||
container: "#hits", | ||
templates: { | ||
item: ` | ||
<div> | ||
<div class="hit-name"> | ||
{{#helpers.highlight}}{ "attribute": "patient" }{{/helpers.highlight}} | ||
</div> | ||
<div class="hit-description"> | ||
{{#helpers.highlight}}{ "attribute": "details" }{{/helpers.highlight}} | ||
</div> | ||
</div> | ||
` | ||
} | ||
}) | ||
]) | ||
search.start() | ||
}) | ||
</script> | ||
</html> | ||
``` | ||
|
||
Ta-da! You have successfully implemented a secure, multitenant search in your Node.js application. Users will only be able to search for documents that belong to them. | ||
|
||
## Conclusion | ||
|
||
In this guide, you saw how to implement secure, multitenant search in a Node.js application. You then created an endpoint to generate tenant tokens for each user. You also built a search interface with InstantSearch to make searches using the tenant token. | ||
|
||
All the code in this guide is a taken from our [multitenacy example](https://tenant-token.meilisearch.com/?utm_campaign=oss&utm_source=docs&utm_medium=node-multitenancy) application. The code is available on [GitHub](https://github.com/meilisearch/tutorials/tree/main/src/tenant-token-tutorial). |