How to create real-time video stream comments in Next.js
Tags: programming, nextjs, appwrite, realtime
Published: 2022-09-12 13:38:51


[Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) is an open-source Baas (backend as a service), which provides us with all the necessary APIs to build our projects. Appwrite has client libraries such as:

1. Web SDK (JavaScript)
2. Flutter SDK (Dart)
3. Apple SDK 
4. Android SDK

These libraries allow the building of client-based applications and websites. This tutorial demonstrates how to use Appwrite's Web SDK and the Next framework to create a video streaming chat room (comment area).

## Aim
We will give a quick run-through of the front end as it is not the focus of the tutorial. This article will demonstrate the following:

1. Authenticate users using Appwrite's Google auth.
2. How to track user sessions (get active user and logged out user).
3. How to create a real-time chat using Appwrite's real-time feature.


## Prerequisites

To follow along, you'll need the following:

- Proficient knowledge of React.
- Docker installation (recommended) or a DigitalOcean droplet subscription.
- An Appwrite instance. Check out this [article ](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9)for the setup.

[Here ](http://github.com/muyiwexy/muyi)is a link to the GitHub repository containing the project. Let's begin!

## Getting started

As we said earlier, we can install Appwrite with either [Docker ](https://docs.docker.com/desktop/install/windows-install/)or [DigitalOcean ](https://www.r-bloggers.com/2021/10/setting-up-appwrite-on-digitalocean/)droplet. We'll use Docker for the tutorial.

> Note for Windows users: Before installing Docker, we need either wsl2 or hyper-v on our device. [Here ](https://docs.microsoft.com/en-us/windows/wsl/install-manual)is a link to an installation guide for wsl2.

In this section, we will be doing the following:

1. Building the front-end
2. Creating a new Appwrite project
3. Enabling project functionalities

### Front-end

We will use Next.js and ChakraUI to build our front-end features. First, let's go over what our front-end is and what it does. To proceed, copy the command below and paste it into your terminal to install Next.js and ChakraUi and ChakraUI icons:


> Note: we will use the node package manager throughout this project.


- Installation for Next.js
```bash
npx create-next-app@latest <folder-name> --ts
```

- Installation for ChakraUI
```bash
npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion
```

- Installation for ChakraUI icons
```bash
npm i @chakra-ui/icons
```

Next, create a `components` folder within the pages folder containing five typescript files (separate from the default `index.tsx` and `_app.tsx` files). We will give a brief explanation below of what each file entails.

#### Login.tsx

We will call this `component` the starter pack of the project, as it will contain the functionalities to authenticate users using Google authentication. In this component, we will create a button and set an `onClick` attribute to a function called `googleAuth`. 

Thus, once a user clicks the button, it triggers the googleAuth function. We will shed more light on the authentication functionalities in the coming sections.
```typescript

import Head from 'next/head'
import { access } from '../api/appwriteconfig'
import { ChatIcon } from "@chakra-ui/icons"
import { Box, Button, Center, Stack } from "@chakra-ui/react"

export default function Login() {
  return (
    <>
      <Head>
        <title>Login</title>
      </Head>
      <Center h="100vh">
        <Stack align="center" bgColor="gray.600" p={16} rounded="3xl" spacing={12} boxShadow="lg">
          <Box bgColor="blue.500" w="fit-content" p={5} rounded="3xl" boxShadow="md">
            <ChatIcon w="100px" h="100px" color="white" />
          </Box>
          <Button onClick={(e) => googleAuth(e)} boxShadow="md"> Sign in with google </Button>
        </Stack>
      </Center>
    </>
  )
}
```
![signin](https://paper-attachments.dropbox.com/s_A4D0734EC521B056898CEBBB00FB1E095B6F0EC4A33A8B7EC6EF6D58D98C4387_1658362649556_Screenshot+1.png)

#### Chat.tsx
This file contains two other components that we will discuss later. It is the entirety of our front-end project, as it handles the rendering of our static video and chat area.

```typescript
import { Flex} from "@chakra-ui/react"
import ChatBar from "./Chatbar"
import Topbar from "./Topbar"
     
export default function Chat() {
    return (
        <>
            <Flex >
                <Flex flex={1} h='100vh' w='100%' direction='column'>
                    <Topbar />
                    <iframe width="100%" height="100%" src="https://www.youtube.com/embed/wWgIAphfn2U"></iframe>
                </Flex>
                <ChatBar />
            </Flex>
        </>
    )
}
```
#### Topbar.tsx
This component contains a simple heading and avatar. Its purpose is to display the user's name without registering it in the database.

```typescript
import { Avatar, Flex, Heading } from '@chakra-ui/react'
import React, { useEffect, useState } from 'react'
import { access } from '../api/appwriteconfig';
export default function Topbar() {
    const [user, setUser] = useState({
        name:''
    })
    const getuser = async () => {
        const userdata = access && access.get();
        userdata
            .then((res) => setUser(res))
            .catch((err) => {
                console.log(err)
            })
    }
    useEffect(() => {
        getuser();
    }, [])
    return (
        <Flex h='81px' bg='gray.100' w='100%' align='center' p='5px'>
            <Avatar src="" marginEnd={3} />
            {user && (
                <Heading size='lg'>{user.name}</Heading>
            )}
        </Flex>
    )
}
```

#### Chatbar.tsx
We use this component file to handle the logout function and chat area (comment). The chat area contains input and a flexbox that displays the messages. 

We achieve this display by using the map function to loop over messages and display `{message.message}` by `{message.name}:`. For the logout function, we used the icon button component from ChakraUI and set an `onClick` function to it.

```typescript
import { ArrowLeftIcon } from '@chakra-ui/icons'
import { Button, Flex, IconButton, Input, Text } from '@chakra-ui/react'
import { useEffect, useState } from "react"
import { useRouter } from 'next/router'
import { access, db } from '../api/appwriteconfig'

export type ChatMessage = {
  name: string
  message: string
}
export default function ChatBar() {
  const [messages, setmessages] = useState<ChatMessage[]>([])

  const ChatArea = () => {
    return (
      <form onSubmit={submitMessage}>
        <Input autoComplete='off' placeholder='Type a comment' type='text' name='message' />
        <Button type='submit' hidden> Submit </Button>
      </form>
    )
  }
  return (
    <Flex w='500px' h='100vh' float='right' borderLeft='1px solid' borderLeftColor='gray.200' direction='column'>
      <Flex padding='5px' h='81px' w='100%' align='center' alignSelf='flex-end' borderBottom='1px solid' borderBottomColor='gray.200'>
        <IconButton onClick={logout} size='sm' isRound icon={<ArrowLeftIcon />} aria-label={''} />
      </Flex>
      <Flex flex={1} direction='column' pt={3} mx={3} overflowX='hidden' sx={{ scrollbarWidth: 'none' }}>
        {messages.map((message) => {
          return (
            <Flex bg='blue.100' w='fit-content' minWidth='50px' margin='5px' borderRadius='lg' p={3}>
              <Text >
                <label>
                  {message.name}:
                </label>
                {message.message}
              </Text>
            </Flex>
          )
        })}
      </Flex>
      <ChatArea />
    </Flex>
  )
}
```
#### Homepage.tsx
This component file acts as a housing for the other files; thus, it is the homepage file.

```typescript
import Chat from './Chat'
function Homepage() {
  return (
    <Chat/>
  )
}
export default Homepage
```

Now, we can go ahead with creating our Appwrite project.

### Creating a new Appwrite project

After installing Appwrite, head to your browser and add the default localhost specified during the installation. Afterward, we can sign up to Appwrite to set up our project configuration.

Once we have set up our Appwrite console, we will create a new project. In this project, create a platform consisting of the web app name and hostname (localhost) and click register. Then, paste the command in the platform section to our terminal to install Appwrite to our node module.

![appwrite home](https://paper-attachments.dropbox.com/s_A4D0734EC521B056898CEBBB00FB1E095B6F0EC4A33A8B7EC6EF6D58D98C4387_1658963513802_Screenshot+4.png)

![select platform](https://paper-attachments.dropbox.com/s_A4D0734EC521B056898CEBBB00FB1E095B6F0EC4A33A8B7EC6EF6D58D98C4387_1658963529892_Screenshot+5.png)

![platform](https://paper-attachments.dropbox.com/s_A4D0734EC521B056898CEBBB00FB1E095B6F0EC4A33A8B7EC6EF6D58D98C4387_1658963548067_Screenshot+6.png)

```bash
npm install appwrite
```

After setting up the platform, we will create a new attribute. To do this, we must create a database > collection > attribute. In our collection settings, we will set a collection level permission (for this tutorial, we'll set it to `role:member`). Now we're good to go, and our Appwrite console is ready for use.

![collection permission](https://paper-attachments.dropbox.com/s_A4D0734EC521B056898CEBBB00FB1E095B6F0EC4A33A8B7EC6EF6D58D98C4387_1658963738079_Screenshot+7.png)

### Project functionalities

After installing Appwrite, we will need to initialize its web SDK in our project. To do this,  create a new folder, and within that folder, a new  `typescript` file called `appwriteconfig.tsx`. 

Then, copy the code below into the `appwriteconfig.tsx` file. We now have our web SDK ready for use within our project:

```typescript
import { Client, Account, Databases } from 'appwrite';
const client = new Client();
const account = new Account(client);
const databases = new Databases(client, 'Comment');
// Init Web SDK
client
    .setEndpoint('http://localhost/v1') // API Endpoint
    .setProject('Chat-app') // project ID
;
export const access = account;
export const db = databases;

```

#### Authentication
We want to authenticate the identity of new users, and to achieve that, we will use Google auth services within Appwrite. We have a step-by-step process to follow through with this section:


- First, we want to set up OAuth 2.0. To do so, head to the [API Console](https://console.cloud.google.com/apis/dashboard).
- Next, we will go to credentials and click on create credentials. Then, we will select OAuth client ID and fill out the following: 
    - Set the application type (For the project, we will select the `web application` from the drop-down).
    - Then we'll specify our app name, authorized JavaScript URI (localhost:3000), and authorized redirect URI (we can get this by going to our Appwrite console and navigating to Users > Settings. Scroll to Google and turn it on; below, we will copy the callback link from Appwrite and paste it in this section).
    - Finally, click on create.

![authopage](https://paper-attachments.dropbox.com/s_A4D0734EC521B056898CEBBB00FB1E095B6F0EC4A33A8B7EC6EF6D58D98C4387_1658789668477_Screenshot+3.png)

![auth1page](https://paper-attachments.dropbox.com/s_A4D0734EC521B056898CEBBB00FB1E095B6F0EC4A33A8B7EC6EF6D58D98C4387_1658789819986_Screenshot+2.png)

- Now, we will get our client ID and secret. Copy these details and paste them into their required field in the Appwrite console. Click update to enable Google.

![auth2page](https://paper-attachments.dropbox.com/s_A4D0734EC521B056898CEBBB00FB1E095B6F0EC4A33A8B7EC6EF6D58D98C4387_1658362862717_Screenshot+2_LI.jpg)

- Finally, in our `Login.tsx` file, we will call our `googleAuth` function and set a `preventDefault` method (it prevents the page from loading when the function kicks in) and use the `createOAuth2Session` method to create a session for new users. We have the implementation of the explanation below.

```typescript
const googleAuth = async (e: any) => {
    e.preventDefault();
    try {
      access.createOAuth2Session(
        'google',
        'http://localhost:3000/component/Homepage',
        'http://localhost:3000/component/Blob'
      );
    } catch (error) {
      console.log(error)
    }
  }
```

#### Log out a user
This function redirects to the login page after a user logs out. To do this, we must import `useRouter` and call the `logout` function attached to the icon button. 

After, we will call the `deleteSession` on the current user and use the `push` method to force a redirect back to the index (`Login.tsx`) page. We have an implementation below:

```typescript
const router = useRouter()

const logout = async () => {
  access.deleteSession("current")
  alert("Logout successful")
  router.push("/")
}
```

#### Creating documents
In our `Chatbar.tsx` file, we want to grab and populate the input in our database. Thus, we will call the `submitMessage` function and create two constants (message — capture the value from the input; CurrentUser — [get account](https://appwrite.io/docs/client/account?sdk=web-default#accountGet)). 

After, we will create a new document with the [createDocument ](https://appwrite.io/docs/client/databases?sdk=web-default#databasesCreateDocument)method with the parameters (`collection id, documents id (use a unique id), data`). Below is an implementation of the explanation above:

```typescript
const submitMessage = async (e: any) => {
  e.preventDefault();
  const message = e.target.message.value;
  const CurrentUser = await access.get()
  await db.createDocument("Chat", 'unique()', {
    name: CurrentUser.name,
    message,
  })
}
```

Currently, the code above saves our input texts in our documents. Now we want to listen to those messages to grab them as they enter the database.

#### Listening to messages
We will get started by creating an effect that subscribes and listens to new messages using the `subscribe` method. For the arguments within the method, we will call an array of channels, followed by a callback function and a payload. After, we will unsubscribe from the channel.

> Note: if we do not unsubscribe, we may get our messages multiple times, as the subscribe method continues to listen to the channel provided.

Here is an implementation of the explanation above:

```typescript
useEffect(() => {
  const unsubscribe = access.client.subscribe(
    ['databases.Comment.collections.Chat.documents'],
    (data) => {
      setmessages((messages) => [...messages, data.payload as ChatMessage]);
    }
  );
  return () => {
    unsubscribe();
  }
  
}, [])
```
#### Grabbing the list of documents
When listening to the messages, we also want to grab the messages immediately, and the list of documents does that effectively. We have the implementation below:

```typescript
const genRandomKey = async () => {
  const messages = await db.listDocuments('Chat', [], 100, 2);
  setmessages(messages.documents as unknown as ChatMessage[])
};
useEffect(() => {
  genRandomKey();
}, [])
```

![result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9z0aby0t20xe5ybejjd.gif)
## Conclusion

This post discussed how to create real-time video stream comments in Next js. We discussed authenticating new users and listening to changes using real-time features in the tutorial. 

### Resources

Check out Appwrite’s docs on the real-time feature [here](https://appwrite.io/docs/realtime). Thanks for reading, and happy coding!