Build a Chat App with Cloudinary and Xata
Tags: xata, cloudinary, nextjs, chat
Published: 2022-11-23 09:13:54

---
title: Build a Chat App with Cloudinary and Xata
published: true
description: In this article, we‘ll learn how to create a chat application with Next.js, Cloudinary's Upload widget, and Xata.
tags: xata, cloudinary, nextjs, chat
# cover_image: 
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-23 08:48 +0000
---

One common thing with platforms like WhatsApp, Telegram, Twitter, and Instagram is that they all offer great chat functionality via their mobile and web applications. The ability to communicate swiftly with people across the globe has never been more important.

In this article, we‘ll learn how to create a chat application with Next.js, Cloudinary's Upload widget, and Xata.


## Demo and Source Code

View the live demo via [this link](https://hackmamba-xata-chat-app.vercel.app/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) and access the source code on [GitHub](https://github.com/nefejames/hackmamba-xata-chat-app/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog).


## Prerequisites

To follow along with this article, we need to have:

- Knowledge of React and Next.js
- A [Cloudinary account](https://cloudinary.com/console/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
- A [Xata account](https://app.xata.io/signin/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
- Understanding of Chakra UI is advantageous but not required


## What Is Xata?

[Xata](https://xata.io/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) is a serverless database built on top of PostgreSQL and Elasticsearch. It comes with useful integrations with popular frameworks and technologies like Next.js, Netlify, Nuxt.js, Vercel, Remix, and SvelteKit.


## What is Cloudinary?

[Cloudinary](https://cloudinary.com/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) is a cloud-based and end-to-end platform with tools and functionalities for storing, processing, editing, managing, delivering, and transforming media assets.


## What We Will Create

The screenshot below shows the application we will create. 
Users can fill in their name, provide a message and upload their avatar through the form on the right. Upon clicking the **Send** button, the data gets saved to Xata’s database. We fetch the chats from the database and display them on the left side of the application. 
The avatar image upload is handled by Cloudinary’s Upload Widget.  

![the chat app we will create](https://paper-attachments.dropboxusercontent.com/s_767BD3F7E260EFDC7E9C209798C513C5E8831A1EBE6D22594749206A5E483F79_1668165222225_Screenshot+2791.png)




## Getting Started

Download, clone, or fork [this starter template](https://github.com/nefejames/hackmamba-xata-chat-app-ui/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) that contains the UI for the chat app.

```
    npx create-next-app xata-cloudinary-chat-app -e https://github.com/nefejames/hackmamba-xata-chat-app-ui
    or
    yarn create next-app xata-cloudinary-chat-app -e https://github.com/nefejames/hackmamba-xata-chat-app-ui
```

Next, navigate to the project directory, and run the command below to install Chakra UI, Next.js, Xata’s SDK, and every other necessary dependency, then run the application.

```bash
    npm i
```

The starter template consists of the following files:

- `components/ChatContainer.js`: the container that holds the chats 
- `components/ChatInput.js`: where users can input and submit new chats
- `components/ChatProfile.js`: holds each user’s name, avatar, and chat message
- `layout/index.js`: the layout for the application
- `utils`: will hold the Xata instance and avatar upload logic, which we will set up later



## Setting up the Xata Database

We start by creating a new database called **chat-app** on our Xata account.

![creating a new database in Xata](https://paper-attachments.dropboxusercontent.com/s_767BD3F7E260EFDC7E9C209798C513C5E8831A1EBE6D22594749206A5E483F79_1668167312852_Screenshot_191.png)


Next, create a `messages` table that will contain the records of the chat messages that users submit. Each record will have `message`, `author`, `img`, and `id` properties; the `id` is automatically generated by Xata.
We got the images from the media uploads in our Cloudinary account.

![creating the messages records](https://paper-attachments.dropboxusercontent.com/s_767BD3F7E260EFDC7E9C209798C513C5E8831A1EBE6D22594749206A5E483F79_1668167558210_Screenshot_192.png)


The database schema is ready, so let’s set up the Xata instance next, and integrate it into the application. 


## Setting up the Xata Instance

Run the command below to install the CLI globally.
```
    npm install @xata.io/cli -g
```

Next, run `xata auth login`, which will prompt you to Create a new API key in the browser or use an existing one; we’ll go with the first option.

![login to create CLI API key](https://paper-attachments.dropboxusercontent.com/s_767BD3F7E260EFDC7E9C209798C513C5E8831A1EBE6D22594749206A5E483F79_1668174894564_xata-auth-login.png)


Once in the browser, give the API a name and click the **Create API key** button.

![creating a Xata API ley](https://paper-attachments.dropboxusercontent.com/s_767BD3F7E260EFDC7E9C209798C513C5E8831A1EBE6D22594749206A5E483F79_1668175038404_xata-create-new-api-key.png)


Back on the dashboard, click on the **Get Code Snippet** button at the top right corner and copy the second command. We will use it to initialize the project locally with the aid of the CLI.

Run the command in the terminal and choose from the configuration options. The screenshot below shows the configurations for this application.

![xata cli config](https://paper-attachments.dropboxusercontent.com/s_767BD3F7E260EFDC7E9C209798C513C5E8831A1EBE6D22594749206A5E483F79_1668175900013_xata-cli-config.png)


The CLI will automatically create a `xata.js` file that contains the `XataClient` instance we need; let’s move on to the coding aspects of the application.


## Fetching the Messages in the Database 

We’ll start by fetching the messages that already exist in the database. Update the `index.js` file with the following code:

```JavaScript
    import { Box, Flex, Heading } from "@chakra-ui/react";
    import ChatContainer from "@components/ChatContainer";
    import ChatInput from "@components/ChatInput";
    import { getXataClient } from "@utils/xata";
    
    export default function Home({ data }) {
      return (
        <div>
          <Heading as="h1" mb={5}>
            Xata Chat App
          </Heading>
          <Flex flexDir={["column", "row"]} justifyContent="space-between">
            <ChatContainer chats={data} />
            <Box>
              <ChatInput />
            </Box>
          </Flex>
        </div>
      );
    }
    
    export async function getServerSideProps() {
      const xata = getXataClient();
      const data = await xata.db.messages.getAll();
      return { props: { data } };
    }
```

Here, we did the following:

- Imported the `getXataClient` utility and initialized a new instance
- Queried the `messages` table and fetched all records with `getServerSideProps`
- Passed the fetched data to the `ChatContainer` component through props


## Displaying the Messages in the ChatContainer Component

Here, we then loop through the data and display the individual messages in the UI.

```JavaScript
    import { Box, VStack } from "@chakra-ui/react";
    import ChatProfile from "./ChatProfile";
    
    export default function ChatContainer({ chats }) {
      return (
        <Box>
          <VStack spacing={5}>
            {chats.map((chat) => (
              <ChatProfile
                key={chat.id}
                author={chat.author}
                img={chat.img}
                message={chat.message}
              />
            ))}
          </VStack>
        </Box>
      );
    }
```


## Setting up the Avatar Upload Functionality in the ChatInput Component

When a user clicks on the **Upload Avatar** button, [Cloudinary’s Upload Widget](https://cloudinary.com/documentation/upload_widget/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) will open, enabling the user to upload a picture; let’s set it up. Update the `ChatInput.js` file with the following code: 

```JavaScript
    import { useState } from "react";
    import { Box, Button, Input, Flex } from "@chakra-ui/react";
    import ShowImageUploadWidget from "@utils/upload";
    
    export default function ChatInput() {
      const [imgUrl, setImgUrl] = useState(null);
      
      return (
        <Box w={["full", "400px"]}>
          <form>
            <Flex flexDir="column">
              <Input name="author" placeholder="Author" />
              <Input name="newMessage" placeholder="Message" />
              <Button
                onClick={() => ShowImageUploadWidget(setImgUrl)}
              >
                Upload Avatar
              </Button>
              <Button type="submit">Send</Button>
            </Flex>
          </form>
        </Box>
      );
    }
```

Here, we did the following:

- Set up an `imgUrl` state that will contain the URL of the avatar users upload to Cloudinary
- Imported a `ShowImageUploadWidget` function from the `utils` directory
- Passed `ShowImageUploadWidget` to the **Upload Avatar** button’s `onClick` event handler

The `ShowImageUploadWidget` function is responsible for the upload functionality. Create a `upload.js` file and paste the following code to set up `ShowImageUploadWidget`.

```JavaScript
    function ShowImageUploadWidget(setImgUrl) {
      window.cloudinary
        .createUploadWidget(
          {
            cloudName: "your-cloud-name",
            uploadPreset: "ml_default",
          },
          (error, result) => {
            if (!error && result && result.event === "success") {
              setImgUrl(result.info.thumbnail_url);
            }
            if (error) {
              console.log(error);
            }
          }
        )
        .open();
    }
    
    export default ShowImageUploadWidget;
```JavaScript

Let’s breakdown the snippet above:

- `ShowImageUploadWidget` calls the `createUploadWidget` that exists in the `cloudinary` object
- `createUploadWidget` takes in our `cloudname` as part of its config
- `ShowImageUploadWidget` accepts the `setImgUrl` function as an argument. If an avatar is uploaded successfully, the `imgUrl` state will be updated with the URL of the avatar a user uploads

We have to load the widget’s script in order for it to work. Next.js provides a `[Script](https://nextjs.org/docs/basic-features/script)` [component](https://nextjs.org/docs/basic-features/script/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) that we can use to load third-party scripts in our application. 

Update the `_app.js` file with the following code:

```JavaScript
    import { ChakraProvider } from "@chakra-ui/react";
    import Wrapper from "@layout/index";
    import Script from "next/script";
    
    function MyApp({ Component, pageProps }) {
      return (
        <ChakraProvider>
          <Script
            src="https://upload-widget.cloudinary.com/global/all.js"
            type="text/javascript"
            strategy="beforeInteractive"
          />
          <Wrapper>
            <Component {...pageProps} />
          </Wrapper>
        </ChakraProvider>
      );
    }

    export default MyApp;
```
Here, we imported the `Script` component from Next.js and used it to load the widget’s script.


## Setting up the Form Submission Functionality in the ChatInput Component

The form submission functionality is the last thing we need to set up. Update the `ChatInput` component with the following code:

```JavaScript
    import { useState } from "react";
    import { Box, Button, Input, Flex, useToast } from "@chakra-ui/react";
    import ShowImageUploadWidget from "@utils/upload";
    
    export default function ChatInput() {
      const toast = useToast();
      const [imgUrl, setImgUrl] = useState(null);
      
      const handleSubmit = async (e) => {
        e.preventDefault();
        const formData = new FormData(e.target);
        const data = Object.fromEntries(formData);
        const author = data.author;
        const newMessage = data.newMessage;
        fetch("/api/add-message", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            author,
            newMessage,
            imgUrl,
          }),
        });
        e.target.reset();
        toast({
          title: "Message created.",
          description: "Message sent to chat db.",
        });
      };
      
      return (
        <Box w={["full", "400px"]}>
          <form onSubmit={handleSubmit}>
            <Flex flexDir="column">
              <Input name="author" placeholder="Author" />
              <Input name="newMessage" placeholder="Message" />
              <Button>
                Upload Avatar
              </Button>
              <Button type="submit">Send</Button>
            </Flex>
          </form>
        </Box>
      );
    }
```

Here, we did the following:

- Created a `handleSubmit` function that takes the form data and passes it on the `add-messages` route. The `add-messages` will handle pushing the form data to the Xata database; we will set up the route next
- Used the `toast` method to display a message upon successful form submission
- Passed `handleSubmit` to the form’s `onSubmit` event handler


For the API route, create a `api/add-message.js` file and paste the following code:

```JavaScript
    import { getXataClient } from "@utils/xata";
    const xata = getXataClient();
    
    const handler = async (req, res) => {
      const { author, newMessage, imgUrl } = req.body;
      await xata.db.messages.create({
        author: author,
        message: newMessage,
        img: imgUrl,
      });
      res.end();
    };
    
    export default handler;
```

Here, we did the following:

- Imported the `getXataClient` utility and initialized a new instance
- Accessed the `author`, `newMessage`, and `imgUrl` from the request’s body
- Saved the new chat data to the `messages` table on Xata


## Conclusion

With that, we have successfully created a chat application with Cloudinary and Xata. The purpose of this simple is to give a brief overview of the features of these awesome technologies. We can take the project to the next step by validating the form fields during submission and adding user authentication and real-time functionality to the application.


## Resources
- [Cloudinary](https://cloudinary.com/documentation/product_gallery)['s Upload Widget Documentation](https://cloudinary.com/documentation/upload_widget/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
- [Next Script Documentation](https://nextjs.org/docs/basic-features/script/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
- [Xata’s Documentation](https://xata.io/docs/overview/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)