> Portal Navigation:
>
> - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version.
> - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages).
> - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`).
> - Top-level index of all portals: https://dev.wix.com/docs/llms.txt
> - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt
## Resource: Tutorial | Set Up an App With the CLI
## Article: Tutorial | Set Up an App With the CLI
## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-set-up-an-app-with-the-cli.md
## Article Content:
# Tutorial | Set Up an App With the CLI
This tutorial demonstrates how to use the [Wix CLI](https://dev.wix.com/docs/wix-cli/guides/about-the-wix-cli.md) to build an app named Business Buddy. The app creates a chatbot connected to an AI that appears on a site owner’s dashboard. The AI answers questions about products in the site's store.
#### The app is built from:
- A front end consisting of two dashboard pages:
- **Products**: A page that allows the site owner to select a product from the site's store and chat with the AI about it.
- **Settings**: A page that allows the site owner to give instructions as to how the AI behaves.
- A backend consisting of the following:
- **Server**: An Express/Node.js server that responds to product chat requests by calling the OpenAI API and to settings requests by calling the mock database.
- **Database**: A mock database that holds the AI behavior settings for users.
#### The app demonstrates how to:
- Build dashboard pages using the [Wix Design System](https://www.wixdesignsystem.com/), the [Wix Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md), and CSS.
- Work with apps built by Wix such as Stores using the [Wix Javascript SDK](https://dev.wix.com/docs/sdk.md).
- Work with external services, such as [OpenAI](https://openai.com/).
- Work with CORS on a server.
- Authenticate and handle requests sent from the Wix dashboard to a server.
#### Project steps
We'll use the following steps to build the Business Buddy app:
1. Initialize the app.
1. Create the dashboard pages.
1. Run a local development server.
1. Install dependencies.
1. Design the Products page.
1. Set up calls to Wix SDKs.
1. Set up communication with the backend.
1. Design the Settings page.
1. Prepare to receive backend requests.
1. Build and deploy the app.
The end result will look like this:

## Before you begin
Before getting started, make sure that:
+ You install [Node.js](https://nodejs.org/en/) (v20.11.0 or higher).
+ You’re logged into your Wix Studio account. If you don’t already have one, [sign up for a Wix Studio account](https://manage.wix.com/account/custom-apps).
## Step 1 | Initialize the app
We use the Wix CLI to initialize our app. In the process of initializing our app, the Wix CLI automatically:
+ Creates a new app in the [Custom Apps page](https://manage.wix.com/account/custom-apps) of your Wix Studio workspace.
+ Sets up a new folder for your app in your local file system. The folder includes:
+ A **`src`** folder containing initial boilerplate code for an app with a dashboard page.
+ A **`package.json`** file containing your app dependencies.
+ Creates a local git repository for your app.
+ Prompts you to set up a development site for testing your app.
To initialize the app:
1. Open a terminal and navigate to the folder where you want to create your app.
1. Run the following command:
```bash
yarn create @wix/new app
```
If prompted, press **y** to install the package.
1. Select **Create a new Wix App**.
1. Select **Create a basic app**.
1. Enter a name for your app. Let’s name our app **Business Buddy**.
1. Back in the terminal, press **Enter** to select the default folder name (`business-buddy`) for your app in your local file system.
You now have a new app, a new folder in your local file system, and a local git repository for developing and testing your app.
## Step 2 | Create the dashboard pages
In the app, we want the dashboard pages to appear in the dashboard like this:

To create this structure, we use the CLI's `generate` command to [generate dashboard page extensions](https://dev.wix.com/docs/wix-cli/guides/extensions/dashboard-extensions/dashboard-pages/add-dashboard-page-extensions.md). This places dashboard page files in subfolders of the `dashboard/pages` folder.
### Create dashboard page files
1. Run the following command and follow the prompts to create a dashboard page extension:
```bash
yarn generate
```
2. When prompted for a page route, type **product**. The route is the path that is appended to the dashboard base URL to access the dashboard page. Learn more about [page routes](https://dev.wix.com/docs/wix-cli/guides/extensions/dashboard-extensions/dashboard-pages/add-dashboard-page-extensions.md).
3. When prompted for a page title, enter **Products**.
Upon completion, the extension files will be created in your local app files under the chosen route with the following structure:
```bash
src
└── extensions
└── dashboard
└── pages
└── product
├── product.extension.ts
└── product.tsx
```
For more information about these files, see [Dashboard Page Extension Files and Code](https://dev.wix.com/docs/wix-cli/guides/extensions/dashboard-extensions/dashboard-pages/dashboard-page-extension-files-and-code.md).
Each dashboard page is defined by a `product.extension.ts` file and a `product.tsx` file. The product.extension.ts file defines the page metadata and the `product.tsx` file defines the page UI and functionality.
Each file pair exists in a folder that determines the route to a page.
4. Repeat the above steps but create a page with route **settings**, and page name **Settings**.
## Step 3 | Run a local development server
Now that you’ve initialized the app, you can run a local development server to see the app in action, and view local changes as you develop it. The local development environment runs the app and its initial boilerplate code on the development site that you chose during app creation.
To run a local development server:
1. Run the following command using npm or yarn:
```bash
yarn dev
```
1. The CLI prompts you to choose a development site (test site), which you’ll use throughout this tutorial to run and test your app. You can choose an existing Wix site as your development site, or create a new one. Let’s **Create a new Development Site**. The newly created development site is automatically named something like **Dev Site 5x2043**, and can be found in your Wix account’s list of sites.
1. Follow the prompt to open the development site on your default browser. If the browser doesn’t open, [install your app on your test site](https://dev.wix.com/docs/build-apps/launch-your-app/app-distribution/test-your-app-on-a-premium-site.md) manually and skip the next step.
1. Click **Agree & Add** to install your app on your development site.
1. Click the **Dashboard** link in the terminal. Your development site’s dashboard opens, and you can see your newly created app’s dashboard pages in the left sidebar under **Apps**. We add the content of our app’s dashboard pages in step 5.
Your app is now running on your development site. As you develop your app, any changes made to your app’s code are reflected in real time on your development site.
If your changes don’t show up, try refreshing the page, or closing and reopening the development site.
## Step 4 | Install dependencies
Before we start coding our app, we need to install some npm packages. In your terminal, run the following commands:
```bash
yarn add @wix/design-system
yarn add @wix/wix-ui-icons-common
yarn add @wix/stores
yarn add @tanstack/react-query
```
The purpose of each of these packages will become clear as we progress.
## Step 5 | Design the Products page
In our app, we want the **Products** page to look like this:

This design is consistent with the rest of the pages in the dashboard. To create it, we’ll use the React components provided by the [Wix Design System](https://www.wixdesignsystem.com/).
The **Products** page is built from:
- 2 main React components.
- One wrapper component for initializing React context providers.
- CSS styling.
The **Products** page UI is defined in several files in the **dashboard** folder as follows:
- `/pages/products/product.tsx`: The header and the top cell that it used for selecting the product to chat about.
- `/pages/products/ProductChat.tsx`: The bottom cell that displays the selected product and the chat.
- `/pages/products/ProductChat.module.css`: CSS for the ProductChat component.
- `/withProviders.tsx`: A [`QueryClientProvider`](https://tanstack.com/query/v4/docs/framework/react/reference/QueryClientProvider) and `WixStyleReactProvider` wrapper. `QueryClientProvider` allows fetching, caching, synchronizing, and updating server state. `WixStyleReactProvider` provides styling according to Wix's design system.
Let's take a look at the code used to build the page's UI. We'll take a look at the code for the page's functionality a bit later.
### page.tsx
Notice how the code uses components from the Wix Design System to build the page. Other than that, it's a standard React component. For more information about each component, see [AutoComplete](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--autocomplete), [Card](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-card--card-content), [TableActionCell](https://www.wix-pages.com/wix-design-system/?path=/story/components-lists-table--tableactioncell), [Divider](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--divider), [Layout](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--layout), and [Page](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-page--page).
> **Note:** At this point, we're showing the code with a hardcoded, dummy product object just to demonstrate how the page will look once a product is selected. We'll add real products to the page later in the tutorial.
```tsx
import {
AutoComplete,
Card,
TableActionCell,
Divider,
Layout,
Page,
} from '@wix/design-system';
import '@wix/design-system/styles.global.css';
import React from 'react';
import { withProviders } from '../../withProviders';
import { ProductChat } from './ProductChat';
import './styles.global.css';
export default withProviders(function ProductsPage() {
const [currentProduct, setCurrentProduct] = React.useState({
name: 'Test Name',
sku: 'Test SKU',
});
const [searchQuery, setSearchQuery] = React.useState('');
return (
{currentProduct && }
);
});
```
### ProductChat.tsx
Here again, the code uses Wix Design System components to build the component UI. For more information about each component, see [Text](https://www.wix-pages.com/wix-design-system/?path=/story/components-typography-text--text), [Box](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout--box), [Card](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-card--card), [Input](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--input), and [Loader](https://www.wix-pages.com/wix-design-system/?path=/story/components-feedback--loader).
```tsx
import { Text, Box, Card, Input, Loader } from '@wix/design-system';
import { products } from '@wix/stores';
import React from 'react';
import * as Icons from '@wix/wix-ui-icons-common';
import styles from './ProductChat.module.css';
type Message = {
author: 'Business Buddy' | 'User';
text: string;
};
export function ProductChat(props: { product: products.Product }) {
const [isWaitingForBusinessBuddy, setIsWaitingForBusinessBuddy] =
React.useState(false);
const [messageDraft, setMessageDraft] = React.useState(
undefined
);
const [chatMessages, setChatMessages] = React.useState([] as Message[]);
return (
}
placeholder='Ask Business Buddy something...'
onChange={(e) => {
setMessageDraft(e.target.value);
}}
value={messageDraft}
/>
{chatMessages.map((message) => (
{message.author}: {message.text}
))}
{isWaitingForBusinessBuddy && (
)}
);
}
```
### ProductChat.module.css
We’ll use the following CSS to define the width of the input element for the ProductChat component:
```css
.userInput {
width: 100%;
}
```
#### withProviders.tsx
Finally, here is the code for the `QueryClientProvider` wrapper.
```tsx
import React from 'react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { WixStyleReactProvider } from '@wix/design-system';
export function withProviders(Component: React.ComponentType) {
return function () {
return (
);
};
}
```
## Step 6 | Set up calls to Wix React SDKs
In our app, on the **Products** page, users can choose a product from the site's Store.
To work with the Stores app on a user's site, we use the [Wix Dashboard SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md) and the [Wix SDK](https://dev.wix.com/docs/sdk/core-modules/sdk/introduction.md). We already showed how to set up your dashboard page extensions with the Dashboard SDK in [the previous section](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-set-up-an-app-with-the-cli.md). In this section, we'll use the SDK to retrieve site data to display in the app.
### Permissions
Before getting started making calls with the SDK, you need to request the proper [permissions](https://dev.wix.com/docs/build-apps/develop-your-app/access/authorization/about-permissions.md).
You can find out which permissions your app needs by checking the API reference for the functions you call. Our app queries store products, so it needs the **Read Products** permission.
You can add permissions in your [app's dashboard](https://dev.wix.com/) by selecting **Permissions** from the sidebar.
### Get products
After setting up permissions, our app can retrieve a list of products from the site's store.
In your **Products** page code add the following imports:
```tsx
import { products } from '@wix/stores';
import { useQuery } from 'react-query';
```
These imports give us access to the functionality we need for retrieving products.
Once we import everything, we'll add the following hooks to our ProductsPage component:
1. We’ll use the `products` module directly to access its `queryProducts()` function to access store products.
```tsx
const { queryProducts } = products;
```
2. Next, we use the React `useQuery()` hook to query for products whose names start with a query string. This string will be set based on the product selected in our `AutoComplete` component. We also make sure to handle any errors.
```tsx
const {
data: products,
isLoading,
error,
} = useQuery(['products', searchQuery], () =>
queryProducts({
filter: {
name: { $startsWith: searchQuery }
}
})
);
if (error) return
Something went wrong
;
```
3. Finally, let’s replace our dummy current product with the proper type now that we have it.
```tsx
const [currentProduct, setCurrentProduct] = React.useState<
products.Product | undefined
>();
```
### Populate products
Now that we have access to a list of the site’s products, we can use them to populate the `AutoComplete` component and define what happens when a user selects a product.
1. First, we set the `AutoComplete` component's `status`.
```tsx
status={isLoading ? 'loading' : undefined}
```
2. Then, we set the `AutoComplete` component's `options` to the products we got from the query by mapping them to a list of options objects with `id` and `value` properties.
```tsx
options={products?.items.map((product) => ({
id: product._id!,
value: product.name,
}))}
```
3. Next, we set up the `AutoComplete` component's `onSelect` function to set the component's current product.
```tsx
onSelect={(e) => {
setCurrentProduct(
products!.items.find(
(product) => product._id === (e.id as string)
)
);
}}
```
While we're at it, we also set the `AutoComplete` component's `onChange` function to set the search query and clear the current product.
```tsx
onChange={(e) => {
setSearchQuery(e.target.value);
setCurrentProduct(undefined);
}}
```
4. Finally, we set the `AutoComplete` component's `value` to the current product if there is one.
```tsx
value={currentProduct?.name ?? undefined}
```
## Step 7 | Set up communication with the backend
In our app, the **Products** and **Settings** dashboard pages will need to communicate with an app backend to work with the OpenAI API and to store user settings.
In general, you communicate with a backend server from a Wix App the same way you would in any other type of app.
You do, however, need to pay special attention to:
- Authenticate that requests to your server are coming from your app and not a malicious user.
- Differentiate between requests from the various instances of your app installed on different sites.
Later, we'll see how to do all that on the server. For now, let's focus on what we need to do on the front end.
### Instance ID
Each time an app is installed on a site a unique instance ID is assigned. You can [use the instance ID](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/about-app-instances.md) to authenticate requests sent from an app and to know which instance of the app is sending the request. The instance ID is added as a query param to your app’s dashboard pages.
Let's see how we add code to send requests to the server using the instance ID. In our app, we've placed this code in a file named `utils.ts` in the **dashboard** folder.
---
1. First, we create a function to get the instance ID from the page's URL.
```ts
export function getAppInstance() {
return new URLSearchParams(window.location.search).get('instance')!;
}
```
2. Next, we create a function to fetch data from the server using the instance ID to authorize the request. Notice how we call the `getAppInstance()` function described above to create the authorization header.
```ts
export async function fetchWithWixInstance(
relativePath: string,
method: string,
body?: any
) {
const res = await fetch(
`${import.meta.env.VITE_API_BASE_URL}/${relativePath}`,
{
method,
headers: {
Authorization: getAppInstance(),
...(body && { 'Content-Type': 'application/json' }),
},
body: body && JSON.stringify(body),
}
);
const json = await res.json();
return json;
}
```
`import.meta.env.VITE_API_BASE_URL` accesses the environment variable `VITE_API_BASE_URL` provided by [Vite](https://dev.wix.com/docs/wix-cli/legacy/wix-cli-for-apps/app-development/development-overview.md#vite).
So, `${import.meta.env.VITE_API_BASE_URL}/${relativePath}` dynamically constructs a full URL by combining the base URL defined in `VITE_API_BASE_URL` with the `relativePath` passed to the function.
### Fetching data
Now that we've set up the infrastructure to make secure requests to the server, we can use the `ProductChat` component to send and receive chat messages.
To add backend requests to ProductChat, we do the following:
1. First, in `ProductChat.tsx` we import the utility function described above.
```tsx
import { fetchWithWixInstance } from '../../utils';
```
2. Then, we add a function to submit messages, send them to the server, and update the state with the response. Notice how this function calls `fetchWithWixInstance()` to post a new message to the server.
```tsx
async function submitMessage() {
const newMessage: Message = {
author: 'User',
text: messageDraft ?? '',
};
setChatMessages((state) => [...state, newMessage]);
setMessageDraft('');
setIsWaitingForBusinessBuddy(true);
const { message } = await fetchWithWixInstance(`chat/product`, 'POST', {
messages: [...chatMessages, newMessage],
product: JSON.stringify(props.product, null, 2),
});
setChatMessages((state) => [
...state,
{
author: 'Business Buddy',
text: message,
},
]);
setIsWaitingForBusinessBuddy(false);
}
```
3. Finally, we call `submitMessage()` when the send icon is clicked.
```tsx
```
And we call `submitMessage()` again when enter is pressed in the `Input` component.
```tsx
onEnterPressed = { submitMessage };
```
You can see the complete code for the **Products** page in our [GitHub repo](https://github.com/wix-incubator/business-buddy-example-app/tree/master/src/dashboard/pages/products).
## Step 8 | Design the Settings page
In our app, the **Settings** page is defined in the `dashboard/pages/settings/page.tsx` as follows:
```js
import {
Button,
Card,
FormField,
InputArea,
Loader,
Page,
} from '@wix/design-system';
import '@wix/design-system/styles.global.css';
import React from 'react';
import { useMutation, useQuery } from 'react-query';
import { fetchWithWixInstance } from '../../utils';
import { withProviders } from '../../withProviders';
import { dashboard } from '@wix/dashboard';
export default withProviders(function SettingsPage() {
const { data } = useQuery<{
behaviorDirective: string;
}>('settings', async () => fetchWithWixInstance(`settings`, 'GET'));
const mutation = useMutation(
async (newBehaviorDirective: string) =>
fetchWithWixInstance(`settings`, 'POST', {
behaviorDirective: newBehaviorDirective,
})
);
const [behaviorDirective, setBehaviorDirective] = React.useState('');
return (
{
mutation.mutate(behaviorDirective);
}}
disabled={mutation.isLoading}
>
{mutation.isLoading ? : 'Save'}
}
> setBehaviorDirective(e.target.value)}
/>
);
});
```
As usual, the code uses Wix Design System components to build the component UI. For more information about each component, see [Button](https://www.wix-pages.com/wix-design-system/?path=/story/components-actions--button), [Card](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-card--card), [FormField](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--formfield), [InputArea](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--inputarea), [Loader](https://www.wix-pages.com/wix-design-system/?path=/story/components-feedback--loader), and [Page](https://www.wix-pages.com/wix-design-system/?path=/story/components-layout-page--page).
### react-query
In this code, we import `useMutation` and `useQuery` from `react-query`:
- **`useQuery`** is used to fetch data from the server and handle caching. In the code below, `useQuery` fetches the current `behaviorDirective` from the server and stores it in data.
```js
const { data } = useQuery<{
behaviorDirective: string;
}>('settings', async () => fetchWithWixInstance('settings', 'GET'));
```
- **`useMutation`** is used to update data on the server. In the code below, `useMutation` sends a new `behaviorDirective` to the server via a POST request:
```js
const mutation = useMutation(
async (newBehaviorDirective: string) =>
fetchWithWixInstance(`settings`, 'POST', {
behaviorDirective: newBehaviorDirective,
})
);
```
### Page component
This component renders a page where we can configure a behavior directive. It includes the following elements:
- **Page Header**: Displays the title "Behavior Settings".
- **Card Component**: Encapsulates the settings form.
- **Card Header**: Displays the title "Behavior Directive" and a "Save" button.
- **Save Button**: When clicked, `useMutation` sends the new directive to the server by calling `mutation.mutate`. It shows a loader instead while the mutation is in progress.
- **Card Subheader**: Adds the subtitle "Give Business Buddy directives on how to answer your questions".
- **Card Content**: Contains a form field with the label "Directive".
- **InputArea**: A text area where users can input the behavior directive. It has a placeholder text, a character counter, and is resizable. The input value is managed by the `behaviorDirective` state.
### Adding a toast
When a user changes the behavior setting on the **Settings** page, we want to show a toast at the top of the dashboard page.
To show toasts, update dashboard components based on state changes, and open and close dashboard modals, we use the [Wix Dashboard React SDK](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md).
1. First, we need to import the [`dashboard`](https://dev.wix.com/docs/sdk/host-modules/dashboard/introduction.md) module from the Dashboard SDK.
```tsx
import { dashboard } from '@wix/dashboard';
```
1. Finally, we call the `showToast()` function of the `dashboard` module and provide a message and type. In our app, we want to show the toast when a behavior setting has been successfully changed, so we call `showToast()` in the `onSuccess` of the mutation used by the **Save** button.
```tsx
{
onSuccess: () => {
dashboard.showToast({
message: "Changes saved!",
type: "success",
});
},
}
```
## Step 9 | Prepare to receive backend requests
In our app, the backend server must receive requests for chat messages, and for storing and retrieving settings values. Each of these operations must be performed by calling an endpoint exposed on the server.
> In this implementation, we use a simple [Express](https://expressjs.com/) server. You're free to use any type of server for your app's backend.
When an app server receives a request to an endpoint, it needs to:
- Authenticate that the request is from your app and not a malicious user.
- Know which instance of your app has sent the request.
You'll also need to set up [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) to make sure your server allows the requests from your app.
### CORS
To set up CORS properly, we need to add headers that allow access from certain origins and with certain HTTP methods.
In our case, we whitelist **localhost** for testing purposes and the URL for our app frontend on the Wix App CDN.
The URL of an app on the CDN is in the following format:
```
https://.wix.run
```
You can find your app's App ID on your [app's dashboard](https://dev.wix.com/).
This is how we whitelist the URLs on our server using Express:
```ts
app.use(
cors({
origin(requestOrigin, callback) {
const whitelist = [
'http://localhost',
'https://1522de8b-ea83-423b-9da0-166fdc7ef372.wix.run',
];
if (
requestOrigin &&
whitelist.some((whitelistOrigin) =>
requestOrigin.includes(whitelistOrigin)
)
) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
})
);
```
### Authentication
Now that we've dealt with CORS issues, the next thing to do is to authenticate the requests coming into our server.
To authenticate requests, we first need our app's secret key from your app's dashboard. We get the key from the [**OAuth** page](invalid-url) in our app's dashboard. Then we add the key to an environment variable named `WIX_APP_SECRET`.
Now that we have the key, we can write code to authorize the request. In our case, we write this code in a file on our server named `utils.ts`.
To add authentication logic, we do the following:
1. First, we create the `authorizeWixRequest()` function. This function reads the authorization header sent from our app frontend and checks it using the `parseInstance()` function described below.
```ts
export function authorizeWixRequest(req: express.Request) {
const authorization = req.headers.authorization;
if (!authorization) throw new Error('No authorization header');
const instance = parseInstance(
authorization,
process.env.WIX_APP_SECRET as string
);
if (!instance) throw new Error('Invalid instance');
return instance;
}
```
2. Next, we create the `parseInstance()` function to parse the received app instance and check it against our app's secret key using the `validateInstance()` function described below.
```ts
export function parseInstance(
instance: string,
appSecret: string
): {
instanceId: string;
appDefId: string;
signDate: string;
uid: string;
permissions: 'OWNER';
demoMode: boolean;
siteOwnerId: string;
siteMemberId: string;
expirationDate: string;
loginAccountId: string;
pai: null;
lpai: null;
} | null {
var parts = instance.split('.'),
hash = parts[0],
payload = parts[1];
if (!payload) {
return null;
}
if (!validateInstance(hash, payload, appSecret)) {
console.log('Provided instance is invalid: ' + instance);
return null;
}
return JSON.parse(base64Decode(payload, 'utf8'));
}
```
3. Then we create the `validateInstance()` function to authenticate the instance against our app secret.
```ts
function validateInstance(hash: string, payload: string, secret: string) {
if (!hash) {
return false;
}
hash = base64Decode(hash);
var signedHash = createHmac('sha256', secret)
.update(payload)
.digest('base64');
return hash === signedHash;
}
```
4. To use this authentication code, we call the `authorizeWixRequest()` function and pass it an incoming request. For example, when we receive a request for a product chat we authorize the request before continuing.
```ts
app.post("/chat/product", async function (req, res) {
const { instanceId } = authorizeWixRequest(req);
// continue to process request...
}
```
### Multiple instances
In our app, the backend server must store settings data for each app instance. We use the instance ID to associate a particular setting value with a particular instance of our app. We can do this by storing the instance ID alongside each setting value in a simple key-value store.
The `authorizeWixRequest()` function we created in the previous section returns the parsed instance information, including an `instanceId`. We can pass that information to the key-value store when we process a request, such as a `/settings` POST, as you can see here.
```tsx
app.post('/settings', async function (req, res) {
const { instanceId } = authorizeWixRequest(req);
const behaviorDirective = req.body.behaviorDirective;
saveBehaviorDirective(instanceId, behaviorDirective);
res.sendStatus(200);
});
```
Then, in the key-value store, we can store the value of the `instanceId` as the key and the setting text as the value.
```tsx
let behaviorDirectives: { [instanceId: string]: string } = {};
export function saveBehaviorDirective(instanceId: string, directive: string) {
behaviorDirectives[instanceId] = directive;
}
```
## Step 10 | Build and deploy the app
After testing your app and seeing that it works as expected, you can create a public preview of your app, build your app, create and manage app versions, and deploy your app. For more information on these tasks, see [Build and Deploy an App with the CLI](https://dev.wix.com/docs/wix-cli/guides/development/build-and-deploy-a-project.md)
## Summary
After completing this tutorial, you have created a dashboard AI chatbot that integrates with a custom backend and Wix site data.
At this point, you have all you need to know to develop an app. Feel free to play around with the example app some more, or get started on writing your own app.