Build a product information management app with Appwrite Cloud and NextJS
Tags: cloud, javascript, nextjs, tutorial
Published: 2023-07-06 18:33:13

Product information management (PIM) apps are that help businesses manage their product information. This information can include product descriptions, images, specifications, and pricing. Also, PIM apps can help businesses improve their product listings by providing accurate and up-to-date information. This can lead to increased sales and improved customer satisfaction.

This post discusses building a PIM system that allows us to add, update, and delete our product information in a Next.js application and an open-source design system, [Appwrite Pink Design](https://pink.appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog), to style the application. A custom backend server is not required.

## GitHub

The project's GitHub repository can be found [here](https://github.com/Tundesamson26/next-product-management).

## Prerequisites 

To follow along with this tutorial, a working knowledge of the following is required:

- React, Next.js, and CSS

You’ll also need an Appwrite Cloud account. Request access to Appwrite Cloud [here](https://appwrite.io/cloud/?utm_source=hackmamba&utm_medium=hackmamba-blog).

## Setting up the project
To begin, let’s create a Next.js starter project by navigating to the desired directory and running the command below in our terminal.

```js
npx create-next-app@latest product-info
```
This will guide us through several prompts to set up the project, including selecting a package manager, a UI framework, and additional features.

![nextjs-command-prompt](https://paper-attachments.dropboxusercontent.com/s_DC3309E456E0AE9718E6E96D81D83642AFB17F9E84D25EA6AC6E00014BF0B4CC_1684762969414_image.png)

Once we’ve set up the project, let’s navigate to the project directory and start the development server using the following commands:

```js
cd product-info
npm run dev
```
## Installing dependencies

**Installing Pink Design**
[Pink Design](https://pink.appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) is an open-source system from Appwrite used to build consistent and reusable user interfaces. It enhances collaboration, development experience, and accessibility.

To install Pink Design, let’s open the terminal in the project directory and run the following command.

```js
npm install @appwrite.io/pink
```

To use Pink Design in our project, we import it into our project's files like so:

```js
import '@appwrite.io/pink';
import '@appwrite.io/pink-icons';
```

**Installing Appwrite**
[Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) is a development platform that provides a powerful API and management console for building backend servers for web and mobile applications. To install it, run the command below:

```js
npm install appwrite
```
## Creating an Appwrite Cloud project

To get started, we need to log into our Appwrite cloud, click the **Create project** button, input `product-info` as the name, and then click **Create**.

![product-info project](https://paper-attachments.dropboxusercontent.com/s_DC3309E456E0AE9718E6E96D81D83642AFB17F9E84D25EA6AC6E00014BF0B4CC_1685909245799_image.png)


**Create a database, collection, and add attributes**
With our project created, we can set up our application database. First, navigate to the **Database** tab, click the **Create database** button, input `product-info-management` as the name, and then click **Create**.

![product-info-management database](https://paper-attachments.dropboxusercontent.com/s_DC3309E456E0AE9718E6E96D81D83642AFB17F9E84D25EA6AC6E00014BF0B4CC_1685909449399_image.png)


Secondly, we need to create a collection for storing our products. To do this, click the **Create collection** button, input `productInfo_collection` as the name, and then click **Create**.

![product-info collection](https://paper-attachments.dropboxusercontent.com/s_DC3309E456E0AE9718E6E96D81D83642AFB17F9E84D25EA6AC6E00014BF0B4CC_1685909620154_image.png)


Thirdly, we need to create attributes to represent our database fields. To do this, we need to navigate to the **Attributes** tab and create attributes for each of the values shown below:

| Attribute key    | Attribute type | Size                   | Required |
| ---------------- | -------------- | ---------------------- | -------- |
| **productName**  | String         | 250                    | YES      |
| **productDesc**  | String         | 500                    | YES      |
| **productImage** | String         | 5000                   | YES      |
| **productPrice** | Integer        | `min` 1 - `max` 100000 | YES      |

![product-info attribute](https://paper-attachments.dropboxusercontent.com/s_DC3309E456E0AE9718E6E96D81D83642AFB17F9E84D25EA6AC6E00014BF0B4CC_1685909731766_image.png)


Lastly, we need to update our database permission to manage them accordingly. Navigate to the **Settings** tab, scroll to the **Update Permissions** section, select `Any`, mark accordingly, and then click **Update**.

![permission update](https://paper-attachments.dropboxusercontent.com/s_DC3309E456E0AE9718E6E96D81D83642AFB17F9E84D25EA6AC6E00014BF0B4CC_1685909840624_image.png)

## Integrating Appwrite Cloud into the Next.js project

To integrate Appwrite into the UI, create a `utils/web-init.js` file. The file should look like this.

```js
import { Client, Account } from "appwrite";
export const client = new Client();
export const account = new Account(client);
client
  .setEndpoint("https://cloud.appwrite.io/v1")
  .setProject("643fee63b2b5b506495c");
```

The code above:

- Imports the module `Client` and `Account` from Appwrite
- Instantiates the `Client` and `Account` objects
- Uses the client object to set the endpoint and project

## Building the user interface

The user interface for the product information management app will showcase all the products created using an input field and list all of them with an edit and a delete icon.

To get started, first, we need to navigate to the `src` directory and create a `components` folder, and in this folder, create a `AddProduct.js` file and add the snippet below:

{% embed https://gist.github.com/Tundesamson26/2be71c362437a660ae2a088714bd6e7e %}

In the [gist](https://gist.github.com/Tundesamson26/2be71c362437a660ae2a088714bd6e7e) above, the code does the following:

- Imports required packages and Appwrite Pink Design
- Implements state variables to store the product’s **name**, **descriptions**, **image**, **price**, and **size**
- Sets a state variable for the `Modal` popup

Next, we import the `src/components/AddProduct.js` into the `src/app/page.js`, like so:

```js 
import AddProduct from "@/components/AddProduct";

export default function Home() {
  return (
    <main className="u-main-center">
      <div>
        <h1 className="u-text-center heading-level-4">
          Product Information Management
        </h1>
      </div>
      <AddProduct/>
    </main>
  );
}
```

At this point, our application should look like the following:

![new-product-information-input](https://paper-attachments.dropboxusercontent.com/s_DC3309E456E0AE9718E6E96D81D83642AFB17F9E84D25EA6AC6E00014BF0B4CC_1685057668048_image.png)

> Note: We can use any image link for the product information system images. In this tutorial, however, we use images from Cloudinary as it is easier to apply transformations and optimize delivery. 
> To learn 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).

**Creating database documents**
Next, we need to add new documents to the database collection. In the `src/components/AddProduct.js` file, write a `createProduct()` function to create the document.

```js 
const createProduct = async (e) => {
    e.preventDefault();
    await databases.createDocument(
      "[DATABASE_ID]",
      "[COLECTION_ID]",
      ID.unique(),
      {
        productName: productName,
        productDesc: productDesc,
        productImage: productImage,
        productPrice: productPrice,
        productSize: productSize
      }
    )
      .then((response) => {
        console.log(response);
        alert("product saved successfully")
      }, function (error) {
        console.log(error);
        alert("product not saved")
      });
}
```
In the code block above, the `createProduct()` function does the following:

- Creates a new document using Appwrite's `createDocument()` function while passing the collection ID and attribute values as parameters

Make sure to add a click event to the **Save** button like so:

```html 
<div className="u-flex u-main-end u-gap-16">
      <button className="button" type="submit" onClick={createProduct}>
          <span className="text">Save</span>
      </button>
</div>
```

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

![product-info documents](https://paper-attachments.dropboxusercontent.com/s_DC3309E456E0AE9718E6E96D81D83642AFB17F9E84D25EA6AC6E00014BF0B4CC_1685909918579_image.png)

## Displaying our product information

Our page displays the product information we entered in our form. With this logic, we want our `createProduct()` function to create the documents to display our product.

Next, we need to navigate to the `src` directory and create a `components` folder, and in this folder, we create a `ListProduct.js` file and add the snippet below: 

```html 
<div className="u-flex u-main-end u-gap-16">
      <button className="button" type="submit" onClick={createProduct}>
          <span className="text">Save</span>
      </button>
</div>
```

The `getProducts()` function uses the Appwrite `listDocuments` API that receives a collection ID parameter. The `getProducts()` function finds the collection with that ID.

**Deleting products from our database**
Next, in the `src/components/ListProduct.js` file, create a `deleteProduct()` function to handle deleting products that we no longer need in our collection or database.

```js 
const deleteProduct = async (document_id) => {
    console.log(document_id)
    try {
      await databases.deleteDocument(
        "[DATABASE_ID]",
        "[COLECTION_ID]",
        document_id
      );
      alert("Item has been deleted successfully");
      await getProduct();
    } catch (error) {
      console.log("Error deleting product:", error.message);
      alert("Item was not deleted");
    }
  };
```
The `deleteProduct()` function does the following:

- Finds a document using its `DATABASE_ID`, `COLECTION_ID`, and `DOCUMENT_ID`
- Deletes that document using the Appwrite `deleteDocument()` function
- Alerts us if the item was successfully deleted
- Runs the `getProduct()` function to display our updated product list

## Creating the product information listing interface

Next, we display the products on our product information page. Paste this code into the `src/components/ListProduct.js` file to do so.

```vue 
<div className="container">
      <table className="table is-selected-columns-mobile">
        <thead className="table-thead">
          <tr className="table-row">
            <th className="table-thead-col" style={{ "--p-col-width": 100 }}>
              <span className="eyebrow-heading-3">Product Name</span>
            </th>
            <th
              className="table-thead-col is-only-desktop"
              style={{ "--p-col-width": 100 }}
            >
              <span className="eyebrow-heading-3">Image</span>
            </th>
            <th
              className="table-thead-col is-only-desktop"
              style={{ "--p-col-width": 200 }}
            >
              <span className="eyebrow-heading-3">Description</span>
            </th>
            <th
              className="table-thead-col is-only-desktop"
              style={{ "--p-col-width": 100 }}
            >
              <span className="eyebrow-heading-3">Price</span>
            </th>
            <th
              className="table-thead-col is-only-desktop"
              style={{ "--p-col-width": 120 }}
            >
              <span className="eyebrow-heading-3">Size</span>
            </th>
            <th
              className="table-thead-col"
              style={{ "--p-col-width": 40 }}
            ></th>
          </tr>
        </thead>
        <tbody className="table-tbody">
          {products.map((product) => (
            <tr key={product.$id} className="table-row">
              <td className="table-col" data-title="Name">
                <div className="u-inline-flex u-cross-center u-gap-12">
                  <span className="text u-break-word u-line-height-1-5">
                    {product.productName}
                  </span>
                </div>
              </td>
              <td className="table-col is-only-desktop" data-title="Type">
                <div className="text">
                  <span className="image">
                    <img
                      className="avatar"
                      width="32"
                      height="32"
                      src={product.productImage}
                      alt=""
                    />
                  </span>
                </div>
              </td>
              <td className="table-col is-only-desktop" data-title="Type">
                <div className="text">
                  <span className="text">{product.productDesc}</span>
                </div>
              </td>
              <td className="table-col is-only-desktop" data-title="Size">
                <span className="tag">{product.productPrice}</span>
              </td>
              <td className="table-col is-only-desktop" data-title="Created">
                <time className="text">{product.productSize}</time>
              </td>
              <td className="table-col u-overflow-visible">
                <button
                  className="button is-text is-only-icon"
                  type="button"
                  aria-label="more options"
                  onClick={() => deleteProduct(product.$id)}
                >
                  <span className="icon-trash" aria-hidden="true"></span>
                </button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
</div>
```
In the code block above, we:  

- Loop through the `products` to render each product
- Destructure and pass in our `productName`, `productDesc`, `productImage`, `productPrice`, and `productSize`
- Pass the `deleteProduct()` function we created to the `onClick()` event listener of our `button`

**Updating our product information**
Next, in the `src/components/ListProduct.js` file, we create a `updateProduct()` function to handle updating and correcting our created products in our collection or database.

{% embed https://gist.github.com/Tundesamson26/ae4327d1ff02051c0eb047cfc86b3f3c %}

In the gist above, the following happened:

- The variable `showModal` populates the modal with a click on the pencil icon.
- The `editMode` was initialized with an object containing a single property `index`, initially set to `null`
- The `editProduct` function initiates the editing mode for a specific product. It takes a `productId` as its parameter. Inside the function, it searches for the product with the matching `productId` in the `products` array
- The `updateProduct()` function locates the product with the provided productId in the products array, creates a copy with the updated values, and replaces the old product with the updated one
- Passed the `updateProduct()` function we created to the `onClick()` event listener of the **Update** `button`

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

{% embed
https://www.loom.com/share/6d3779c89ae0469d95abe9d8eedf6072 %}

## Conclusion

This article discussed creating a product information management system using the Appwrite Cloud’s Database feature to create, retrieve, update, and delete data on our database. It can implement authentication features to make it more secure. This PIM can serve as the basis for a full-fledged inventory creation system for a store.

## Resources
- [Appwrite Cloud](https://cloud.appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Appwrite Databases API](https://appwrite.io/docs/client/databases?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Why Pink Design is the Must-Have Tool for your Web Design Arsenal in 2023](https://dev.to/hackmamba/why-pink-design-is-the-must-have-tool-for-your-web-design-arsenal-in-2023-51fl)