Build a Production-ready E-commerce Application with Remix and Appwrite
Tags: remix, tutorial, javascript, serverless
Published: 2022-12-16 13:55:42

With improvements in the technologies and financial institutions governing buying and selling, we have evolved from traditional buying and selling markets to e-commerce websites. 

[Remix](https://remix.run/) is a full-stack React framework for building web applications. It provides several helpful features such as server-side rendering, file system–based routing, TypeScript support, built-in support for cookies and sessions, and more.

## What we will be building
This post discusses building an e-commerce application that allows us to create, delete and display our products in a Remix application. A custom backend server is not required.

## Repository 
The source code can be found [here](https://github.com/Tundesamson26/product-storytelling-examples)

## Prerequisites
To get the most out of this project, the following are required:

- A basic understanding of CSS, JavaScript, and React.js
- A Docker Desktop installation. Run the `docker -v` command to verify whether Docker Desktop is installed. If not, install it from [here](https://www.docker.com/products/docker-desktop/) 
- An Appwrite instance running on our computer. Check out this [documentation](https://appwrite.io/docs/installation#installWithDocker/?utm_source=hackmamba&utm_medium=hackmamba-blog
) to create a local Appwrite instance. We will use Appwrite’s powerful database service and experience to manage our catalog

## Setting up our Remix app
First, we need to run the command below in the terminal to set up a new Remix application.

```js
npx create-remix@latest
```
The command above triggers a command-line interface (CLI) where we can configure the application. The images below show the configuration options the CLI provides:

![remix-cli](https://paper-attachments.dropboxusercontent.com/s_E867A248BE2257A3954D056214F9548F9E9D46F82F3F07FF4322CD11BF3382CC_1669155669690_image.png)

After creating our app, change the directory to the project and start a development server with the following command:

```js
cd <name of our project>
npm run dev
```
Remix will start a development environment accessible by default at http://localhost:3000.

## Installing Tailwind CSS
[Tailwind CSS](https://tailwindcss.com/) is a "utility-first" CSS framework that allows us to rapidly create user interfaces for web applications.

To install Tailwind CSS in our project, run these terminal commands.

```js
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
These commands create two files in the root directory of our project, `tailwind.config.js`, and `postcss.config.js`.

In our `tailwind.config.js`, we add the paths to all our template files with the code below.

```js
module.exports = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```
Next, update the scripts in our `package.json` file to build our development and production CSS.

```json
{
  "scripts": {
    "build": "npm run build:css && remix build",
    "build:css": "tailwindcss -m -i ./styles/app.css -o app/styles/app.css",
    "dev": "concurrently \"npm run dev:css\" \"remix dev\"",
    "dev:css": "tailwindcss -w -i ./styles/app.css -o app/styles/app.css"
  }
}
```
Next, we create a `./styles/app.css` file and add the `@tailwind` directives for each of Tailwind’s layers.

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
Lastly, we import the compiled `./app/styles/app.css` file in our `./app/root.jsx` file.

```js
import styles from "./styles/app.css"

export function links() {
  return [{ rel: "stylesheet", href: styles }]
}
```

## Installing Appwrite
[Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog
) is an open-source, end-to-end, backend server solution that allows developers to build applications faster.

To use Appwrite in our Remix application, install the Appwrite client-side SDK for web applications.

```js
npm install appwrite
```

## Creating a new Appwrite project

We go to [localhost](http://localhost/) and create a new account to see our console.

On our console, click on the **Create Project** button to start a new project.

![appwrite-create-project-console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cegevvn1tmpoz2o7d1zd.png)

After creating a project, scroll down within the Appwrite console and select **Add platform**.

![appwrite-add-platform](https://paper-attachments.dropboxusercontent.com/s_E867A248BE2257A3954D056214F9548F9E9D46F82F3F07FF4322CD11BF3382CC_1670052122947_image.png)
 
Select `Web App` and Register the Name and Hostname. Complete the registration for the Web App platform.


> In our case, we are using localhost, so we insert `*localhost`


![appwrite-web-config](https://paper-attachments.dropboxusercontent.com/s_E867A248BE2257A3954D056214F9548F9E9D46F82F3F07FF4322CD11BF3382CC_1670051948087_image.png)


Our project dashboard appears once we have created the project. At the top of the page, click the settings bar to access the Project ID.  

![appwrite-settings](https://paper-attachments.dropboxusercontent.com/s_E867A248BE2257A3954D056214F9548F9E9D46F82F3F07FF4322CD11BF3382CC_1669775734705_image.png)


Now, copy the Project ID, which we need to initialize the Appwrite Web SDK.

## Creating the collection and attributes
In the Appwrite Web [Console](http://localhost/console), click on **Database**, located on the left side of the dashboard.

![appwrite-db-console](https://paper-attachments.dropboxusercontent.com/s_E867A248BE2257A3954D056214F9548F9E9D46F82F3F07FF4322CD11BF3382CC_1669777073811_image.png)


We create a collection in our database tab by clicking the **Create Collection** button. This action redirects us to a **Permissions** page.

![appwrite-permission-tab](https://paper-attachments.dropboxusercontent.com/s_E867A248BE2257A3954D056214F9548F9E9D46F82F3F07FF4322CD11BF3382CC_1669777221083_image.png)


We'll also assign Read and Write Access to the collection.

Next, go to the attributes tab to create the properties we want a document to have. 


![appwite-attr](https://paper-attachments.dropboxusercontent.com/s_E867A248BE2257A3954D056214F9548F9E9D46F82F3F07FF4322CD11BF3382CC_1669778043923_image.png)


Create a string attribute of **productName**, an integer attribute of **productPrice**, an integer attribute of **productQuantity**, and a string attribute of **productImage**. We'll use these base attributes and can add more in the future.

## Setting up the E-commerce application

To set up our application, we’ll clone this [repository](https://github.com/Tundesamson26/product-storytelling-examples) and run the command below in the project directory:

```js
$ cd remix
$ npm install
```
This repository contains all the initial setups we'll need for our app, helping us focus on our app's main functionality.

Once the development server is running, we can view it on our browser at http://localhost:3000.


> Note: We will run into some errors if we ran the app as is.

The E-commerce app homepage should look like this:

![ecomm-homepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pai027rvoqna8lh9lvt5.png)


## Making the e-commerce interact with our database


**Creating an anonymous user session**
Appwrite requires a user to sign in before reading or writing to a database to enable safety in our application. However, they allow us to create an anonymous session that we'll use in this project.

Open `.routes/index.tsx`, located in the app folder. Paste the code below to initialize the `Appwrite` modules:

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

const client = new Client();
client
  .setEndpoint('http://localhost/v1') // Your Appwrite Endpoint
  .setProject('[PROJECT-ID]');

const account = new Account(client);
const database = new Databases(client);

account.createAnonymousSession().then(
  (response) => {
    console.log(response);
  },
  (error) => {
    console.log(error);
  }
);
```
Only signed-in users can interact with the Appwrite database, so we created an anonymous session as a workaround.


> Note: The `PROJECT_ID` resides in the Appwrite console.

**Creating database documents**
In the `app/routes/cart.tsx` file, let’s write a `handleOrders` function to create documents in our collection.

```js
const handleOrders = () => {
      let promise = database.createDocument(
        '[DATABASE-ID]',
        '[PRODUCT-COLLECTION-ID]',
         "unique()",
        {
          productName: basket.cart.name,
          productPrice: basket.cart.price,
          productQuantity: basket.cart.quantity,
          productImage: basket.cart.image
        }
      );
      promise.then(
        function (response) {
          console.log(response); // Success
          alert('order has been successfully saved');
        },
        function (error) {
          console.log(error); // Failure
        }
      );
}
```
This `handleOrders` function above does the following:

- Uses the Appwrite `createDocument()` method, which creates a document using the collection ID and data fields to be stored. This collection ID is the same ID we copied earlier.
- Alerts us when we have successfully saved our document, then clears the information in our local state variables.

Next, we pass our `handleOrders()` function into an `onClick` event-listener on our `Link` element.

```html
<Link
    to="/checkout"
    className="bg-text text-primary py-3 mt-10 rounded font-semibold text-center"
    onClick ={handleSubmit}
 >
       Checkout
</Link>
```
Click on the `Checkout` button and go to the Documents tab on Appwrite's project dashboard to see the saved documents.

**Listing documents**
To see all we've done, we need to create a function that would fetch all the orders.

In the `app/routes/cart.tsx` file, we write a `getOrders` function to list documents in our collection.

```js
const getOrders = () => {
    let promise = database.listDocuments(
        '[DATABASE-ID]',
        '[PRODUCT-COLLECTION-ID]',
    );
    promise.then(
      function (response) {
        console.log(response); // Success
        alert("orders list");
      },
      function (error) {
        console.log(error); // Failure
      }
    );
  };
  useEffect(() => {
    getOrders();
 }, []);
```
In the code snippet above, we create a `getOrders()` function to display our bookmarks. We use Appwrite's `listDocuments()` method to do this.

We pass the **DocumentID** and **collection ID** parameter to the `getOrders()` method to specify what collection we want to access.

Our `useEffect()` hook runs the `getOrders()` function.

**Deleting documents**
In the `app/routes/cart.tsx` file, let’s create a `handleDelete()` function to delete documents we no longer want in our collection.

```js
const handleDelete =  () => {
   let promise = database.deleteDocument(
    '[COLLECTION_ID]', 
    '[DOCUMENT_ID]'
   );
   promise.then(
     function (response) {
       console.log(response); // Success
       alert("order have been deleted successfully");
     },
     function (error) {
       console.log(error); // Failure
     }
   );
};
```
The `handleDelete()` function above does the following:

- Finds a document using its collection ID, and the document ID gets passed into the function.
- Deletes that document using Appwrite’s `deleteDocument()` method.
- Alerts us if we delete an item.

Finally, the production-ready E-commerce application looks like this:

{% embed
https://www.loom.com/share/35a9fe73c5b94bea8676317a170bb880 %}

## Conclusion

This article taught us how to leverage Appwrite's database features to store, list, and delete orders from an E-commerce application. This app can be the basis for a full-fledged inventory creation system for a store. Modify the documents to include handling a proper checkout flow with payment and shipping.

## Resources

- [Appwrite’s documentation](https://appwrite.io/docs/installation/?utm_source=hackmamba&utm_medium=hackmamba-blog
)
- [Remix Documentation](https://remix.run/docs/en/v1)