Building a Real-Time Chat Application with Next.js, TypeScript, Prisma, and Pusher Arfatur Rahman
February 15, 2025
Comments (0)

Building a Real-Time Chat Application with Next.js, TypeScript, Prisma, and Pusher

In this post, we'll walk through the process of building a real-time chat application using Next.js, TypeScript, Prisma, and Pusher. We'll explain the purpose of each package, how they work together, and provide a step-by-step explanation of the code.


Table of Contents

  1. Introduction
  2. Technologies Used
  3. Setting Up the Project
  4. Backend Implementation
  5. Frontend Implementation
  6. Real-Time Communication with Pusher
  7. Conclusion

Introduction

Real-time communication is a crucial feature in modern web applications, especially for chat applications, notifications, and collaborative tools. In this tutorial, we'll build a real-time chat application where users can send and receive messages instantly. We'll use Next.js for the frontend and backend, TypeScript for type safety, Prisma for database management, and Pusher for real-time communication.


Technologies Used

1. Next.js

Next.js is a React framework that enables server-side rendering, static site generation, and API routes. It simplifies the development of full-stack applications by providing a unified structure for both frontend and backend.

  • Why Next.js?
    • Built-in API routes for backend logic.
    • Server-side rendering for better SEO and performance.
    • Easy deployment with Vercel.

Next.js Documentation


2. TypeScript

TypeScript is a typed superset of JavaScript that adds static type checking. It helps catch errors during development and improves code readability.

  • Why TypeScript?
    • Type safety reduces runtime errors.
    • Better developer experience with autocompletion and type hints.

TypeScript Documentation


3. Prisma

Prisma is an ORM (Object-Relational Mapping) tool that simplifies database access and management. It provides a type-safe API for interacting with your database.

  • Why Prisma?
    • Type-safe database queries.
    • Easy migrations and schema management.
    • Supports multiple databases (PostgreSQL, MySQL, SQLite, etc.).

Prisma Documentation


4. Pusher

Pusher is a real-time communication service that enables instant data transfer between clients and servers. It uses WebSockets to provide low-latency communication.

  • Why Pusher?
    • Easy to integrate with frontend and backend.
    • Scalable and reliable real-time communication.
    • Supports channels and events for flexible messaging.

Pusher Documentation


Setting Up the Project

1. Initialize a Next.js Project

Run the following command to create a new Next.js project:

npx create-next-app@latest real-time-chat --typescript

2. Install Dependencies

Install the required dependencies:

npm install pusher pusher-js prisma @prisma/client zod axios

3. Set Up Prisma

Initialize Prisma and configure your database:

npx prisma init

Update the schema.prisma file with your database configuration and define the models:

model Conversation {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  name      String
  email     String   @unique
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  Message Message[]
}

model Message {
  id             String       @id @default(auto()) @map("_id") @db.ObjectId
  message        String
  conversationId String       @db.ObjectId
  createdAt      DateTime     @default(now())
  updatedAt      DateTime     @updatedAt
  userRole       String       @default("user")
  conversation   Conversation @relation(fields: [conversationId], references: [id])
}

Run the following command to generate and apply the migrations:

npx prisma migrate dev --name init

Backend Implementation

1. Pusher Configuration

Create a pusher.ts file to configure Pusher for both server and client:

import PusherServer from "pusher";
import Pusher from "pusher-js";

export const pusherServer = new PusherServer({
  appId: process.env.PUSHER_APPID as string,
  key: process.env.NEXT_PUBLIC_PUSHER_KEY as string,
  secret: process.env.PUSHER_SECRET as string,
  cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER as string,
  useTLS: true,
});

export const pusherClient = new Pusher(
  process.env.NEXT_PUBLIC_PUSHER_KEY as string,
  {
    cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER as string,
  },
);

2. API Routes

Create an API route to handle message creation and retrieval:

import { routeErrorHandler } from "@/lib/api-error-handler";
import { formatResponse } from "@/lib/api-response";
import { db } from "@/lib/db";
import { pusherServer } from "@/lib/pusher";
import { z } from "zod";

export async function POST(req: Request, { params }: { params: { id: string } }) {
  try {
    const { id } = params;
    const body = await req.json();
    const { message, userRole } = body;

    const newMessage = await db.message.create({
      data: { message, userRole, conversationId: id },
    });

    await pusherServer.trigger("my-channel", id, {
      messages: [newMessage],
    });

    return formatResponse([newMessage], "Message sent successfully", 200);
  } catch (error) {
    return routeErrorHandler(error);
  }
}

Frontend Implementation

1. Pusher Client Integration

Subscribe to the Pusher channel and listen for new messages:

import { useEffect, useState } from "react";
import { pusherClient } from "@/lib/pusher";

const ChatComponent = ({ conversationId }: { conversationId: string }) => {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const channel = pusherClient.subscribe("my-channel");
    channel.bind(conversationId, (data: { messages: any[] }) => {
      setMessages((prev) => [...prev, ...data.messages]);
    });

    return () => {
      channel.unbind(conversationId);
      pusherClient.unsubscribe("my-channel");
    };
  }, [conversationId]);

  return (
    <div>
      {messages.map((msg, i) => (
        <div key={i}>{msg.message}</div>
      ))}
    </div>
  );
};

2. Send Messages

Create a form to send messages:

import axios from "axios";
import { useState } from "react";

const MessageForm = ({ conversationId }: { conversationId: string }) => {
  const [message, setMessage] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    await axios.post(`/api/conversation/${conversationId}`, {
      message,
      userRole: "user",
    });
    setMessage("");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
      <button type="submit">Send</button>
    </form>
  );
};

Real-Time Communication with Pusher

Pusher enables real-time communication by using WebSockets. When a message is sent, the server triggers an event on a specific channel. The client listens to this channel and updates the UI in real time.


Conclusion

In this tutorial, we built a real-time chat application using Next.js, TypeScript, Prisma, and Pusher. We covered the setup, backend implementation, frontend integration, and real-time communication. This stack provides a robust and scalable solution for building real-time applications.

Feel free to extend this project by adding features like user authentication, message history, or multimedia support.

Tags:

PusherJSTypeScriptPrismaNextJS

About the Author

Arfatur Rahman

Arfatur Rahman

Software Developer

I’m Arfatur Rahman, a Full-Stack and AI-Driven Software Developer with deep expertise in building modern SaaS platforms, RAG-based AI applications, scalable APIs, and real-time web systems. My work focuses on combining high-quality engineering with cutting-edge AI technologies to create applications that are reliable, secure, and capable of intelligent decision-making.

I specialize in technologies such as Next.js, React, TypeScript, Node.js, Prisma, Supabase, MongoDB, and Azure—alongside advanced AI stacks including LangChain, Vector Databases, embeddings generation, and Retrieval-Augmented Generation (RAG). I develop production-ready AI chatbots, knowledge-bases, automation tools, and full-stack platforms that integrate seamlessly with OpenAI, Gemini, Mistral, and Azure AI.

My engineering approach emphasizes performance, scalability, and clean architecture, enabling me to build systems that handle real-world traffic, complex data pipelines, secure authentication flows, and modern ISR/SSR strategies in Next.js. I’m passionate about developing intelligent applications that blend strong backend engineering with real-world AI capabilities—ensuring high performance, reliability, and future-proof design.

📍 Chittagong, Bangladesh📞 +880 1819 439 292📧 [email protected]

Comments

No Comments

Leave a replay

Your email address will not be publish. Required fields are marked *