> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Secured API keys in Vue InstantSearch

> Use secured API keys in your Vue InstantSearch app to keep your data safe.

export const Index = () => <Tooltip tip="An Algolia index is a searchable dataset that consists of records and configuration settings. These settings define how the records are searched and ranked.">
    index
  </Tooltip>;

## Search-only API keys in web apps

You can use the [search-only API key](/doc/guides/security/api-keys/#search-only-api-key) in your frontend code.
This key is safe to expose because it only allows search operations.

If you need additional restrictions,
such as limiting the access to specific indices,
or applying rate limits,
generate a **secured API key** on your backend.

## Secured API keys

Secured API keys add restrictions and search parameters that your users can't change.
You must generate them on your server, or anyone can remove the restrictions again.

This guide shows how to use secured API keys in an InstantSearch app.
For mode details, see [User-restricted access to data](/doc/guides/security/api-keys/how-to/user-restricted-access-to-data/).

The aim is to build an app that generates a secured API key with restrictions on the server,
and then uses it on the client.

See the full example on
[GitHub](https://github.com/algolia/doc-code-samples/tree/master/vue-instantsearch/secured-api-keys).

### Server

#### Generate a secured API key

Generate the secured API key on your server.
This example uses [Express](https://expressjs.com), but the same approach works with any server.

To generate a key, you need the Algolia JavaScript client and a parent API key with the
[search ACL](/doc/guides/security/api-keys/#access-control-list-acl).

In this example, the generated key only allows searching the `demo_ecommerce` index.
You can also apply
[additional restrictions](/doc/guides/security/api-keys/in-depth/api-key-restrictions/),
including
[search parameters](/doc/guides/security/api-keys/in-depth/api-key-restrictions/#query-parameter-restrictions).

```js JavaScript icon=code theme={"system"}
const algoliasearch = require("algoliasearch");

const client = algoliasearch("ALGOLIA_APPLICATION_ID", "ALGOLIA_SEARCH_API_KEY");
const securedApiKey = client.generateSecuredApiKey({
  parentApiKey: "ALGOLIA_SEARCH_API_KEY",
  restrictions: {
    restrictIndices: "demo_ecommerce",
  },
});
```

Send the secured API key to the client in one of these ways:

* Return it from an endpoint, then fetch it before you create your InstantSearch app.
* Inline it in the HTML, then read it from a global object in the client.

### Inline the API key

If your build tool generates the HTML, add a placeholder value and replace it on the server before sending the page.

<Note>
  If you control the HTML template, inline the value directly.
</Note>

```html HTML icon=code-xml theme={"system"}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script>
      window.SERVER_DATA = __SERVER_DATA__;
    </script>
  </head>
</html>
```

Replace the placeholder with the generated API key:

```js JavaScript icon=code theme={"system"}
const path = require("path");
const fs = require("fs");
const util = require("util");
const express = require("express");

const app = express();
const readFileAsync = util.promisify(fs.readFile);

app.get("/", async (_, res) => {
  const index = await readFileAsync(
    path.join(__dirname, "dist", "index.html"),
    "utf-8",
  );

  const indexWithServerData = index.replace(
    "__SERVER_DATA__",
    JSON.stringify({
      ALGOLIA_API_KEY: securedApiKey,
    }),
  );

  res.send(indexWithServerData);
});
```

Start the server and read the key from `window.SERVER_DATA` in the client.
The next section explains how to use this key in an InstantSearch app.

### Client

#### Retrieve the API key

Now that you have the API key on the global object, you can retrieve it from the client code and inject it into [`searchClient`](/doc/api-reference/widgets/instantsearch/vue). Make sure you clean up the global object. Otherwise, this value will stay in memory.

```html Vue theme={"system"}
<template>
  <ais-instant-search
    :search-client="searchClient"
    index-name="demo_ecommerce"
  >
    <!-- InstantSearch widgets -->
  </ais-instant-search>
</template>

<script>
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import 'instantsearch.css/themes/satellite-min.css';

export default {
  data() {
    return {
      searchClient: algoliasearch(
        'ALGOLIA_APPLICATION_ID',
        window.SERVER_DATA.ALGOLIA_API_KEY
      ),
    };
  },
};
</script>
```

That's it for the client.
Your app can now only target the `demo_ecommerce` index.
You can try to target a different one like `demo_media` but the API will return an error.

* [Demo](https://codesandbox.io/s/github/algolia/doc-code-samples/tree/master/vue-instantsearch/secured-api-keys)
* [Source code](https://github.com/algolia/doc-code-samples/tree/master/vue-instantsearch/secured-api-keys)

<Note>
  This code has been specifically created for Vue 2. Some [modifications](https://v3-migration.vuejs.org/) may be required for it to work correctly in Vue 3.
</Note>

## Prevent cross-site scripting vulnerabilities

Algolia's [highlighting feature](/doc/guides/building-search-ui/ui-and-ux-patterns/highlighting-snippeting/vue) helps users understand how their query matches the results.
It's a key visual cue.

Algolia's [highlighting feature](/doc/guides/building-search-ui/ui-and-ux-patterns/highlighting-snippeting/vue) helps users understand how their query matches the results.
It's a key visual cue.

Algolia wraps matching words in HTML tags.
By default, it uses tags like [`em`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em), which the browser renders as HTML.
If your site includes user-generated content, this can expose your app to cross-site scripting (XSS) attacks.
The same risk applies to the snippeting feature.

To prevent this, InstantSearch restricts highlighting to a predefined set of HTML tags and escapes all others.
If you set a custom tag in the Algolia dashboard,
it's overridden and replaced by [`mark`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark).

To use a different tag, such as [`strong`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong),
set the `highlightedTagName` props in [`ais-highlight`](/doc/api-reference/widgets/highlight/vue) or [`ais-snippet`](/doc/api-reference/widgets/snippet/vue).

```html Vue theme={"system"}
<template>
  <ais-instant-search
    index-name="instant_search"
    :search-client="searchClient"
  >
    <ais-hits>
      <template v-slot:item="{ item }">
        <p>
          <ais-highlight attribute="name" :hit="item" />
          <ais-snippet attribute="description" :hit="item" />
        </p>
      </template>
    </ais-hits>
  </ais-instant-search>
</template>
```

To turn this safety feature off,
use `:escape-hits="false"` on the [`ais-hits`](/doc/api-reference/widgets/hits/vue) component, then the HTML will no longer be escaped.
