How to add S3 adapter to an Appwrite instance
Tags: tutorial, nextjs, aws, appwrite
Published: 2023-09-22 08:18:30

In the modern day, cloud storage facilities have evolved significantly, presenting companies with a plethora of options to store and manage data efficiently. The availability of cloud storage options allows developers to design and deploy applications that scale effortlessly to accommodate growing user bases and increasing data volumes. 

Cloud providers invest heavily in state-of-the-art security measures, data encryption at rest and in transit, identity access management, storage capacity, and comprehensive compliance certifications. This level of security is often beyond the capabilities of individual organizations, making cloud storage a preferred choice for sensitive data.

As data grows in volume and importance, the elasticity that cloud providers deliver accommodates increasing user demands and remains performant even during traffic spikes.

This article demonstrates a beginner-friendly tutorial on integrating an Amazon S3 adapter into an Appwrite instance. The complete source code for this project is located [here](https://github.com/ugwutotheeshoes/next-appwrite-s3). Clone and fork it to get started.

### Prerequisite

To follow along with this tutorial, you’ll need the following requirements:

- A basic understanding of [Next.js](https://nextjs.org?utm_source=hackmamba&utm_medium=hackmamba-blog)
- Node.js and its package manager, `npm`; install [here](https://nodejs.org/en?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Docker](https://www.docker.com/get-started?utm_source=hackmamba&utm_medium=hackmamba-blog) installed
- Access to an Appwrite Cloud account; submit a request for Appwrite Cloud [here](https://appwrite.io/cloud?utm_source=hackmamba&utm_medium=hackmamba-blog)
- An Amazon Web Services (AWS) account; create a free account [here](https://signin.aws.amazon.com/signin?redirect_uri=https%3A%2F%2Fconsole.aws.amazon.com%2Fconsole%2Fhome%3FhashArgs%3D%2523%26isauthcode%3Dtrue%26nc2%3Dh_ct%26src%3Dheader-signin%26state%3DhashArgsFromTB_eu-north-1_b9663b39f47a26d5&client_id=arn%3Aaws%3Asignin%3A%3A%3Aconsole%2Fcanvas&forceMobileApp=0&code_challenge=pzN5FuR3zjuujLjFoGXZOwxQ3P5X1n5ljVWL8dREtWE&code_challenge_method=SHA-256?utm_source=hackmamba&utm_medium=hackmamba-blog)

Before initializing an Appwrite instance, we must prepare an Amazon S3 bucket and authenticate it to Appwrite. 

### What is Amazon S3?

[Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html?utm_source=hackmamba&utm_medium=hackmamba-blog), also known as Amazon Simple Storage Service, is a web service interface-based object storage service provided by Amazon Web Services. It includes performance, security, and scalability that are unmatched in the market.  

### How Amazon S3 works

Amazon S3 is an object storage service that stores data as objects within buckets. An object is a file and any metadata that describes the file. A bucket is a container for objects that can store any number of objects/data.

To fetch or send data to an S3 bucket, we’ll use an Amazon S3 adapter. The S3 adapter needs to be authenticated by an Appwrite instance locally using an AWS access key and secret key. 

To create an Amazon S3 bucket, log into the [AWS Console](https://signin.aws.amazon.com/signin?redirect_uri=https%3A%2F%2Feu-north-1.console.aws.amazon.com%2Fservicequotas%2Fhome%2Fservices%2Fs3%2Fquotas%3FhashArgs%3D%2523%26isauthcode%3Dtrue%26region%3Deu-north-1%26state%3DhashArgsFromTB_eu-north-1_fe75efd8c69e16ca&client_id=arn%3Aaws%3Asignin%3A%3A%3Aconsole%2Fservicequotas&forceMobileApp=0&code_challenge=wFaZtAjUlysIuZnBsEA9pulFEtdXvFFrsKjIXm2jswU&code_challenge_method=SHA-256?utm_source=hackmamba&utm_medium=hackmamba-blog), head to the search bar on the top right corner of the dashboard, and search **S3**. Go to the **S3** page and create a new bucket.


![Create a bucket](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1690811070945_S3-Management-Console+1.png)


Use the general configuration below to create a bucket.

|                        |                                                                       |
| ---------------------- | --------------------------------------------------------------------- |
| **Bucket Name**            | pink-collage                                                          |
| **AWS Region**             | US East (N. Virginia) us-east-1 (THIS MAY VARY DEPENDING ON A USER’S LOCATION) |
| **Object Ownership**       | ACLs Disabled                                                         |
| **Public Access Settings** | Block all public access                                               |
| **Bucket Versioning**      | Disable                                                               |
| **Default Encryption**     | Server-side Encryption with Amazon S3-managed keys                    |

> _**NOTE:** The AWS region may vary depending on your location. Visit [here](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html) to learn more._

![Bucket configuration](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1690811508639_S3-bucket.png)


We also want to get credentials that give Appwrite access to read and write our newly created bucket. To do that, click on the username in the upper right corner and select **Security Credentials**. Next, select **Create Access Key**. We'll be redirected to the dashboard to see the newly created credentials. Copy the access secret and access key; we'll use them later.


![Create Access Key](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1691012924550_Security-credentials-IAM-Global.png)
![Set of credentials](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1692115878213_MJdOZdmY.png)


> _**NOTE:** Don’t share your access secret and access key._

## Configuring Appwrite

To use Appwrite in our Next.js application, install Appwrite’s client-side software development kit (SDK) and [Pink Design](https://pink.appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) to style web applications.
```js
npm install appwrite
npm install @appwrite.io/pink
```
We need to create an Appwrite instance in a preferred directory by running the command below:

```js
docker run -it --rm \
    --volume /var/run/docker.sock:/var/run/docker.sock \
    --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
    --entrypoint="install" \
    appwrite/appwrite:1.3.8
```
Then, configure the instance using the details below:

|                       |                                                                                      |
| --------------------- | ------------------------------------------------------------------------------------ |
| **Server HTTP port**      | Default: 80 (Use the default port or press “enter”)                                  |
| **Server HTTPS port**     | Default: 443 (Use the default port or press “enter”)                                 |
| **API key**               | Default: 'your-secret-key' (Feel free to change this)                                |
| **Appwrite hostname**     | Default: 'localhost' (In case you have a domain or subdomain, if not, press “enter”) |
| **DNS A record hostname** | Use the same input as the **Appwrite hostname**                                          |

Once our Appwrite instance is running, we can configure the storage adapter. To do that, locate the `.env` file Docker created for the Appwrite instance and replace the variables as shown below:
```js
_APP_STORAGE_DEVICE=S3
_APP_STORAGE_S3_BUCKET=OUR_BUCKET
_APP_STORAGE_S3_REGION=OUR_REGION
_APP_STORAGE_S3_SECRET=OUR_ACCESS_SECRET
_APP_STORAGE_S3_ACCESS_KEY=OUR_ACCESS_KEY
```

> _**NOTE:** To locate the .env file, check your computer root file. For example, **C:\Users\{USER_NAME}\appwrite\.env**._

Once the environment variables are configured, save the file and head to the `appwrite` directory, then restart the Appwrite instance using `docker-compose up -d`. Then, head to the Appwrite instance server — `localhost`, where we’ll see Appwrite’s console. Create a new account and project. Let’s name this project **appwrite-s3**.


![Create a project](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1690752345864_Organizations-Appwrite+4.png)


The new project will appear on the console dashboard. Next, copy the **Project ID** and **API Endpoint**; we'll use them later.


> _**NOTE:** Don’t share your **Project ID** or **API Endpoint**._

Navigate to the **Storage** tab and create a new bucket named **pink-collage**.

![Storage tab](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1690752432250_Console-Appwrite+6.png)
![Create a bucket](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1690752497062_Storage-Appwrite.png)


After creating the bucket, go to the **Update Permissions** section on the **Settings** page. We want to assign a **Read Access** and **Write Access** with a **role: any** value. We can customize these roles later to specify who has access to read or write to our bucket. 

We also want to allow a set of file extensions. Go to the **Update Allowed File Extension** section and add any file extension format for the objects we’ll upload to our bucket.



![Update Permission](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1691014104530_Bucket-Appwrite.png)
![Allowed File Extension](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1691014144824_Bucket-Appwrite+1.png)



> _**NOTE:** Appwrite storage accepts file extension formats like jpg, png, svg, gif, html, pdf, mp4, etc._

## Initializing a Next.js project

Let’s bootstrap a new Next.js project with the following command:

```js
npx create-next-app <project-name>
```
The command above triggers a command-line interface (CLI) where we can create our Next.js application. The image below shows the configuration options the CLI provides:


![command-line interface](https://paper-attachments.dropboxusercontent.com/s_50D3638E1255964D59378DD072A77D7370181545B8E18D71709A5A1E41F0B344_1676357334328_ProductContext.js+-+furniture-store+-+Visual+Studio+Code+14_02_2023+07_24_42.png)


Navigate to the project directory and start a development server at [https://localhost:3000/](https://localhost:3000/) in our browser.

```js
cd <project-name> 
npm run dev
```

> _**NOTE:** `<project-name>` above stands for the name of our app; we can call it any name we deem fit._

### Store and retrieve a file with an S3 adapter

Next, upload a file into the Appwrite bucket. Any file uploaded to Appwrite’s bucket will automatically be uploaded to Amazon S3 bucket. To confirm, visit the Amazon S3 bucket we created earlier to see the exact files we uploaded to Appwrite.


![Uploaded file in Appwrite](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1691004926356_Bucket-Appwrite+2.png)
![Uploaded file in Amazon S3](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1691004935501_pink-collage-S3-bucket.png)

Next, create a `pages/component/ApiHelper.js` file to create an instance to interact with Appwrite services. Paste the **Project ID** and **API Endpoint** we got from Appwrite earlier on.

```js
import { Client, Storage } from "appwrite";
const client = new Client();
export const storage = new Storage(client);
client
    .setEndpoint("http://localhost/v1") // Your API Endpoint
    .setProject("OUR_PROJECT_ID"); // Your project ID
```
Next, head to the `pages/index.js` file to fetch the file we uploaded. 

```js
//pages/index.js
import Link from "next/link";
import "@appwrite.io/pink";
import { storage } from "./component/ApiHelper";
export default function Home() {
const result = storage.getFilePreview("[BUCKET_ID]", "[FILE_ID]", "[width]", "[height]");
console.log(result); // Resource URL
return (
  <main>
    <div>
      <nav className="u-flex u-cross-center u-main-space-between u-padding-16">
        <h2 className="logo u-padding-16 eyebrow-heading-1 u-color-text-pink">
         <Link href="/">PINK COLLAGE</Link>
        </h2>
        <button className="button">
          <Link href="/upload">UPLOAD AN IMAGE</Link>
        </button>
      </nav>
      <div className="article">
        <img src={result} alt="images" />
        <div className="article-container">
          <h2 className="eyebrow-heading-1">The Pink Flamingo</h2>
          <p>
            Sunt et dolore labore est voluptate laborum nisi incididunt officia qui esse ut...
          </p>
        </div>
      </div>
    </div>
  </main>
 );
}
```    

The code snippet above does the following:

- Imports the Appwrite module from `pages/component/ApiHelper.js`
- Fetches and renders the file we uploaded to the Appwrite bucket earlier on

This is what the app will look like:

![Homepage](https://paper-attachments.dropboxusercontent.com/s_5EA989E427F2300F18426CB4C7F09902F652C8A62C40F041E200C866E555F2CB_1691061560750_localhost-3000+16.png)


Next, we want to be able to upload files directly from our application. To do this, create a new file — `pages/upload.js` which will have a custom file input to upload a file of any format. The uploaded file will be in an image format.
```js
import React, { useState } from "react";
import Link from "next/link";
import "@appwrite.io/pink";
import { ID } from "appwrite";
import { storage } from "./component/ApiHelper";
    
const upload = () => {
const [upload, setUpload] = useState(null);
    
const onFileChange = (e) => {
 if (e.target.files) {
 setUpload(e.target.files[0]);
 }
};   
const fileUpload = () => {
 if (!upload) {
  return;
}
const promise = storage.createFile(
  "[BUCKET_ID]",
  ID.unique(),
  upload
);
promise.then(
 function (response) {
   console.log(response); // Success
 },
 function (error) {
   console.log(error); // Failure
  }
 );
};
return (
  <main>
    <div>
      <nav className="u-flex u-cross-center u-main-space-between u-padding-16">
        <h2 className="logo u-padding-16 eyebrow-heading-1 u-color-text-pink">
          <Link href="/">PINK COLLAGE</Link>
        </h2>
        <button className="button">
          <Link href="/">HOME</Link>
        </button>
      </nav>
      <h2 className="header eyebrow-heading-1 u-color-text-pink u-padding-16">
        Upload an image
      </h2>
      <div className="section">
        <button className="button" onClick={fileUpload}>
          Upload
        </button>
      </div>
    </div>
  </main>
 );
};
    
export default upload;
```
The code snippet above does the following:

- Creates a navbar button that redirects to the homepage 
- Creates a file input to access the file to be uploaded
- Sets up a useState variable, which retains the file
- Then, interact with Appwrite services and upload the file

The application will look like this after applying the necessary configurations:

{% embed https://www.loom.com/share/bc96e7c1b67040b78b3921d41bc9911d?sid=f9517d11-e8a9-4462-959e-8f19db5c1667 %}

## Conclusion

By leveraging the power of Appwrite's storage services, developers can easily integrate AWS S3 as a storage adapter for scalable and reliable storage of various data types. This combination allows flexibility in handling different file formats and ensures data security through AWS's robust infrastructure. With the S3 adapter, developers can use Appwrite's features while seamlessly storing and retrieving objects in AWS S3, enhancing their applications' overall performance and user experience.

## Resources
- [Appwrite Storage Documentation](https://appwrite.io/docs/client/storage?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Amazon S3 Documentation](https://aws.amazon.com/s3/?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [An Appwrite Guide — How to create an Appwrite instance locally in 3 steps](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9)
- [Appwrite Docker Installation Guide](https://appwrite.io/docs/self-hosting#installWithDocker/?utm_source=hackmamba&utm_medium=hackmamba-blog)