How to use labels in Appwrite to create a subscription system
Tags: appwrite, subscription, label, nextjs
Published: 2023-11-06 21:50:33

A subscription system is the process by which the customer pays a recurring price to access a product or service. The two most common categories of subscriptions are as follows.

- A subscription for unlimited use of a service or collection of services. The usage may be personal, for a family, or a group utilizing the service simultaneously. Examples of this subscription model are Netflix, Apple Music, and Spotify.
- A subscription for basic usage or limited access, which is mostly free, and a charge is paid to have unlimited access to the resources provided by that service. An example of this model is Medium.

With a subscription system, specific data and images are hidden from users until they subscribe.

[Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog), a powerful backend-as-a-service (BaaS) platform that provides APIs for building web and mobile applications, offers the capability of easily managing a user’s access to specific resources. This is done using **Labels**. For example, a user can be assigned a `subscriber` label when subscribing to a service. 

In this article, we will learn how to use Appwrite labels to manage a user’s access to certain resources. 


## GitHub

Check out the source code [here](https://github.com/obinnadtq/appwrite-labels).

## Prerequisites

To follow along with this tutorial, the following are required:

- A basic understanding of JavaScript
- Familiarity with [Next.js](https://nextjs.org/)
- An [Appw](https://cloud.appwrite.io/login?utm_source=hackmamba&utm_medium=hackmamba-blog)[r](https://cloud.appwrite.io/login?utm_source=hackmamba&utm_medium=hackmamba-blog)[ite Cloud](https://cloud.appwrite.io/login?utm_source=hackmamba&utm_medium=hackmamba-blog) account

## Creating a Next.js project

To create a Next.js project, open a terminal and enter the following command:

```powershell
npx create-next-app appwrite-labels
```

We will then answer a series of prompts.

![Creating a Next.js app](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1694550239853_Screenshot+2023-09-12+at+22.22.08.png)


Next, we will go to the project directory and start the development server on `localhost:3000` with the commands below.

```powershell
cd appwrite-labels && npm run dev
```
## Installing Appwrite

As we mentioned above, [Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) is a BaaS platform that provides APIs for building web and mobile applications.

To use Appwrite in our application, we will install the Appwrite client-side SDK for web applications using the following command:

```powershell
npm install appwrite
```
## Creating an Appwrite project

To create a new Appwrite project, we will sign in to the Appwrite cloud using our sign in credentials.

![Appwrite Cloud sign in page](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1695311905342_image.png)


Next, we will create a new project. We‘ll name our project `Car-Gallery-Subscription-Service`.

![Creating an Appwrite Project](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698176416772_image.png)


After the creation of the project, we will copy the `Project ID` and `API Endpoint` from the Settings tab, as we will need them in the next step.

![Appwrite Project credentials](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698180954699_image.png)

## Setting up the Appwrite SDK

We will create a `.env.local` file in our project root directory for our environment variables. We will add the following entries to our `.env.local` file.

```javascript
PROJECT_ID=<APPWRITE_PROJECT_ID> //Your Appwrite Project ID
ENDPOINT=<APPWRITE_ENDPOINT> //Your Appwrite Cloud endpoint
```

The values of the environment variables were obtained in the previous step when we created our Appwrite project. To access the environment variables within our application, we will modify the `next.config.js` file. The file should look like this.

```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
    env: {
        PROJECT_ID: process.env.PROJECT_ID,
        ENDPOINT: process.env.ENDPOINT
    }
}
module.exports = nextConfig
```

Next, we will create a file `src/appwrite.ts` to abstract our Appwrite SDK calls. 

```javascript
import { Account, Client } from "appwrite";

const client = new Client();

client
  .setEndpoint(process.env.ENDPOINT!)
  .setProject(process.env.PROJECT_ID!)

export const account = new Account(client);
```

The code above imports the **Client** and **Account** objects from Appwrite. Then, we create an instance of the **Client** object. We set our `Endpoint`,  `Project ID` that we obtained from the previous step into our client instance. 


## Appwrite Labels

[Appwrite Labels](https://appwrite.io/docs/products/auth/labels) are used to categorize users to grant them access to certain resources. In this tutorial, we will restrict some car images from the user until they have the `subscriber` label.

First, we will use Appwrite Storage to store our images. To create a storage, click on the **Storage** tab in the Appwrite project.

![Creating a Storage](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698183131093_image.png)


Then, we will click the **Create bucket** button to create a bucket. We will name our bucket `car-images`. We will need the **Bucket ID** in a later step. 


![Create a Bucket](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698183435286_image.png)


After creating our bucket, we will go ahead to create files in the bucket. To create a file, we will click the **Create file** button and then upload our image files.

![Creating a file](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698184272565_image.png)


After uploading all our image files, we will set permissions on our Bucket and files. To do so, we will click on the **Settings** tab of our Bucket. Then, click the `+` button in the Permissions section to add a Label.

![Creating a Label](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698185364128_image.png)


We click on **Label** to create a label. We name our label `subscriber` so we grant only users that have the `subscriber` label access to our car images.

![Creating a Label](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698185912934_image.png)


Then, we click on the **Add** button to add the label.

Next, we update the permissions we want our users to have to our resources.  We only want our users to have `Read` permissions to read the car images.

![Updating permissions](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698186490137_image.png)


We now update our `env.local` and `next.config.js` files with the environment variable for our **Bucket ID.** The `env.local` file should look like this:

```powershell
PROJECT_ID=<APPWRITE_PROJECT_ID> //Your Appwrite Project ID
ENDPOINT=<APPWRITE_ENDPOINT> //Your Appwrite Cloud endpoint
BUCKET_ID=<STORAGE_BUCKET_ID> //Your Bucket ID
```

The `next.config.js` file should look like this:

```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  env: {
    PROJECT_ID: process.env.PROJECT_ID,
    ENDPOINT: process.env.ENDPOINT,
    BUCKET_ID: process.env.BUCKET_ID,
  }
}
module.exports = nextConfig
```

## Building our user interface

Our application has two pages: the home page, which all users can access, and the gallery page, which only subscribers can access.

First, we modify the file `src/pages/index.tsx`, our home page. The file's content at this time is just a welcome message styled using Tailwind CSS.

```javascript
export default function Home() {
  return (
    <div className="flex h-full flex-col justify-center items-center">
    <h1 className="text-4xl mb-5 font-bold"> Welcome to the Car Gallery Application</h1>
    </div>
  )
}
```    

Next, we create a new file, `src/pages/gallery/index.tsx`, which is the page displaying the different car images. The file should look like this:

```javascript
export default function Gallery() {
  return (
    <div className="flex h-full flex-col justify-center items-center">
      <p className="text-2xl mb-5 font-bold"> You are not subscribed</p>
    </div>
  )
}
```   

We create a nav bar to navigate between the two pages. To do this, we modify the `src/components/Layout.tsx` file. 

```javascript
import Link from 'next/link';
import { usePathname } from 'next/navigation';

const menuItems = [
  {
    href: '/',
    title: 'Home',
  },
  {
    href: '/gallery',
    title: 'Gallery',
  }
];

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const path = usePathname();
  return (
    <div className='min-h-screen flex flex-col'>
      <div className='flex flex-col md:flex-row flex-1'>
        <aside className="bg-gray-300 w-full md:w-60">
          <nav>
            <ul>
              {menuItems.map(({ href, title }) => (
                <li className='m-2' key={title}>
                  <Link href={href} className={'flex p-2 bg-fuchsia-50 rounded hover:bg-fuchsia-200 cursor-pointer  
                    ${path === href && 'bg-fuchsia-300 text-white'}'}>
                    {title}
                  </Link>
                </li>
              ))}
            </ul>
          </nav>
        </aside>
        <main className="flex-1">{children}</main>
      </div>
    </div>
  )
}
``` 

Next, we modify the `src/pages/_app.tsx` to apply the layout to all pages in the application.

```javascript
import Layout from '@/components/Layout'
import '@/styles/globals.css'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}
``` 

At this point, our UI looks like this:

![The user interface](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1695065401003_image.png)


We update the `src/appwrite.ts` file. We import the **Storage** object from Appwrite to access the files in our bucket. The file should look like this:

```javascript
import { Account, Client, Storage } from "appwrite";
const client = new Client();

client
  .setEndpoint(process.env.ENDPOINT!)
  .setProject(process.env.PROJECT_ID!)

export const storage = new Storage(client);
export const account = new Account(client);
```

We update the `src/pages/gallery/index.tsx` file. The file should look like this at this point:

```javascript
import { account, storage } from "@/appwrite";
import { AppwriteException, Models } from "appwrite";
import React, { useEffect, useState } from "react";

export default function Gallery() {
  const [imageFiles, setImageFiles] = useState<Models.File[]>([]);
  const [errorCode, setErrorCode] = useState<number>();

  useEffect(() => {
    (async () => {
      try {
        await account.get();
      }
      catch (e) {
        await account.createAnonymousSession();
      }
    })()
  }, [])

  useEffect(() => {
    const getCarImages = async () => {
      try {
        const fileList = await storage.listFiles(process.env.BUCKET_ID!);;
        setImageFiles(fileList.files);
      }
      catch (error: any) {
        if (error instanceof AppwriteException) {
          setErrorCode(error.code);
        }
        console.log(error);
      }
    }
    getCarImages();
  }, [])

  const images = imageFiles?.map(x => {
    const imagePreview = storage.getFilePreview(x.bucketId, x.$id);
    return (
      <li key={imagePreview.href} className="mt-4"><img src={imagePreview.href} width="256" className="h-40" /></li>
    )
  });

  return (
    <>
      {images &&
        <ul className="flex flex-wrap">{images}</ul>
      }
      {errorCode === 401 &&
        (<div className="flex h-full flex-col justify-center items-center">
          <h1 className="text-4xl mb-5 font-bold"> You are not subscribed</h1>
        </div>
        )}
    </>
  )
}
```    

The code above does the following: 

- **Lines 6 - 7**: We initialize the `imageFiles` and `errorCode` states of our component using React’s **useState** hook.
- **Lines 9 - 18**: We use the **useEffect** hook to get the active Appwrite user session and if there is none, we create an anonymous session.
- **Lines 20-33**: We use the **useEffect** hook to fetch the image files in our Appwrite bucket and set the `imageFiles` state to the response. If we have an error, we check if the error is an instance of AppwriteException and set the `errorCode` state to the error code of the response.
- **Lines 36-41**: We iterate through our `imageFiles` and use the `getFilePreview` method of the Storage object to preview our image files. We append each file as a list item to be displayed on the screen.
- **Lines 43-54:** We return the images as an unordered list or a text saying the user is not subscribed if the error code is 401 i.e. the user is not authorized to access the resources.

Now, we run our application and click on the `Gallery` menu. We see the text `You are not subscribed` as shown below.

![Not subscribed](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698346304058_image.png)


This is because we specified that only a user who has the label `subscriber` can access our cars images and our current user does not have that. 

To assign the user a label, we click on the **Auth** menu in our Appwrite project and then click on the particular user we want to assign the label. 

![Assigning a label](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698346506446_image.png)


When we click on the particular user, on the Labels section, we assign the user the `subscriber` label to access our resource and then click the **Update** button.

![Assigning user a label](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698346669793_image.png)


When we return to our application, the user should be able to view the car images now.


![Car Images](https://paper-attachments.dropboxusercontent.com/s_C2DEB1DAA6617C5A02B3C4519992687B98288EA9CAC2ABF39953ACA273402AA8_1698346827701_image.png)


For further development, the [labels](https://appwrite.io/docs/products/auth/labels) can be assigned through a different subscription workflow which, in turn, assigns the correct permissions to a user. 

## Conclusion

In this tutorial, we used Appwrite **Labels** with a Next.js application to restrict user access to images until they are assigned the label that can access the resource, in this case `subscriber` label. Appwrite **Labels** are used to create a subscription system and can be extended to other use cases where we want to limit access to specific resources in an application.

## Resources
- [Appwrite Labels](https://appwrite.io/docs/authentication-management#labels)
- [Appwrite Cloud](https://cloud.appwrite.io/login?utm_source=hackmamba&utm_medium=hackmamba-blog)