How to add DigitalOceans Spaces adapter to an Appwrite instance
Tags: devops, appwriteself, nuxt, programming
Published: 2023-09-21 14:59:37


The rapid development of the Internet has created an unprecedented need for better storage. The current state of web development makes it crucial for applications to utilize services such as user authentication, file storage, and data delivery. Fortunately, [Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) offers a solution that includes these services, eliminating the need for complex server setup and management.
 

## What is Appwrite?

Appwrite is an all-in-one solution offering backend services such as authentication, databases, and file storage. It's user-friendly and can be self-hosted or used as a cloud service. 
To overcome storage limitations encountered when using a self-hosted Appwrite instance, Appwrite allows you to extend its storage service using several external storage services, such as [](https://www.digitalocean.com/products/spaces?utm_source=hackmamba&utm_medium=hackmamba-blog)[DigitalOcean Spaces](https://www.digitalocean.com/products/spaces?utm_source=hackmamba&utm_medium=hackmamba-blog).

## What is DigitalOcean Spaces?

DigitalOcean Spaces is an object storage service offered by DigitalOcean, a popular cloud infrastructure provider. Spaces allows you to store and serve large amounts of data, such as images, videos, and other files, in a scalable and cost-effective manner.
This resilient, secure, and cost-effective file storage solution can seamlessly connect with Appwrite via an adapter, enhancing the storage service offered by the Appwrite instance.


## Benefits of extending Appwrite storage service with DigitalOcean Spaces

Adding the DigitalOcean's Spaces adapter to an Appwrite instance essentially extends Appwrite's storage service. This extension offers the following advantages.

- **Scalability and performance**: As your application gains traction, the need for storage increases. DigitalOcean’s Spaces scales seamlessly, accommodating large data volumes. Merging it with Appwrite ensures smooth performance and efficient data handling.
- **Cost** **efficiency**: Unlike conventional server-based storage, Spaces lets you pay solely for used storage, a budget-friendly choice to expand your Appwrite app minus unnecessary costs.
- **Backup** **and** **recovery** **made** **simple**: With Spaces, crafting Appwrite backups is straightforward. Regular backups ensure that you're well-prepared for unforeseen data loss incidents.

_Next, let's dive into extending your self-hosted Appwrite service with DigitalOcean’s Spaces by building a **media-gallery application** and adding the storage service._


## Tutorial: Building a Nuxt.js media-gallery application

Here we’ll build a media gallery application using Nuxt.js, self-hosting Appwrite, extending Appwrite storage with DigitalOcean Spaces, and linking the storage to the application. The project repository can be found [here](https://github.com/Otrex/nuxt-media-gallary). The application's functionalities will include:

- Creating image galleries.
- Storing and displaying created galleries.
- Generating shareable links for the galleries.
  
## Prerequisites

The following requirements apply:

- Basic knowledge of JavaScript, [Vue.js](https://vuejs.org/), and [Nuxt.js](https://nuxt.com/).
- [Node.js](https://nodejs.org/en) and [Yarn](https://classic.yarnpkg.com/en/) installed on your computer.
- [Docker](https://docs.docker.com/engine/install/) installed for local hosting of Appwrite.
- A [DigitalOcean](https://www.digitalocean.com/?utm_source=hackmamba&utm_medium=hackmamba-blog) account to create and access Spaces.
## Setting up DigitalOcean Spaces

To set up your DigitalOcean Spaces, create a new Spaces bucket on DigitalOcean and extract the Spaces Bucket access credentials.

**Creating a new Spaces Bucket**

To get started, log into your DigitalOcean account.

![A screenshot showing the DigitalOcean platform](https://lh5.googleusercontent.com/BiQ5WrbtXyx4VsdEOG9G9H6UXY082-arK0_BW-FJTCWZHfrpmV1ifRruytmmgzffLWnVdb7sykbjbfZBaeSdu48n9o6wlDZF5pM1zXzxOQLecL5yCsup2BgBANra-ymKIhDkRv5qtW9FKchlenHYV8k)


Click **Store static objects**, which takes you to the section for creating Spaces.

![A screenshot showing the Spaces creation interface](https://lh4.googleusercontent.com/D2YQB0GqfIC4ub25VsY0Es0w9dS4-6F6L1QIjGNqtBmRfhkASQsjtpqWiHU2LH8u_RyOavGwKl_EK4VSAGSvD-Is9h1AyquTA3JGAKZ8CDYsMRRG0vIemt35xwnMtHWmTmE95zofX-589AI3x058j1Y)


Afterward, keep the default values for **Choose a datacenter region** and the content delivery network (CDN). Enter your preferred bucket name in the **Choose a unique Spaces Bucket name** section. For this tutorial, you’ll be using **nuxt-gallery**.

Usually, a Spaces Bucket is linked to a project. For this guide, you’ll use the default project created by Digital Ocean when you sign up successfully.

Next, click **Create a Spaces Bucket**.

![A screenshot showing the Create a Spaces Bucket](https://lh5.googleusercontent.com/W4g_N2oSPgV4-KwJeff2VEtQ4waj8_FMIwZb1TRNei4bLQssu8aTPsUSw4btawxLKF5QWvbvXlW4zqOca7g7Ev_boUM2w3Uty1Wvjc1m7YAMcmz3ueHYtZP7b1bWJ54VmQ6sBmcTF99CIqXkxqcZjcE)


Your Spaces bucket will be created and assigned an **Origin Endpoint.**

![A screenshot showing the created bucket](https://lh6.googleusercontent.com/QJt0fln0SMgAJlMrkNRuu7JVxzNANbMOoFe7Km4-qX850mQGm7zzn_Eq2DIctFySI6xrugymLgRdiZUlb1idBw2GQ0dn802Vs1LUKv26nIBUWG7CiUb8HSRH0pnCwNGvEFzwZB0p7w8q-G9_q17xbNQ)


Next, you’ll need to enable anyone to access the content on your bucket. To do this, click on the **Settings** tab.

![A screenshot showing the nuxt-gallery settings tab](https://lh4.googleusercontent.com/9J4HuCoiLXLmZ_PLz81QN7IqeT7gg19fidphou99XjvuTk6Ao2F3tUgdUe3F9Zz_JlEhZdoELJogcQAeL5YRJi6yqiPYnVzA3WUNJWSMyqaWpqzpERuLRNqE5t0AqrjbhpZRLJ0S39xjNLs7YyLpO8E)


Then on the **File Listing** section, click **Edit.**

![A screenshot showing the file listing section](https://lh3.googleusercontent.com/BRTMgEZ7i4AHANtDAwKweO14YqRaFn4zNMlDqIFUg924gZxWLFdwh8wKJs9w8cLBlf75SK0Q4lqHtKVUpHAeiHS1q-__95PfSdsxV-IrhiShHL5ag5n7j-AtlAQEVOosn_U-ej6AlkHACeeqGxdo_Uo)


After that, select **Enable File Listing**.

![A screenshot showing the Enable File Listing option](https://lh3.googleusercontent.com/tsXxkdSOBsb65r0J54hNWdK_1TnHAtIUYxY1wUgW1au7xtW_nLeOBmTwfBU3Jb5AOzRgV54IIgfsDYd4q28MS7Mkpzhu3NzwHQIuI2R3b82G78VSDlGzB4qSLe9O0HENz4SvH2IE_BSvRZdkO_0K-Gg)

**Getting the Spaces Access Keys**
Next, you’ll need to retrieve the following keys:

- `<bucket-name>`
- `<access-key>`
- `<access-secret>`
- `<bucket-region>`

The bucket URL can be found in the **Origin Endpoint** indicated below.

![A screenshot showing the bucket URL](https://lh6.googleusercontent.com/6GwHTVgL_8G8fVbKVQeIvcDJ-Ok9zqAcKCXxTSFGODowgDVJ3GhFlk_kofCIYWjUatAuNr_hyEw05rhvYfMVsQkKOZ2ez-QR0h3N1Fc1KhyRLmv0sQD8Du3D0qTboeaD_eODqVSA6aHuTbqTvjkWEr0)


This bucket URL contains the `<bucket-name>` and the `<bucket-region>` in the form:
```sh
https://<bucket-name>.<bucket-region>.digitaloceanspaces.com
```
 You can then extract these keys from the URL on your dashboard.

Next, the `<access-key>` can be obtained by navigating to **API menu.** Click on **API.**

![A screenshot showing the API menu on the navigation bar](https://lh6.googleusercontent.com/BHwkMYwGBjU2P01CjLVfLNG7csfmLjI5d9jB4jDkznuCvObAMFNU4JsRA-5L6bp265cPXhg0AukGTw7nCdx6edEApESpLxGM8H4pPZlK99c0XlRytO4n8JSW-sk5zB6ENGIiyhN8QFee1EgeuWk7Y80)


After that, click on **Spaces Key.**

![A screenshot showing the Spaces Key tab](https://lh3.googleusercontent.com/kXLVpTEowt-lS_-_Aog7QYBdydeA7I5PNrK-01QiGCIPJJOQeMRR9LvwGpeNzXzmR4wUrBda9OjDZbbVKcEDRiTYJzmpo6sj8bMu9DuIIsF_1cp1JCTZYqXyLW59ihVDfM6yX9LeixsxiO0JZlyZCQs)


Then, in the **Spaces Key** section, click **Generate New Token.**

![A screenshot of the Generate New Key button on the Spaces Key](https://lh3.googleusercontent.com/23Ws6ofo9coNsAqcr6dHrh3D1ulwW7cx312GpxQ4oz6DDB7Xp-z3es2nlug_V88QiAoNQyQgcpGs0CbXQqw63gcWNWt4u1Fuembfk2g9Ft0O7fdAfVXaNb99jqdgo5Hbo7a-io8n6NAd2nOhHDTQtSY)


Enter the name of the key, for this case, **nuxt-media-gallery**.

![A screenshot showing the input to enter the name for your access keys](https://lh6.googleusercontent.com/iQ0vW6FwrwVPjLazbYRjGMnKOIcPNjzIGMQPeKXb7TX92GcWn8T27-zkRD0f2VDYrBDRn_uphojKxb4MLXJUvUjoobaD2aqZlQ6mDeL2B11ELRFT2o6zSLqTeWrvDfkTWYwp9JxfhVFSMcPAa6CNIO8)


Then click the button indicated below to generate the key.

![A screenshot showing the button to trigger generating the key](https://lh3.googleusercontent.com/snwT-E-MkTzczJbbvgBt3trJjYfGn8aJDHItnmASTHF0EZqfzB6t3xcwtgCe1hqUeQqU7jUVBNZ03L9yWoI7GDgpTAJxUVGa-ptSmiacEznONwWScI196eToGClhVMZl3ij3Yz9v2gezyUuyhB2cyq4)


After that, you’ll have the **access keys** and **secret**.

![A screenshot showing the Access Key and Access Secret](https://lh4.googleusercontent.com/FJNYVpJnRJpFpvBHl1tevRj2yKDNKvvrjCsPCCPXjAdPQydU3lIR0J3A6JxpueFrXwCEohLwsSnfbLflkqXg6GWs78JID5MIGwf23X6IwPgzVqoxn7Nq8DCK4yBSP8xXbrdBCPrgkZ9NUOD-NJhK6lQ)


The **Access Secret** is located in the **Secret** row, and the **Access Key** is located in the **nuxt-media-gallery** row. Make a copy and save your Secret key because it will be lost if you leave the page.

## Setting up Appwrite

To proceed, you‘ll need to locally host your Appwrite server. Let’s use Docker to accomplish this. Check out the [installation guide](https://appwrite.io/docs/self-hosting?utm_source=hackmamba&utm_medium=hackmamba-blog) to learn more about self-hosting Appwrite.

**Locally hosting Appwrite with Docker**
Ensure Docker is running by using the command below on your terminal to verify.
```
docker info
```
Create a directory named **appwrite-spaces** in the current working directory using the following: 
```
mkdir appwrite-spaces 
cd appwrite-spaces
```

Then, download the Appwrite base [docker-compose.yml](https://appwrite.io/install/compose?utm_source=hackmamba&utm_medium=hackmamba-blog) and [.env](https://appwrite.io/install/env?utm_source=hackmamba&utm_medium=hackmamba-blog) files into the created folder.

Open the **.env** file and update the keys' values as follows:
```
_APP_STORAGE_DEVICE=DOSpaces
_APP_STORAGE_DO_SPACES_BUCKET=<bucket-name>
_APP_STORAGE_DO_SPACES_REGION=<bucket-region>
_APP_STORAGE_DO_SPACES_SECRET=<access-secret>
_APP_STORAGE_DO_SPACES_ACCESS_KEY=<access-key>
```

Setting `_APP_STORAGE_DEVICE` from `Local` to `DOSpaces` indicates to Appwrite to use DigitalOcean Spaces instead of a locally implemented storage system.

Make sure you replace `<bucket-name>`, `<bucket-region>`, `<access-secret>`, and `<access-key>` (which are the keys obtained from the API section of the DigitalOcean dashboard).

Once that is done, execute this command within the **appwrite-spaces** directory:
```
docker compose up --remove-orphans -d
```
Using the `--remove-orphans` flag will remove containers unrelated to the present services before initiating the new ones.

![A screenshot showing the executing Docker compose command](https://lh3.googleusercontent.com/mBa5Vh7Ao_z5-OuhCAvvZF2j0LNDclVWegK0mMYo2WiW4H1I5eJreaO0mCz0PVqZO4D-nUjQ16Grf71U2L_MhD0wqY9iriIxQ7BzChC4e0-6lUqaX33uSagBv_bDE29Cuq2uh_t64ImWiRLrry_olJ0)

After the services have been pulled by Docker from the Docker registry, the containers are then created.

![A screenshot showing the competed execution of the command](https://lh5.googleusercontent.com/mISW18bfkdBOpOb7ogKr6OWh_1tkxFG0pNfsoz5bGVzU7Uj2VpD7iidWQRP1ZZaXZisi49KFaJiVslAFdOCm40xUpS3sqiXygFAz6yMBcbK-6ScRZN039QXEgVI8MP3mwp-7MNU8MlRw-vp4z0TKUlo)

After that, navigate to [http://localhost](http://localhost) to access your Appwrite instance.

![A screenshot showing the landing page after visiting http://localhost](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692404779839_Screenshot+2023-08-19+at+01.09.34.png)

Next, click **Sign up** to create a new account.

![A screenshot showing the sign up page](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692404794053_Screenshot+2023-08-19+at+01.11.17.png)


After signing up, enter your project’s name. For this case, use **nuxt-media-gallery** then click **Create Project**.

![A screenshot showing the section to enter the project’s name](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692404804042_Screenshot+2023-08-19+at+01.13.03.png)


**Creating the Appwrite storage for the project**
To create the storage, Select **Storage** and click **Create bucket**. 

![A screenshot showing the Storage menu button and the Create Bucket button](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692404950475_Screenshot+2023-08-19+at+01.27.41.png)


After that, enter the name of the bucket. In this case, use **nuxt-media-bucket**, then click **Create**.

![A screenshot showing the name input and the create button](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692404998090_Screenshot+2023-08-19+at+01.29.48.png)


Following that, you must grant **CREATE** and **READ** permissions to **any** user on this storage bucket. To do so, click the **Settings** tab.

![A screenshot showing the settings tab](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692405071916_Screenshot+2023-08-19+at+01.30.42.png)


Then, navigate to the **Permissions** section, click **Add a role to get started** and select the **Any** option.

![A screenshot showing the Add a role to get started button](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692405127600_Screenshot+2023-08-19+at+01.31.38.png)


After that, select the **CREATE** and **READ** permissions.

![A screenshot showing the CREATE and READ checkbox](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692405314777_Screenshot+2023-08-19+at+01.33.53.png)


**Extracting your Appwrite credentials**
To retrieve your credentials, you need to get the values for the following:

- `<appwrite-bucket-id>`
- `<appwrite-project-id>`
- `<appwrite-api-endpoint>`

Navigate to **Settings** by clicking the **Settings menu** to obtain the `<appwrite-project-id>`.

![A screenshot showing the Settings menu](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692405388747_Screenshot+2023-08-19+at+01.35.53.png)


After that, find the **Project ID** and **API Endpoint** as indicated below.

![A screenshot showing the Project ID and the API Endpoint](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692405487020_Screenshot+2023-08-19+at+01.37.08.png)


Next, to get the **bucket ID**, navigate to the **Storage** menu; there, you’ll see the **bucket ID** on the bucket as indicated below.

![A screenshot showing the BucketID](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692405638457_Screenshot+2023-08-19+at+01.39.10.png)

## Setting up the Nuxt.js project

To set up the Nuxt.js project, open your terminal, and run the command below:
```
npx nuxi@latest init <project-name>
```
In this case, replace the `<project-name>` with `nuxt-media-gallery`. Then run the command below:
```
cd <project-name>
```
This changes the current working directory of the terminal to the project’s directory. Next, you’ll need to install all the dependencies.

The dependencies for this project include:

- [Appwrite SDK](https://appwrite.io/docs/sdks)
- [Swiperjs](https://swiperjs.com/) (this will be used to perform the slide show)
- [TailwindCSS](https://tailwindcss.com/)

To install, run the command:
```
yarn add appwrite swiper @nuxtjs/tailwindcss @vueuse/core
```

Follow the steps [here](https://tailwindcss.nuxtjs.org/getting-started/setup) to set up **TailwindCSS** for the project.
After that, create an **assets** folder in the root directory and add the folders and files according to this [link](https://github.com/Otrex/nuxt-media-gallary/tree/main/assets).

**Adding the environmental variable**
To add these variables, create a **.env** file in the root directory of the project and add the following to the file:
```
NUXT_APPWRITE_API_ENDPOINT=<appwrite-api-endpoint>
NUXT_APPWRITE_BUCKET_ID=<appwrite-bucket-id>
NUXT_APPWRITE_PROJECT_ID=<appwrite-project-id>
```
Replace the `<appwrite-api-endpoint>`, `<appwrite-bucket-id>`, and `<appwrite-project-id>` values with the values you obtained from the Appwrite dashboard.

**Creating the UI components and screens**
To do so, first add the **pages** folder to the project's root directory. This is required for creating the application's **routes/screens**.

Next, navigate to the **app.vue** file and change the file's content to look like this:
```html
<template>
  <div class="h-screen">
    <nuxt-page />
  </div>
</template>
<script lang="ts">
import "swiper/css";
</script>
```
**Landing screen**
Following that, create a screen to collect the user's name so that the media files can be saved and identified.

In the **pages** folder, create a file, **index.vue,** and add the following:
```html
<template>
  <div class="x-container">
    <div class="x-card">
      <h1>Welcome to MShare</h1>
      <p>Please provide your name so we can setup your unique profile</p>
      <div class="mt-2">
        <input
          type="text"
          class="w-full"
          v-model="store.name"
          placeholder="Name: e.g. Obisike Treasure"
        />
      </div>
      <div class="mt-2">
        <input
          type="button"
          @click="navigateTo('/gallery')"
          :disabled="!!!store.name"
          class="w-full bg-orange-400 disabled:bg-orange-200 disabled:text-gray-600"
          value="Create"
        />
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
const store = useAppSession();
</script>
```
The vue template above consists of a card-like UI element with the heading "Welcome to MShare" and a paragraph prompting users to provide their name to create their gallery. Two input elements are present: one for the user's name and another for a "Create" button. The button is disabled until the user enters their name and then changes color. When users enter their name and click the button, the "create" method is triggered, allowing them to continue with the gallery creation.

**Gallery screen**

Following that, you need to add the **route/screen** for the users to upload their gallery. To do this, create a file **gallery.vue** in the **pages** directory, then add the following:

```html
<template>
  <div class="h-screen p-[3rem]">
    <div class="x-card !max-w-full" v-if="store.previewKey"> 
      <h3> Sharable Link: 
        <span class="bg-slate-200 inline-block px-2"> {{ location?.origin }}/share/{{ store.previewKey }} </span>
      </h3>
    </div>
    <div class="x-card !max-w-full"> 
      <h1> Select your media file:</h1>
    </div>
    <div class="x-card !max-w-full"> 
      <div class="grid grid-cols-2 sm:grid-cols-4 gap-[2rem]">
        <transition-group appear name="fade"> 
          <div class="w-full aspect-square x-border" v-for="(img, idx) in images" :key="img.idx"> 
            <div class="w-full h-full relative">
              <img :src="img.url" class="w-full h-full object-cover" />
              <div class="absolute inset-0 backdrop:bg-slate-500">
                <button class="bg-white rounded-md m-3 px-3" @click="removeImage(idx)">Remove</button>
              </div>
            </div>
          </div>
        </transition-group>
        <div class="w-full aspect-square"> 
          <label class="x-input__file w-full h-full"> 
            <svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M450-450H200v-60h250v-250h60v250h250v60H510v250h-60v-250Z"/></svg>
            <p> Add Image</p>
            <input
              type="file"
              ref="imageField"
              @change="setImages"
              name="images"
              multiple
              class="hidden"
              accept="image/*"
            />
          </label>
        </div>
      </div>
    </div>
    <div class="x-card !max-w-full"> 
      <button 
        class="bg-orange-300 disabled:bg-orange-100 w-full py-[1rem]" 
        @click="submit" 
        :disabled="!images?.length"
      >{{ loading ? 'Uploading...': 'Upload' }}</button>
    </div>
  </div>
</template>
``` 

The template above consists of a container with multiple cards and grids. The first card displays a sharable link if the `store.reviewKey` data exists. The second and third cards show static content. The fourth card contains a grid with images that can be added or removed using a **Remove** button. Users can upload multiple images using a hidden file input, and an **Upload** button is disabled until images are selected.

```html
<script setup lang="ts">
import { ID } from "appwrite";
import { IFileImage } from "utils";
const config = useRuntimeConfig();
const store = useAppSession();
const loading = ref<boolean>()
const location = ref<Location>();
const images = ref<IFileImage[]>([]);
onMounted(() => {
  location.value = window.location
})
function removeImage(idx: number) {
  images.value.splice(idx, 1);
}
async function setImages(e: any) {
  const files = (e.target as HTMLInputElement).files || [];
  const fileImages = await Promise.all(
    Array.from(files).map(async (file, idx) => ({
      url: await convertToURL(file), 
      file, 
      idx
    })),
  );
  images.value.push(...fileImages);
}

async function submit() {
  try {
    loading.value = true
    await Promise.all(images.value.map((img, idx) => {
      return pushToAppwrite(img.file, idx)
    }))
    store.value.previewKey = generateKey(images.value.length - 1);
    alert("Successfully published!")
    
  } finally {
    loading.value = false
  }
}


async function pushToAppwrite(file: File, idx: number) {
  const storage = appwriteStorage();
  await storage.createFile(
    config.public.appwriteBucketId, 
    ID.custom(generateKey(idx)),
    file
  );
}
</script>
```

In the provided script, the `pushToAppwrite()` function connects to Appwrite using credentials and uploads a given file object. A unique key is generated using `generateKey()` for future file retrieval.
The `setImages()` function extracts images from the file input and creates a base64 URL with `convertToURL()`. This URL is then used to render the file blob as an image URL in a grid block.
The `submit()` function uploads images using `setImages()` to the `images` state. It also generates a `previewKey` for creating a shareable URL. To retrieve files with the shareable ID, the file ID has to be in this format: 
```sh
<name>-<timestamp>-<length-of-array-of-images>
```

**Share screen**

Next, create the screen for the shareable link. Begin by creating a folder, **share**, within the **pages** directory. Inside the **share** folder, create a file called **[pid].vue**.
As per Nuxt guidelines, the route path is **/share/[pid]**.

Then, append the specified content to the **[pid].vue** file.

```html
<template>
  <div class="py-[10%] px-[15%] w-full h-screen">
    {{ route.params }}
    <div class="flex flex-col w-full h-full">
      <div class="x-card !max-w-full"> 
        <h3> Obisike's Slideshows </h3>
      </div>
      <div class="pb-20"> 
        <swiper
          :effect="'cards'"
          :grabCursor="true"
          :autoplay="{
            delay: 2500,
            disableOnInteraction: true,
          }"
          :modules="modules"
        >
          <template v-for="(img, idx) in images" :key="idx">
            <swiper-slide class="h-[20rem]">
              <div class="w-full">
                <img :src="img" class="w-full object-cover" loading="lazy" />
              </div>
            </swiper-slide>
          </template>
        </swiper>
      </div>
    </div>
  </div>
</template>
<style scoped>
.swiper-cards{
  overflow:visible;
  height: 50%;
}
.swiper-cards .swiper-slide {
  transform-origin:center bottom;
  -webkit-backface-visibility:hidden;
  backface-visibility:hidden;
  overflow:hidden;
}
</style>
```

The template creates a slideshow using the **Swiper** library. It arranges images in a card-style layout, with a title given by the name of the gallery’s creator together with **SlideShow**, above the slideshow. The images transition using a **cards** effect and autoplay every 2.5 seconds. The template ensures responsiveness and lazy loading for images.

```html
<script setup lang="ts">
import { Swiper, SwiperSlide } from "swiper/vue";
import { EffectCards, Autoplay } from "swiper/modules";
const modules = [Autoplay, EffectCards];
const config = useRuntimeConfig();
const route = useRoute();
const images = ref<string[]>([]);
onMounted(() => {
  getImages()
})
async function getImages() {
  const storage = appwriteStorage();
  const keys = getFileIdsFromPreviewKey(route.params.pid as string);
  if (!keys) return;
  keys.forEach(k => {
    const url = storage.getFileView(config.public.appwriteBucketId, k)
    images.value.push(url.toString())
  })
}
</script>
```

To break it down, in the script above, when the component loads, a function is triggered to fetch images, and then the `getImage()` function gets the image from Appwrite using the route parameter `pid`.

Additional modules, Autoplay and EffectCards, are applied to the Swiper component to enhance the slideshow with a card-stacking effect.

Next, you’ll need to add the utility functions.

**Adding the utilities functions**

To add utility functions, create a folder **utils** and a file **index.ts** within the **utils** folder. Then copy the contents of [this file](https://github.com/Otrex/nuxt-media-gallary/blob/main/utils/index.ts) and paste them into the **index.ts** file.
The functions in this file is automatically imported due to Nuxt.js. 

One of the notable functions you copied is the `appwriteStorage()` function.

```js
export function appwriteStorage() {
  const config = useRuntimeConfig();
  const client = new Client();
  const storage = new Storage(client);
  client
      .setEndpoint(config.public.appwriteAPIEndpoint)
      .setProject(config.public.appwriteProjectId);
  return storage
}
```

This function obtains the environment variable via `useRuntimeConfig()`, establishes a connection to Appwrite Storage service, and returns the storage object, which contains methods for retrieving and uploading files to appwrite.

## Application demo

To showcase your application, start by accessing your terminal within the project directory, then execute the following command:
```
yarn dev
```
Once the command is successfully executed, the Nuxt.js development server will start on the port 3000.

Visit http://localhost:3000 on the browser to preview the screens. 

Next, enter your desired name then click **Create.**

![A screenshot showing the entry point of the application](https://lh5.googleusercontent.com/c_2zngPA_upj7QN9CVHU6W-Yxal9cIqjQoo9ewis1cU9YJKtXks4MhwTmF80ZQBCQ8Ivu6S-KlsLZ1tkLBCab489KKPDsKpDpDcoRms42UMRQ-NkHgv99o9jt3NV35e5tSA5y8CiNxTGdDFKptF-q18)


After that, click **Add Image**, to add the images.

![A screenshot showing the add images screen](https://lh5.googleusercontent.com/QM4tU4ZCjORTZ-VAkTmuPLbD9_ZyKKyR4LclBo-Qnvmc1FsyTT9qepKwoDSGM8x4MzO104eESXjTfqqreRZ04zFru3mpbFxU5LMDzZwQfUwDx8b3dWfJbzjjI8kQ8ExGZAu2nBjydu9THgglKXcZ72Q)


Then click **Upload** to upload the selected images. 
Once that is done, a shareable link will be generated. Copy and open this link in a different browser window.

![A screenshot showing the shared gallery](https://lh6.googleusercontent.com/ZaGsYpDXfAKL2rrHzQ_6fyS5f_zM28p9wOTOBI5cOg3_az8SqN-IP49VrLwF5Uz714Ne0IBvJTaxCgIz-vt2CvqoJU04_M1UCRavk0thFGUQRBYpfLDyW5RZwIDUqipTNZ9V8aeuhdYwQy9vt-BnY3Y)


Here is a video showcasing the application:

https://www.loom.com/share/0aac3ddd749748ad915b0f438837c6f6?sid=1daab281-7f22-40d4-ba9d-04cc45251084


To prove that the files are uploaded to Digital Oceans directly, here is a screenshot showing the file on Digital Oceans’ spaces.

![a screenshot showing the file on Digital Oceans’ spaces](https://paper-attachments.dropboxusercontent.com/s_44CF350CF0CA067DC478723FAA4CD50A95BD7757BE59288FE2D6442E64A0263E_1692545843048_Screenshot+2023-08-20+at+16.37.11.png)

## Conclusion

In this tutorial, we've learned how to extend Appwrite's storage capabilities using DigitalOcean Spaces. By combining the strengths of these two services, developers can create robust applications with secure and scalable storage solutions.

## Resources
- [Scale Appwrite Storage with DigitalOcean Spaces](https://dev.to/appwrite/scale-appwrite-storage-with-digitalocean-spaces-36kh)
- [Nuxt.js Documentation](https://nuxt.com/docs)
- [Appwrite Documentation](https://appwrite.io/docs?utm_source=hackmamba&utm_medium=hackmamba-blog)