How to add S3 adapter to an Appwrite instance

Emmanuel Ugwu - Sep 22 '23 - - Dev Community

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. 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
  • Node.js and its package manager, npm; install here
  • Docker installed
  • Access to an Appwrite Cloud account; submit a request for Appwrite Cloud here
  • An Amazon Web Services (AWS) account; create a free account here

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

What is Amazon S3?

Amazon S3, 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, 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

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 to learn more.

Bucket configuration

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
Set of credentials

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 to style web applications.

npm install appwrite
npm install @appwrite.io/pink
Enter fullscreen mode Exit fullscreen mode

We need to create an Appwrite instance in a preferred directory by running the command below:

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
Enter fullscreen mode Exit fullscreen mode

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:

_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
Enter fullscreen mode Exit fullscreen mode

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

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
Create a bucket

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
Allowed File Extension

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:

npx create-next-app <project-name>
Enter fullscreen mode Exit fullscreen mode

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

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

cd <project-name> 
npm run dev
Enter fullscreen mode Exit fullscreen mode

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
Uploaded file in Amazon S3

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.

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
Enter fullscreen mode Exit fullscreen mode

Next, head to the pages/index.js file to fetch the file we uploaded.

//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>
 );
}
Enter fullscreen mode Exit fullscreen mode

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

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.

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;
Enter fullscreen mode Exit fullscreen mode

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:

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .