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, 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.
Prerequisites
To follow along with this tutorial, the following are required:
Creating a Next.js project
To create a Next.js project, open a terminal and enter the following command:
npx create-next-app appwrite-labels
We will then answer a series of prompts.
Next, we will go to the project directory and start the development server on localhost:3000
with the commands below.
cd appwrite-labels && npm run dev
Installing Appwrite
As we mentioned above, Appwrite 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:
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.
Next, we will create a new project. We‘ll name our project Car-Gallery-Subscription-Service
.
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.
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.
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.
/** @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.
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 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.
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.
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.
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.
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.
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.
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:
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:
/** @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.
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:
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.
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.
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:
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:
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:
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
anderrorCode
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 theerrorCode
state to the error code of the response. -
Lines 36-41: We iterate through our
imageFiles
and use thegetFilePreview
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.
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.
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.
When we return to our application, the user should be able to view the car images now.
For further development, the 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.
Top comments (0)