Below is an implementation for communication tools including a messaging system, email notifications, and announcements/notices using Next.js, NestJS, and GraphQL.
Backend (NestJS)
1. Entities
Message Entity:
// message.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn } from 'typeorm';
import { User } from './user.entity';
@Entity()
export class Message {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => User, (user) => user.sentMessages)
sender: User;
@ManyToOne(() => User, (user) => user.receivedMessages)
receiver: User;
@Column()
content: string;
@CreateDateColumn()
timestamp: Date;
}
Announcement Entity:
// announcement.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
@Entity()
export class Announcement {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
content: string;
@CreateDateColumn()
createdAt: Date;
}
2. Services
Message Service:
// message.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Message } from './message.entity';
@Injectable()
export class MessageService {
constructor(
@InjectRepository(Message)
private messageRepository: Repository<Message>,
) {}
async findAll(): Promise<Message[]> {
return this.messageRepository.find({ relations: ['sender', 'receiver'] });
}
async create(senderId: number, receiverId: number, content: string): Promise<Message> {
const newMessage = this.messageRepository.create({
sender: { id: senderId },
receiver: { id: receiverId },
content,
});
return this.messageRepository.save(newMessage);
}
}
Announcement Service:
// announcement.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Announcement } from './announcement.entity';
@Injectable()
export class AnnouncementService {
constructor(
@InjectRepository(Announcement)
private announcementRepository: Repository<Announcement>,
) {}
async findAll(): Promise<Announcement[]> {
return this.announcementRepository.find();
}
async create(title: string, content: string): Promise<Announcement> {
const newAnnouncement = this.announcementRepository.create({ title, content });
return this.announcementRepository.save(newAnnouncement);
}
}
3. Resolvers
Message Resolver:
// message.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { MessageService } from './message.service';
import { Message } from './message.entity';
@Resolver(() => Message)
export class MessageResolver {
constructor(private messageService: MessageService) {}
@Query(() => [Message])
async messages() {
return this.messageService.findAll();
}
@Mutation(() => Message)
async sendMessage(
@Args('senderId') senderId: number,
@Args('receiverId') receiverId: number,
@Args('content') content: string,
) {
return this.messageService.create(senderId, receiverId, content);
}
}
Announcement Resolver:
// announcement.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { AnnouncementService } from './announcement.service';
import { Announcement } from './announcement.entity';
@Resolver(() => Announcement)
export class AnnouncementResolver {
constructor(private announcementService: AnnouncementService) {}
@Query(() => [Announcement])
async announcements() {
return this.announcementService.findAll();
}
@Mutation(() => Announcement)
async createAnnouncement(
@Args('title') title: string,
@Args('content') content: string,
) {
return this.announcementService.create(title, content);
}
}
Frontend (Next.js)
1. Apollo Client Setup
// apollo-client.js
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:3000/graphql',
cache: new InMemoryCache(),
});
export default client;
2. Messaging System Page
// pages/messages.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_MESSAGES = gql`
query GetMessages {
messages {
id
content
timestamp
sender {
username
}
receiver {
username
}
}
}
`;
const SEND_MESSAGE = gql`
mutation SendMessage($senderId: Int!, $receiverId: Int!, $content: String!) {
sendMessage(senderId: $senderId, receiverId: $receiverId, content: $content) {
id
content
timestamp
}
}
`;
export default function Messages() {
const { loading, error, data } = useQuery(GET_MESSAGES);
const [sendMessage] = useMutation(SEND_MESSAGE);
const [senderId, setSenderId] = useState('');
const [receiverId, setReceiverId] = useState('');
const [content, setContent] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await sendMessage({ variables: { senderId: parseInt(senderId), receiverId: parseInt(receiverId), content } });
setSenderId('');
setReceiverId('');
setContent('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Messages</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
placeholder="Sender ID"
value={senderId}
onChange={(e) => setSenderId(e.target.value)}
/>
<input
type="number"
placeholder="Receiver ID"
value={receiverId}
onChange={(e) => setReceiverId(e.target.value)}
/>
<textarea
placeholder="Message Content"
value={content}
onChange={(e) => setContent(e.target.value)}
></textarea>
<button type="submit">Send Message</button>
</form>
<ul>
{data.messages.map((msg) => (
<li key={msg.id}>
<strong>{msg.sender.username}</strong> to <strong>{msg.receiver.username}</strong>: {msg.content} <em>at {msg.timestamp}</em>
</li>
))}
</ul>
</div>
);
}
3. Announcements Page
// pages/announcements.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_ANNOUNCEMENTS = gql`
query GetAnnouncements {
announcements {
id
title
content
createdAt
}
}
`;
const CREATE_ANNOUNCEMENT = gql`
mutation CreateAnnouncement($title: String!, $content: String!) {
createAnnouncement(title: $title, content: $content) {
id
title
content
createdAt
}
}
`;
export default function Announcements() {
const { loading, error, data } = useQuery(GET_ANNOUNCEMENTS);
const [createAnnouncement] = useMutation(CREATE_ANNOUNCEMENT);
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createAnnouncement({ variables: { title, content } });
setTitle('');
setContent('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Announcements</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
placeholder="Content"
value={content}
onChange={(e) => setContent(e.target.value)}
></textarea>
<button type="submit">Create Announcement</button>
</form>
<ul>
{data.announcements.map((ann) => (
<li key={ann.id}>
<strong>{ann.title}</strong> - {ann.content} <em>at {ann.createdAt}</em>
</li>
))}
</ul>
</div>
);
}
GraphQL Schema
Define your GraphQL schema to match the resolver functions:
type User {
id: ID!
username: String!
}
type Message {
id: ID!
content: String!
timestamp: String!
sender: User!
receiver: User!
}
type Announcement {
id: ID!
title: String!
content: String!
createdAt: String!
}
type Query {
messages: [Message!]!
announcements: [Announcement!]!
}
type Mutation {
sendMessage(senderId: Int!, receiverId: Int!, content: String!): Message!
createAnnouncement(title: String!, content: String!): Announcement!
}
Email Notifications (Optional)
To send email notifications, you can integrate an email service provider like SendGrid or Nodemailer in your NestJS application.
Email Service:
// email.service.ts
import { Injectable } from '@nestjs/common';
import * as nodemailer from 'nodemailer';
@Injectable()
export class EmailService {
private transporter;
constructor() {
this.transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'your-email@gmail.com',
pass: 'your-email-password',
},
});
}
async sendEmail(to: string, subject: string, text: string) {
const mailOptions = {
from: 'your-email@gmail.com',
to,
subject,
text,
};
await this.transporter.sendMail(mailOptions);
}
}
You can then inject this EmailService
in your MessageService
and AnnouncementService
to send email notifications upon message or announcement creation.
This setup covers the backend and frontend code for developing a messaging system, email notifications, and announcements/notices. You can expand on this by adding more features, such as real-time notifications, message threading, and more.
To implement real-time notifications in the messaging and announcements system, we can use WebSockets. Here, I'll guide you through setting up real-time notifications using NestJS with WebSockets on the backend and integrating it with the Next.js frontend.
Backend (NestJS)
1. Install WebSocket Dependencies
First, install the WebSocket package for NestJS:
npm install @nestjs/websockets @nestjs/platform-socket.io
2. Create WebSocket Gateway
Create a WebSocket gateway to handle real-time communication.
message.gateway.ts
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
ConnectedSocket,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { MessageService } from './message.service';
import { Message } from './message.entity';
@WebSocketGateway()
export class MessageGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server;
constructor(private readonly messageService: MessageService) {}
async handleConnection(socket: Socket) {
console.log(`Client connected: ${socket.id}`);
}
async handleDisconnect(socket: Socket) {
console.log(`Client disconnected: ${socket.id}`);
}
@SubscribeMessage('sendMessage')
async handleSendMessage(@MessageBody() data: { senderId: number, receiverId: number, content: string }) {
const message = await this.messageService.create(data.senderId, data.receiverId, data.content);
this.server.emit('receiveMessage', message);
return message;
}
}
announcement.gateway.ts
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
ConnectedSocket,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { AnnouncementService } from './announcement.service';
import { Announcement } from './announcement.entity';
@WebSocketGateway()
export class AnnouncementGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server;
constructor(private readonly announcementService: AnnouncementService) {}
async handleConnection(socket: Socket) {
console.log(`Client connected: ${socket.id}`);
}
async handleDisconnect(socket: Socket) {
console.log(`Client disconnected: ${socket.id}`);
}
@SubscribeMessage('createAnnouncement')
async handleCreateAnnouncement(@MessageBody() data: { title: string, content: string }) {
const announcement = await this.announcementService.create(data.title, data.content);
this.server.emit('newAnnouncement', announcement);
return announcement;
}
}
3. Update Module
Update your module to include the gateways:
app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Message } from './message.entity';
import { MessageService } from './message.service';
import { MessageGateway } from './message.gateway';
import { Announcement } from './announcement.entity';
import { AnnouncementService } from './announcement.service';
import { AnnouncementGateway } from './announcement.gateway';
@Module({
imports: [TypeOrmModule.forFeature([Message, Announcement])],
providers: [MessageService, MessageGateway, AnnouncementService, AnnouncementGateway],
})
export class AppModule {}
Frontend (Next.js)
1. Install Socket.io Client
Install the socket.io client for Next.js:
npm install socket.io-client
2. Set Up WebSocket Connection
Set up the WebSocket connection and handle real-time events.
lib/socket.js
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000');
export default socket;
3. Update Messaging System Page
Update the messaging system page to use WebSocket for real-time notifications.
pages/messages.js
import { useState, useEffect } from 'react';
import { useQuery, gql } from '@apollo/client';
import socket from '../lib/socket';
const GET_MESSAGES = gql`
query GetMessages {
messages {
id
content
timestamp
sender {
username
}
receiver {
username
}
}
}
`;
export default function Messages() {
const { loading, error, data, refetch } = useQuery(GET_MESSAGES);
const [messages, setMessages] = useState([]);
const [senderId, setSenderId] = useState('');
const [receiverId, setReceiverId] = useState('');
const [content, setContent] = useState('');
useEffect(() => {
if (data) {
setMessages(data.messages);
}
}, [data]);
useEffect(() => {
socket.on('receiveMessage', (message) => {
setMessages((prevMessages) => [...prevMessages, message]);
});
return () => {
socket.off('receiveMessage');
};
}, []);
const handleSubmit = (e) => {
e.preventDefault();
socket.emit('sendMessage', { senderId: parseInt(senderId), receiverId: parseInt(receiverId), content });
setSenderId('');
setReceiverId('');
setContent('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Messages</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
placeholder="Sender ID"
value={senderId}
onChange={(e) => setSenderId(e.target.value)}
/>
<input
type="number"
placeholder="Receiver ID"
value={receiverId}
onChange={(e) => setReceiverId(e.target.value)}
/>
<textarea
placeholder="Message Content"
value={content}
onChange={(e) => setContent(e.target.value)}
></textarea>
<button type="submit">Send Message</button>
</form>
<ul>
{messages.map((msg) => (
<li key={msg.id}>
<strong>{msg.sender.username}</strong> to <strong>{msg.receiver.username}</strong>: {msg.content} <em>at {msg.timestamp}</em>
</li>
))}
</ul>
</div>
);
}
4. Update Announcements Page
Update the announcements page to use WebSocket for real-time notifications.
pages/announcements.js
import { useState, useEffect } from 'react';
import { useQuery, gql } from '@apollo/client';
import socket from '../lib/socket';
const GET_ANNOUNCEMENTS = gql`
query GetAnnouncements {
announcements {
id
title
content
createdAt
}
}
`;
export default function Announcements() {
const { loading, error, data, refetch } = useQuery(GET_ANNOUNCEMENTS);
const [announcements, setAnnouncements] = useState([]);
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
useEffect(() => {
if (data) {
setAnnouncements(data.announcements);
}
}, [data]);
useEffect(() => {
socket.on('newAnnouncement', (announcement) => {
setAnnouncements((prevAnnouncements) => [...prevAnnouncements, announcement]);
});
return () => {
socket.off('newAnnouncement');
};
}, []);
const handleSubmit = (e) => {
e.preventDefault();
socket.emit('createAnnouncement', { title, content });
setTitle('');
setContent('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Announcements</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
placeholder="Content"
value={content}
onChange={(e) => setContent(e.target.value)}
></textarea>
<button type="submit">Create Announcement</button>
</form>
<ul>
{announcements.map((ann) => (
<li key={ann.id}>
<strong>{ann.title}</strong> - {ann.content} <em>at {ann.createdAt}</em>
</li>
))}
</ul>
</div>
);
}
Running the Application
- Start the NestJS server with WebSocket support.
- Start the Next.js application.
- Test sending messages and creating announcements to see real-time updates.
This setup provides real-time notifications for messaging and announcements using WebSockets, enhancing the user experience with immediate updates. You can expand this further by adding more features and optimizations as needed.
If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!
Disclaimer: This content in generated by AI.
Top comments (0)