How to Create an E-commerce Product Catalog in NextJS
Tags: javascript, nextjs, webdev, react
Published: 2022-04-05 18:17:52

Transactions have been a universal concept as old as time. With improvements in the technologies and financial institutions governing buying and selling, we have evolved from the traditional markets of buying and selling to e-commerce websites. 

## What we will be building

This post will discuss building a product catalog that allows us to create, delete and display our products in a NextJS application. We do not require a custom backend server.

**GitHub URL** 


https://github.com/Iheanacho-ai/appwrite-product-catalog

## Prerequisites 

To get the most out of this project, we require the following:

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


## Setting up our Next.js app

[Next.js](https://nextjs.org/) is an open-source React framework that enables us to build server-side rendered static web applications.

To create our Next.js app, we navigate to our preferred directory and run the terminal command below: 

```bash
    npx create-next-app@latest
    # or
    yarn create next-app
```

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

```bash
    cd <name of our project>
    npm run dev
```

To see our app, we go to [http://localhost:3000](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, we run these terminal commands.

```bash
    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 this code below.

```js
    module.exports = {
      content: [
        "./pages/**/*.{js,ts,jsx,tsx}",
        "./components/**/*.{js,ts,jsx,tsx}",
      ],
      theme: {
        extend: {},
      },
      plugins: [],
    }
```
Next, we add the tailwind directives in our `styles/global.css` file.

```css
    @tailwind base;
    @tailwind components;
    @tailwind utilities;

```
## Installing Appwrite

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

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

```bash
    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, there is a **Create Project** button. Click on it to start a new project.

![Appwrite E-commerce Catalog Console Index ](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1648400276700_Screenshot+64.png)


Our project dashboard appears once we have created the project. At the top of the page, there is a **settings** bar. Click it to access the **Project ID** and **API Endpoint**.  


![Appwrite E-commerce Catalog Console Settings Page](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1648410772373_Screenshot+73-2.png)


We copy the **Project ID** and **API Endpoint**, which we need to initialize the Appwrite Web SDK.

![Appwrite E-commerce Catalog Settings Page](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1648400551957_Screenshot+74.png)


In our `index.js` file, we initialize a new Appwrite instance with the following.

```js
    import {Appwrite} from "appwrite";
    import {useEffect, useState } from "react";
    
    const Home = () => {
    
        // Init our Web SDK
        const sdk = new Appwrite();
        sdk
        .setEndpoint('http://localhost/v1') // our API Endpoint
        .setProject(projectID) // our project ID
        ;
    
        return(
          <div>Hello World!</div>
        )
    }
    
    export default Home;
```

**Creating the collection and attributes**

In the Appwrite web Console, on the left side of the dashboard, we click on Database.

![Appwrite E-commerce Catalog Console Database Page ](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1648400364070_Screenshot+72.png)


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

At the **Collection Level**, we want to assign a **Read Access** and **Write Access** with a **role:all** value. You can modify this customize roles and access to your database.


![Appwrite E-commerce Catalog Console Permissions Page ](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1648400773634_Screenshot+75.png)


On the right of the **Permissions page**, we copy the **Collection ID**, which we need to perform operations on collection’s documents.

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


![Appwrite E-commerce Catalog Console Attributes Page ](https://paper-attachments.dropbox.com/s_3246DA27AE45A86F6F25AA1081C5093568E7040F843BFEDFC885B2DBE73C8346_1647778026866_Screenshot+27.png)


We create a string attribute of **productName**, an integer attribute of **productPrice**, and a string attribute of **productImage**. We use these base attributes and could add more in the future.


![Appwrite E-commerce Catalog Console Attributes Page ](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1648400867424_Screenshot+76.png)

## Creating the Product catalog.

In the `index.js` file, we create our product catalog page, which will be divided into two sections. The first section will contain a form to collect product information and the second section to list the products in our database.

In this section of the article, we will work on the form. To create the form styled with Tailwind CSS, we add the following code snippet in the `index.js` file.


https://gist.github.com/Iheanacho-ai/0e0d68b6f8d17cfeddccbfc4b25c5f72


Next, we augment the styles with the following in the `global.css` file.

```css
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    
    .product-container{
      margin-left: 37%;
      width: 30%;
    }
    .cursor{
      cursor: pointer;
    }
  ```  

Here is what our form looks like. 

![Appwrite E-commerce Catalog Form](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1647968155403_Screenshot+41.png)



## Adding form interaction with the database

Appwrite has a safety policy that allows only signed users to read and write to the database. However, it allows us to create an anonymous session, which we will use in this project.

In our `index.js` file, we create our anonymous user session using Appwrite's `createAnonymousSession` method.

```js
    import {Appwrite} from "appwrite";
    import {useEffect, useState } from "react";
    
    const Home = () => {
    
        // Init our Web SDK
        const sdk = new Appwrite();
        sdk
        .setEndpoint('http://localhost/v1') // our API Endpoint
        .setProject(projectID) // our project ID
        ;
    
        //creating an anonymous Session
        
        const createAnonymousSession = async() => {
          try{
            await sdk.account.createAnonymousSession();
          }catch(err){
            console.log(err)
          }
            
        }
        useEffect(()=> {
          createAnonymousSession()
        }, [])
    
        return(
          <div>Hello World!</div>
        )
    }
    
    export default Home;
    
```    

**Creating state variables to hold our form values**

In the `index.js` file, we create state variables that will hold form input values. 

In addition to the form input state variables, we will create a `productList` variable used later in this tutorial. This `productList` variable will hold the products we get from our database.

```js

    const [productName, setProductName] = useState('');
    const [productPrice, setProductPrice] = useState('');
    const [productImage, setproductImage] = useState('');
    const [productList, setProductList] = useState([]);
```

Next, in our `index.js` file, we pass the state variables as our input field values.



https://gist.github.com/Iheanacho-ai/51f4f3ef58ab56ef22026639a0514258

In the code block above, we do the following:

- Pass the state variables as values to their respective input fields.
- Use the `onChange` event listener to update the state variables when users type in the input fields.

**Creating Appwrite database documents**

In our `index.js` file, we create our `handleProductCatalog` function to add new documents to the database collection.

```js

    //creates the documents and runs listProducts
    
    const handleProductCatalog = async () => {
      try{
        let promise = await sdk.database.createDocument(collectionID, 'unique()', {
          "productName" : productName,
          "productPrice": productPrice,
          "productImage": productImage
        });
        
        setProductName('');
        setProductPrice('');
        setproductImage('');
      
        alert('your job item has been successfully saved')
    
      }catch(error){
        console.log(error)
      }   
    }
```

In the code block above, our `handleProductCatalog` function does the following:

- Create a new document using Appwrite’s `createDocument()` function, while passing the collection ID and attribute values as parameters.
- Alert us when we have successfully saved the document, then clears the information in the local state variables.

Next, we pass our `handleProductCatalog` to an `onClick` event listener on our `button` element.

```jsx

    <button type= "button" className="cursor inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" onClick={handleProductCatalog}>
      Save
    </button>

```

**NOTE: We use a button with a `type= button` to override the button’s default submit behavior.**

Here's how our `index.js` file should look.

https://gist.github.com/Iheanacho-ai/918a519813cf7fa14fa7b2f8753ac15c

For your product catalog images, you can use any image link. In this tutorial, however, we use images from Cloudinary as it is easier to apply transformations and optimize delivery. 
To understand how to upload images to Cloudinary, check out the [Cloudinary documentation](https://cloudinary.com/blog/automating_file_upload_and_sharing?utm_source=appwrite&utm_medium=affiliate&utm_term=hackmamba).

Fill out the form, and go to the Documents section of our database to see our saved documents.


![Appwrite E-commerce Catalog Console Documents Page](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1648411801791_Screenshot+77.png)


## Creating our Product Listing Page

Our page displays the product information we entered in our form. With this logic, we want our `handleProductCatalog` function to be responsible for creating the documents to display our product.

In our `index.js` file, we create a `listProducts` function that gets called when we mount our app and when our `handleProductCatalog` function runs.

```jsx

    //lists our products
    
    const listProducts = async () => {
      try {
        let response = await sdk.database.listDocuments(collectionID);
        setProductList(response.documents)
      } catch (error) {
        console.log(error)
        } 
      }
    
    useEffect(()=> {
        createAnonymousSession(),
    
    // runs list product function when our app mounts
    
        listProducts()
      }, [])
    
    
    const handleProductCatalog = async () => {
      try{
        let promise = await sdk.database.createDocument(collectionID, 'unique()', {
          "productName" : productName,
          "productPrice": productPrice,
          "productImage": productImage
        });
        
        setProductName('');
        setProductPrice('');
        setproductImage('');
      
        alert('your job item has been successfully saved')
      
    // runs our listProducts function
    
        listProducts()
      }catch(error){
        console.log(error)
      }  
    }

```

The `listProducts` function uses the Appwrite `listDocuments` API that receives a collection ID parameter. The`listProducts` function finds the collection with that ID and updates the `productList` variable with the products from that collection.

**Deleting products from our database**

Next, in our `index.js`  file we create a `handleDelete`  function, to handle deletion of products we do not need anymore in our collection or database.


```jsx
    const handleDelete = async (documentid) => {
      try {
        await sdk.database.deleteDocument('624098515dbd1ae68ea2', documentid);
        alert("item have been deleted successfully")
        listProducts()
        
      } catch (error) {
        console.log(error)
      }
    }

```

The `handleDelete` function does the following:

- Finds a document using its collection ID.
- Deletes that document using Appwrite `deleteDocument()` function.
- Alerts us if the item was successfully deleted.
- Runs the `listProducts` function to display our updated product list.

**Creating our product listing UI**

Next, we display the products on our product listing page. We paste this code into our `index.js` file to do this.

```jsx
    <div className="bg-white">
      <div className="max-w-2xl mx-auto py-16 px-4 sm:py-24 sm:px-6 lg:max-w-7xl lg:px-8">
        <h2 className="sr-only">Products</h2>
        {
          productList ? (
            <div className="grid grid-cols-1 gap-y-10 sm:grid-cols-2 gap-x-6 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
              {
                productList.map(({productName, productImage, productPrice, $id}) => (
                  <a href="#" className="group" id={$id}>
                    <div className="w-full aspect-w-1 aspect-h-1 bg-gray-200 rounded-lg overflow-hidden xl:aspect-w-7 xl:aspect-h-8">
                      <img src={productImage} alt="Tall slender porcelain bottle with natural clay textured body and cork stopper." className="w-full h-full object-center object-cover group-hover:opacity-75" />
                    </div>
                    <h3 className="mt-4 text-sm text-gray-700">{productName}</h3>
                    <p className="mt-1 text-lg font-medium text-gray-900">${productPrice}</p>
                    <button
                      type="button"
                      className="cursor inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
                      onClick={()=> handleDelete($id)}
                    >
                    Delete
                  </button>
                  </a>
                ))
              }
            </div>
          ) : null
        }
      </div>
    </div>

```
In the code block above, we:  

- Check if the `productList` variable is not null.
- Loop through the `productList` variable to render each product.
- Destructure and pass in our `productName`, `productPrice`, `productImage` and `$id` variables.
- Pass the `handleDelete` function we created to the `onClick` event listener of our `button`.

 Here is what our complete `index.js` file looks like
 

https://gist.github.com/Iheanacho-ai/cbcb04211612c527a40c3642f064691d

Fill out the form to see what the product catalog looks like.


![Appwrite E-commerce Catalog ](https://paper-attachments.dropbox.com/s_314B50E78D2533608F0DEE9BE97FEF3C5B7916B2F8CAC55559F4DCA3A07D86F6_1647970384651_Screenshot+40.png)

## Conclusion

This article discussed creating a product catalog using the Appwrite Database feature to create, retrieve and delete data on our database. This product catalog can serve as the basis for a full fledged inventory creation system for a store. Modify the documents to include more product fields.

## Resources

Here are some resources that might be helpful:


- [Automating File Upload and sharing](https://cloudinary.com/blog/automating_file_upload_and_sharing?utm_source=appwrite&utm_medium=affiliate&utm_term=hackmamba)
- [Database API](https://appwrite.io/docs/client/database?utm_source=hackmamba&utm_medium=blog&utm_campaign=next-product-catalog)
- [What is Appwrite and How to use it Efficiently
](https://dev.to/noviicee/what-is-appwrite-and-how-to-use-it-efficiently-3o1m)