# Create a bookmark functionality with remote actions and device ID with Xano

In this tutorial, we'll show you how you can allow app users to create bookmarks with a list of items, using [Xano](https://www.xano.com/) as the backend.

We're using the app case created in the [World Cities with Xano tutorial](/connect-api/browse-by-backend/xano/xano-build-nested-lists.md). So if you want to build this app, **you should follow that tutorial first**. If you are interested in building a bookmark functionality for your app, but not in building this particular app case, you can still continue reading and adapt your app case to the following steps.

The bookmark functionality will allow an app user to bookmark some cities among the ones displayed in the app. This will be achieved by implementing a [remote action](/data-binding/data-binding-types/data-binding-add-remote-actions-to-your-ui.md), bound to a "bookmark" icon next to each city. This remote action will trigger a PATCH request, containing the [device ID](/connect-api/request-url-variables.md), which will be stored in the database, in the row that corresponds to the city the user bookmarked. Finally, we'll have a screen where the user can see the list of cities that were previously bookmarked.

![](/files/-Mj3o_fs0MCODRWRcG7A)

## 🎨 Design file

{% embed url="<https://www.figma.com/file/cQm4zLlJOHps6NSP8TEBFE?node-id=2:12>" %}

## 🚧 Create the backend functionality for bookmarks in Xano

In order to build the World Cities app backend, follow the steps [here](/connect-api/browse-by-backend/xano/xano-build-nested-lists.md#creating-the-backend-with-xano) to create the database and first API endpoints. After following those steps, you'll have a database with information about several cities, together with the world region they belong to. Also, you'll have some endpoints to get information about those cities and regions. Bravo will connect to those endpoints to feed the mobile app with the necessary data.

### Adding bookmarks column to the database

Now, we'll need to add an extra column to the `Cities` table shown in the tutorial. This column, called Bookmarks, will store the device ID of all the users that choose to bookmark a particular city.

![](/files/-Mj3qIDdTYDBFTPoeEHV)

1. &#x20;Go to the `Cities` table on Xano, and click on the `+` sign to add a new column.
2. In the field types step, choose `Text` and call it "**bookmarks**".
3. Choose `list` in the **Structure** dropdown, and make sure **Required** has the `no` value.
4. Hit `Save` to add this field to the table.

### Creating the API endpoints and data processing functionality

Now that the table is ready to store the device ID of the users that bookmarked a particular city, we'll create two endpoints.

One of them will return the cities that have been bookmarked by a particular device, and the 2nd one will accept data, in form of a `PATCH` request, containing a new device ID for one of the cities. This second endpoint will be the one targeted by the remote action we'll later add in Bravo.

#### **`GET`** **Cities by device** <a href="#block-73b80be75499424f8607cb62efd79fed" id="block-73b80be75499424f8607cb62efd79fed"></a>

First, we'll create the endpoint that will return a list of cities containing a particular device ID in the **bookmarks** field. We'll use this data later for the screen containing the list of cities a user bookmarked. This endpoint will accept a `GET` request, with the device ID passed as a query string parameter (we'll show later how the URL should be constructed).

&#x20;   1\. In the World Cities API group, click on `Add API endpoint` on the top right of the dashboard.

![](/files/-Mj3tbYohI8fCNhOsMWr)

&#x20;   2\. Select `CRUD Database Operations`, then the `Cities` table

&#x20;   3\. Select the `GET request`, and put **citiesByDevice** as the name.

&#x20;   4\. Hit `Save`. This will automatically create an endpoint that will accept the GET request.

Now, click on the endpoint just created. We'll add an input variable, which will be the device ID received by the endpoint as a query string parameter.

&#x20;   1\. In the `1. Inputs` section, we'll click on the `+` sign at the top right. Select `Text`.

&#x20;   2\. Put **deviceId** as the input name, and specify `Yes` in the `Required` dropdown.

&#x20;   3\. Leave the rest as is, and click `Save`.

Now, we'll use this input value to filter the data in the `Cities` table and return only the cities that contain the device ID provided by the user.

&#x20;   1\. Click on the `Query all Records` item

![](/files/-Mj3uCfPalPBLGaAMPk2)

&#x20;   2\. In the `Filter` tab, add a filter by **custom query** by clicking on the pencil icon, and set the parameters as shown in the image below.

<figure><img src="/files/83ZOfsp6YAWjtxrttiCZ" alt=""><figcaption></figcaption></figure>

&#x20;   3\. After doing this, click `Save`. Now, our **citiesByDevice** endpoint is ready!

You can test it by clicking on `Run & Debug` at the top of the dashboard. If you introduce a `deviceId` parameter already present in the **bookmarks** field of any of the cities, the data for these cities will be returned in the API response. If none of the cities contains this device ID, the response will be empty.

#### **`PATCH`** **Cities** <a href="#block-ac256f1d807a488dbd77b1e765035489" id="block-ac256f1d807a488dbd77b1e765035489"></a>

Now, we'll create a PATCH endpoint that will receive a new device ID for a specific city, every time a user bookmarks a city. This endpoint will be a bit more complex than the previous one, as several steps need to be completed to add the device ID information.

As we did for the previous endpoint:

1. We'll click on `Add API endpoint` > `CRUD Database Operations` > select the `Cities` table
2. Select the `EDIT` operation > change the verb to `PATCH`

Now, we'll click on the endpoint we just created, and access the endpoint dashboard. We'll have two inputs in our endpoint:

1. One integer called `cities_id` (already created for us)
2. One text called `device_id` (we'll need to add this one, setting it as `required`).

These values will be sent in the API request: the **cities\_id** as part of the URL, and the **device\_id** inside the JSON body of the request.

**We'll hide all the other inputs**, by clicking on them and disabling the `Visible to the API` toggle switch.

![](/files/-Mj3uYVSuP-GVcWeLj9n)

Now comes the most complex part: adding steps to the `Function stack` to be able to add the device ID to the **bookmarks** field of the corresponding city. Remember that this field is a list of text elements, which correspond to the device IDs.

We'll add the following functions to the stack:

![](/files/-Mj3ub64ssXZvPGAuGBg)

1️⃣ **Get Record from Cities**

1. Click on the `+` icon on the top right to add a stack item.
2. Click on `Database Requests`, select `Get Record` (we only want to retrieve a single row from the table), and choose `Cities`.
3. You'll see the **Get Record** operation has been added to the stack. Click on it, as we need to specify that the record we want to retrieve is the one having the `cities_id` input as ID. For that, configure the `Inputs` panel as shown below.

![](/files/-Mj3ulxfFJ5jvAL9pdGU)

1. Also, we need to make some changes to the `Output` panel. We are only interested in returning the **bookmarks** list from the table item, as we'll later add to this array the device ID input, and store it again in the table. `Customize` the response as shown below.
2. Change the **Variable** name to `bookmarks_array`. (remember to click `Save`).

![](/files/-Mj3ur1bPlwXndaA-f2O)

2️⃣ **Create Variable**

In the previous step, we got the bookmarks list from the table item we selected (using the input `cities_id` provided in the PATCH request). This was stored in a variable called `bookmarks_array` (see screenshot above). This variable is an object containing the **bookmarks** list.

To add the **device ID** to that list, we want to select that list inside of the object, so we're able to add a new item to the list.

1. We'll create a new variable by clicking on the blue `+` icon on the top right of the `Function stack`, then choosing `Data Manipulation`, and then `Create Variable`.
2. Click on the variable we just created. On the right panel, input `bookmarks_array.bookmarks` as the **value** of the variable, and `bookmarks` as the **name** (see screenshot below). Hit `Save`.

![](/files/-Mj3uwg0ACfk_PcNb5xi)

3️⃣ **Array: Has Any Element**

Now we have the bookmark list in the form of an array. We can perform operations with this array, such as searching elements, or adding new ones.

We'll first add a new item to the function stack to perform a search over the array. We want to see if the **device\_id** input is already stored in the array, which would mean that the user had already bookmarked that city.

1. Add a new `Data Manipulation` operation, then select `Arrays`, and finally `Has Any Element`.
2. Here, specify `bookmarks` in the array dropdown (the name we defined in step 2️⃣ for the list).
3. Add an expression by clicking on the pencil icon. Select the `=` operator, setting `device_id` to the left of it, and `$this` to the right. This will check whether any of the array elements is equal to the **device\_id** variable, and return a boolean variable: `true` if an element matches the **device\_id**, `false` if not.

![](/files/-Mj3v1iCs38ED0ScZH_K)

1. Click `Save` after specifying this expression, set `in_array` as the variable **name** (this will be the boolean variable mentioned above), and click `Save` again to close the panel.

![](/files/-Mj3v5m2So1jH8qo3cht)

**4️⃣ Conditional statement**

Finally, we'll add a conditional statement, as we want to add the device ID to the bookmark list in case that ID is not in the list, but we don't want to add it in case it already is, to avoid having duplicated IDs in the list.

1. For that, we'll add a new function to the stack, selecting `Data Manipulation`, and then `Conditional`.
2. Click on the pencil icon and set the statement below. Click `Save`.

![](/files/-Mj3vB08K40EhcYF0sGd)

We'll see how the conditional will have two paths: `Then` and `Else`. The first path will be taken if the `in_array` variable is false (which means the **device\_id** has not been found in the **bookmarks** list). If this is the case, we'll want to add the new **device\_id** to the bookmarks list, and update the table record. Otherwise, the `Else` path will be taken. In this case, as the device ID is already present in the bookmarks list, we won't perform any further operations, else than returning an empty body in the HTTP response.

**4️⃣.1️⃣** **`Then`** **path**

1. Under `Then`, add a new function: `Data Manipulation` → `Arrays` → `Add To End of Array`
2. Set `bookmarks` as **existing variable** (we defined this one in step 2️⃣)
3. Set `device_id` as **value**. This will add the **device\_id** to the end of the **bookmarks** array.

Finally, we want to update the `bookmarks` field in the table with the **bookmarks** array we created (containing the newly added **device\_id**), for the row with `cities_id` as ID.

1. Under the `Array: Add to end` function we just created, add a new function: `Database Requests` → `Edit Record` → `Cities`
2. In the `Inputs` tab, on `Find Cities record by field`, set `id` (name of the table field) next to **field\_name**, and `cities_id` (input value received with the HTTP request) next to **field\_value**. As you can imagine, this will specify which record of the table will be edited.

![](/files/-Mj3vufXdsRLws2E7Q76)

1. Under **Cities Metadata**, set the `bookmarks[]` field of the table equal to the `bookmarks` list variable containing the new **device\_id** item (see screenshot below). **Make sure all the other items are invisible.** This will prevent errors later on.

![](/files/-Mj3vz2Lf_eDlU__eljf)

1. Now we'll go to the `Output` tab to decide what will be the content of the HTTP response returned after calling this endpoint. We chose to return **all** the fields of the updated table record. We set the name `result` to this variable (it will be needed later).

![](/files/-Mj3w2w9-aCdj0hp42XQ)

4️⃣.2️⃣ **`Else`** **path**

We still need to specify what will happen if the conditional defined in step 4️⃣ takes the `else` path. In this case, as mentioned before, we'll just do "nothing", and return an empty body that will be sent in the HTTP response.

1. Add a new function to the stack, under `Else`: `Data Manipulation` → `Create Variable`.
2. Set the variable name to `result`. It needs to have the same name as the variable returned when editing the table record in the previous step. Finally, set its value to `null`.

![](/files/-Mj3xDPTvPozehp4jTsJ)

Finally, under **3. Response** section in the main endpoint dashboard, we need to specify what will be returned as the HTTP response. We need to bind the `result` variable defined both in 4️⃣.1️⃣ and 4️⃣.2️⃣. Click on the item inside the response area, and set it to the `result` variable as shown below.

![](/files/-Mj3xGlp345R90jO9fN-)

Now we're done with the endpoint! Feel free to test it before jumping into Bravo to connect the app design to the Xano endpoints we just created.

## ⚙️ Creating the API requests in Bravo Data Library

Now that we've created the endpoints, it's time to create the API requests that will target them. At this point, **go back to the World Cities with Xano tutorial and** [**follow these steps**](/connect-api/browse-by-backend/xano/xano-build-nested-lists.md#setting-up-the-data-binding-on-bravo)**.**

Once you do that, you'll have the World Cities app as in the original tutorial. Now, we'll create two more API requests to implement the new bookmark functionality.

### 1️⃣ **Request**: **`GET`** **Get cities by device**

Duplicate one of the existing GET requests, and use the following URL:

```
https://XANO_URL/citiesByDevice?deviceId=${device.id}
```

In the **Test Values** tab, specify a **device.id** that already exists in the **bookmarks** field of the Xano database (in case you don't have any, add one with a random value). In our case, the value device33 already exists there.

![](/files/-Mj3y7p5-aDbr6t3txZC)

Click `Send` to get a response. After doing this, make sure the following data is selected:

![](/files/-Mj3yDYzW0mCZyQKZM6X)

Finally, in the `Selected Data` panel, set the **.data\[].id** path to `city_id`. This way, we can reuse the detail requests created in the [original tutorial](/connect-api/browse-by-backend/xano/xano-build-nested-lists.md#setting-up-the-data-binding-on-bravo).

### 2️⃣ **Request**: **`PATCH`** **Add bookmark**

Now, we'll create the PATCH request we'll later bind to a remote action, to send the `device.id` to the Xano endpoint and add the bookmark.

Duplicate one of the existing requests, and use the following URL. Then, change the API verb to **`PATCH`**.

```
https://XANO_URL/cities/${city_id}
```

Go to the `Body` tab and add the following JSON.

```
{
  "device_id": "${device.id}"
}
```

Click `Send` to test the request. As we won't need to bind the response data, it's not necessary to modify the **Received Data** or **Selected Data** tabs.

### 3️⃣ **Update the** `Get Single City` **request**

Finally, we need to update the **Get Single City** request we created in the original tutorial. This is the request bound to the **City Detail** screen, where we'll bind the remote action to add a bookmark.

1. In the **Received Data** panel of this request, make sure the `.data.id` path is selected, along with all the other necessary data for the **City Detail** screen.
2. In the **Selected Data** panel, give this path the name of `city_id` as shown below. Notice that this is the **same name** we specified for the URL query string variable in the **Add Bookmark** request. This way, Bravo will know how to "connect" the **Get Single City** and **Add Bookmark** requests, so the bookmark is added to the city where the user triggered the remote action.

![](/files/-Mj3zLUM33EP4DcvpT7g)

## 🔖 Binding the UI elements to the API requests

Finally, we need to bind the mobile UI to the API requests we just created. Once again, **go back to the original tutorial** and [complete these steps](/connect-api/browse-by-backend/xano/xano-build-nested-lists.md#bind-the-data-to-the-screens).

For the bookmark functionality, we'll have a new screen: **Bookmarked Cities**. This screen will show the list of cities the user has bookmarked based on their device ID. In the data binding mode of this screen, bind the data as follow.

![](/files/-Mj3zrud4qGYvq9IeqOV)

Finally, we'll bind the **City Detail** screen, where we have the bookmark icon with remote action.

1. In the data binding, click on the bookmark icon layer.
2. Under **Action: Remote action,** bind the **`PATCH`** **Add Bookmark** request as shown below. Configure the success and error response actions. In our example, we added an alert message for both.

![](/files/-Mj4--1ZrH8IFKGP70d3)

That's it! Now you can test the bookmark functionality in your app by clicking on the bookmark icon in a city detail page, and see the city appear in the **Bookmarked Cities** screen.

Happy Bravorizing! 🥳


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.bravostudio.app/connect-api/browse-by-backend/xano/xano-create-a-bookmark-functionality-with-remote-actions-and-device-id.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
