Build a serverless subscription system with Stripe and Appwrite for premium user roles in Nuxt
Tags: nuxt, appwrite, stripe, serverlessfunction
Published: 2023-02-15 14:04:30

---
title: Build a serverless subscription system with Stripe and Appwrite for premium user roles in Nuxt
published: true
description: Learn how to build a metric tracker with premium features in a Nuxt.js application with Appwrite’s serverless function and Stripe
tags: #Nuxt #Appwrite #Stripe #ServerlessFunction
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7v2plaec00ac8hmtyuwv.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-02-15 13:27 +0000
---

Serverless functions, lambda functions, functions as a service (FAAS), and serverless frameworks, among others, are some innovations leveraging serverless architecture. Such an architecture allows developers to build robust applications with improved resilience, reduced cost, and unburdened with the overhead of maintaining servers.

In this post, we will learn how to build a metric tracker with premium features in a Nuxt.js application with Appwrite’s serverless function and Stripe.


## GitHub Links

The project source codes are below:

- [Appwrite function with Node.js](https://github.com/Mr-Malomz/metric-stripe-function)
- [Nuxt app](https://github.com/Mr-Malomz/metric-tracker)


## Prerequisites

To fully grasp the concepts presented in this tutorial, the following are required:

- Basic understanding of JavaScript and Vue.js
- Docker [installation](https://www.docker.com/get-started/)
- An Appwrite instance; check out this [article](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) on how to set up an instance or install with one-click on [DigitalOcean](https://marketplace.digitalocean.com/apps/appwrite) or [Gitpod](https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod)
- [Appwrite CLI](https://appwrite.io/docs/command-line?utm_source=hackmamba&utm_medium=hackmamba-blog) installed
- A Stripe account ([sign up](https://dashboard.stripe.com/register) for a trial account, it is completely free)


## Set up a Stripe account to process payments

To get started, we need to log into our Stripe account to get our **Secret Key**. The **Secret Key** will come in handy for processing payments in our application. To do this, navigate to the **Developers** tab and copy the **Secret Key**.

![Stripe overview](https://res.cloudinary.com/dtgbzmpca/image/upload/v1676469121/stripe.png)




## Integrate Appwrite function with Stripe

By default, Appwrite supports Node.js as a runtime for creating serverless functions. To get started, we need to log into our Appwrite console, click the **Create project** button, input `metric-tracker` as the name, and then click **Create**.

![Create project](https://res.cloudinary.com/dtgbzmpca/image/upload/v1676469121/appwrite.png)


**Initialize function directory**
With our project created on Appwrite, we can now create a directory that our function will use to process payments using Stripe. To do this, we first need to navigate to the desired directory and run the command below:

```bash
mkdir metric-stripe-function && cd metric-stripe-function
```

The command creates a project folder called `metric-stripe-function` and navigates into this folder.

Secondly, we need to log in to the Appwrite server using the command-line interface (CLI).

```bash
appwrite login
```

We will be prompted to input an `email` and `password`, which need to be the credentials we used to sign up for the Appwrite console.

Lastly, we need to link our function directory to the Appwrite project created earlier by running the command below:

```bash
appwrite init project
```

We will be prompted with some questions on how to set up the project, and we can answer as shown below:

```bash
How would you like to start? <select "Link this directory to an existing Appwrite project">
Choose your Appwrite project <select "metric-tracker">
```

**Create Appwrite function inside the project**
With our project succesfully set up, we can now proceed to creating a function by running the command below:

```bash
appwrite init function
```

We will also be prompted with some questions about how to set up our function; we can answer as shown below:

```bash
What would you like to name your function? <input "metric-stripe-function">
What ID would you like to have for your function? (unique()) <press enter>
What runtime would you like to use? <scroll to node-18.0 and press enter>
```

The command will create a starter Node.js project.

![Starter project](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674950130815_Screenshot+2023-01-29+at+00.55.20.png)


Secondly, we need to install the required dependency by running the command below:

```bash
cd functions/metric-stripe-function 
npm i axios
```


Thirdly, we need to modify the `index.js` file inside the `src` folder as shown below:

```js
const axios = require('axios');

module.exports = async function (req, res) {
    const data = {
        amount: 100,
        currency: 'usd',
        payment_method: 'pm_card_visa',
    };

    const body = `amount=${data.amount}&currency=${data.currency}&payment_method=${data.payment_method}`;

    await axios
        .post('https://api.stripe.com/v1/payment_intents', body, {
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            auth: {
                username: 'REPLACE WITH STRIPE SECRET KEY',
            },
        })
        .then((_) =>
            res.json({
                status: 200,
                message: 'Subscription processed successfully',
            })
        )
        .catch((_) =>
            res.json({
                status: 500,
                message: 'Error processing subscription',
            })
        );
};
```

The snippet above does the following:

- Imports the required dependency
- **Lines 4-10** create an API body that consists of `amount`, `currency`, and `payment_method` of type card and using a test card (**pm_card_visa**)
- **Lines 12-30** make an API call to Stripe by passing in the `body`, set the authentication using the **Secret Key**, and return the appropriate response

**Note**: We hardcoded the amount, currency, and payment method in this post. However, Stripe supports multiple payment methods and options we can adopt in a production environment.

Lastly, we must navigate to the project terminal and deploy our function.

```bash
cd ../..
appwrite deploy function
```

We will also be prompted about the function we would like to deploy. Select the `metric-stripe-function` function by pressing the **spacebar key** to select and the **enter key** to confirm the selection.

![Select function to deploy](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674950706091_Screenshot+2023-01-28+at+11.24.26.png)

![Sample of a deployed function](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674950714318_Screenshot+2023-01-28+at+11.25.00.png)


We can also confirm the deployment by navigating to the **Function** tab on the Appwrite console.

![Deployed function](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674950743218_Screenshot+2023-01-28+at+11.37.11.png)


Lastly, we must update the deployed function permission since we need to call it from our Nuxt application. To do so, navigate to the **Settings** tab, scroll to the **Execute Access** section, select `Any` and click **Update**.

![Update permission](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674950833310_Screenshot+2023-01-28+at+18.14.46.png)



## Create a Nuxt app and set up database

With our function deployed and ready to accept payment via Stripe, we can now set up a Nuxt project. To get started, we need to clone the project by navigating to the desired directory and running the command below:

```bash
git clone https://github.com/Mr-Malomz/metric-tracker.git && cd metric-tracker
```

**Setting up the project**
First, we need to add Appwrite as a dependency by running the command below:

```bash
npm i appwrite && npm i
```

Then, we can run our project using the command below:

```bash
npm run dev
```


![Running app (home page)](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674951702697_Screenshot+2023-01-28+at+00.49.17.png)
![Running app (metric page)](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674951702685_Screenshot+2023-01-28+at+00.49.08.png)


**Create a database and add sample data**
First, we need to create a database with the corresponding collection, attributes, document, and add sample data as shown below:

| **name**      | **isSubscribed** |
| ------------- | ---------------- |
| John Travolta | false            |

![Collection](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674951948912_Screenshot+2023-01-28+at+18.08.14.png)


Lastly, we need to update our collection permission to manage them accordingly. To do this, navigate to the **Settings** tab, scroll down to the **Update Permissions** section, select `Any`, mark accordingly, and **Update**.

![Select Any and update](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674952004861_Screenshot+2023-01-28+at+18.13.06.png)




## Building the metric tracker with subscription system

To get started, we first need to create a `components/utils.js` to abstract the application logic from the UI and add the snippet below:

```js
import { Client, Databases, Account, Functions } from 'appwrite';

const PROJECT_ID = 'REPLACE WITH PROJECT ID';
const DATABASE_ID = 'REPLACE WITH DATABASE ID';
const COLLECTION_ID = 'REPLACE WITH COLLECTION ID';
const FUNCTION_ID = 'REPLACE WITH FUNCTION ID';
const DOCUMENT_ID = 'REPLACE WITH DOCUMENT ID';

const client = new Client();

const databases = new Databases(client);

client.setEndpoint('http://localhost/v1').setProject(PROJECT_ID);

export const account = new Account(client);

export const getUserDetails = () =>
    databases.getDocument(DATABASE_ID, COLLECTION_ID, DOCUMENT_ID);

export const createSubscription = () => {
    const functions = new Functions(client);
    return functions.createExecution(FUNCTION_ID);
};

export const updateUserSubscription = (name, isSubscribed) => {
    const data = {
        name,
        isSubscribed,
    };
    return databases.updateDocument(
        DATABASE_ID,
        COLLECTION_ID,
        DOCUMENT_ID,
        data
    );
};
```

The snippet above does the following:

- Imports the required dependency
- Initializes Appwrite `client` and `databases` with required arguments
- Creates `account`, `getUserDetails`, `createSubscription`, and `updateUserSubscription` functions for managing user sessions, obtaining user details, creating the subscription, and updating user subscription status 

**PS**: *We can get the required IDs on our Appwrite Console.*

Secondly, we need to update the `app.vue` file to use the `account` helper function to check if a user has a valid session.

```vue
<template>
  <div class="min-h-screen bg-brand-bg lg:flex lg:justify-between">
    <Sidebar />
    <section class="w-full h-auto px-4 py-2 lg:px-8">
      <NuxtPage></NuxtPage>
    </section>
  </div>
</template>

<script setup>
import { account } from "./components/utils";

//check session
onMounted(async () => {
  account
    .get()
    .then()
    .catch((_) => account.createAnonymousSession());
});
</script>
```

Thirdly, we need to update the `metrics.vue` file in the pages folder as shown below:

`metrics.vue` Logic

```vue
<script setup>
import { getUserDetails } from "../components/utils";

//state
const user = ref(null);

//get user details
onMounted(async () => {
  getUserDetails()
    .then((res) => {
      user.value = res;
    })
    .catch((_) => {
      alert("Error loading user details");
    });
});
</script>
```

The snippet above does the following:

- Imports the required dependency
- Creates application state and retrieves user details upon page load using the `getUserDetails` helper function

`metrics.vue` UI

```vue
<template>
  <div class="mt-20">
    <section class="flex items-center mb-10">
      <h1 class="text-3xl font-bold inline-block mr-4">Metrics</h1>
      <p
        class="
          px-2
          text-sm
          bg-indigo-900 bg-opacity-30
          text-indigo-900
          rounded-full
        "
      >
        premium
      </p>
    </section>
    <section class="flex" v-if="user?.isSubscribed">
      <div
        class="
          border
          w-full
          flex
          justify-center
          flex-col
          items-center
          h-96
          mr-2
          rounded-md
        "
      >
        <p class="text-gray-500 mb-4">Daily insight</p>
        <h1 class="text-4xl font-bold">456367</h1>
      </div>
      <div
        class="
          border
          w-full
          flex
          justify-center
          flex-col
          items-center
          h-96
          mr-2
          rounded-md
        "
      >
        <p class="text-gray-500 mb-4">Total insight</p>
        <h1 class="text-4xl font-bold">10956367</h1>
      </div>
    </section>
    <Locked v-else :name="user?.name" />
  </div>
</template>
```

The snippet above does the following:

- Conditionally shows premium metrics using the user’s subscription status
- Updates the `Locked` component to receive `name` props

Lastly, we need to update the `Locked.vue` file in the `components` folder as shown below:

```vue
<template>
  <div class="flex w-full flex-col items-center justify-center h-96">
    <p class="text-center justify-center mb-4">
      Oops! Looks like you don't have access to this page
    </p>
    <p class="text-center justify-center mb-4">
      Get full access for by subscribing
    </p>
    <button
      class="px-8 py-2 bg-indigo-800 text-white rounded hover:bg-indigo-700"
      @click="onSubmit"
    >
      Upgrade to paid
    </button>
  </div>
</template>

<script setup>
import {
  createSubscription,
  updateUserSubscription,
} from "../components/utils";
const props = defineProps(["name"]);

const onSubmit = () => {
  createSubscription()
    .then((_) => {
      updateUserSubscription(props.name, true)
        .then((_) => {
          alert("Subscription created successfully!");
          window.location.reload();
        })
        .catch((_) => {
          alert("Error subscribing user!");
        });
    })
    .catch((_) => {
      alert("Error subscribing user!");
    });
};
</script>
```

The snippet above does the following:

- Imports the required dependency
- Defines the expected component props
- Creates an `onSubmit` function that uses the `createSubscription` and `updateUserSubscription` helper functions to manage subscription
- Updates the UI to use the `onSubmit` function


With that done, we can restart a development server using the command below:

```bash
npm run dev
```

![](https://media.giphy.com/media/dGd4daA38V4GWjeRg2/giphy.gif)


[https://media.giphy.com/media/dGd4daA38V4GWjeRg2/giphy.gif](https://media.giphy.com/media/dGd4daA38V4GWjeRg2/giphy.gif)

We can validate the subscription by checking the Appwrite function **Executions** tab and Stripe **Log** tab.

![Appwrite Execution tab](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674954092961_Screenshot+2023-01-28+at+17.19.04.png)
![Stripe Log tab](https://paper-attachments.dropboxusercontent.com/s_78D3AEF08E035CCEA99A8FBBCBD8B7DF8156025DF5268F616A911A80C85217A8_1674954113489_Screenshot+2023-01-28+at+17.19.58.png)



# Conclusion

This post discussed how to build a metric tracker with protected premium features using Appwrite and Stripe. The demo is a base implementation demonstrating Appwrite's support for building serverless architecture. Appwrite gives developers the magic wand to build medium- to large-scale applications at a reduced cost and excellent developer experience.

These resources may also be helpful:

- [Appwrite official documentation](https://appwrite.io/docs?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Appwrite Node SDK](https://github.com/appwrite/sdk-for-web?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Appwrite Function](https://appwrite.io/docs/functions?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Stripe payment intent](https://stripe.com/docs/payments/payment-intents)
- [Stripe payment guide](https://stripe.com/docs/payments)
- [Nuxt 3 official documentation](https://nuxt.com/docs)