Introduction to the

😛 wanted to build a chat room in their spare time to consolidate front-end skills, so they started the development of Astro Boy’s chat room on June 24, 2020. The 😈 project was developed in full typescript to lay the foundation for future functional iterations. Of course, I love typescript myself.

Program interface

Function is introduced

  • Mobile compatibility
  • Modify user information (profile picture/User name/password)
  • Group chat/private chat
  • Create group/join group/drop group/fuzzy search group
  • Add friends/Delete friends/blur search users
  • News page
  • Expression package
  • Picture sending/picture preview
  • Online head count
  • Custom Background
  • Reconnection to remind

An overview of the technology

  • Typescript: A superset of JavaScript that provides a type system and improves code readability and maintainability.
  • Vue2.6.x: Front-end progressive framework.
  • Socket/ IO: realtime communication, websocket third-party library.
  • Vuex: State management mode developed specifically for vue.js applications.
  • Nestjs is a framework for building efficient and extensible Node.js server applications. Written in TypeScript, Nestjs combines OOP1, FP2, and FRP3 concepts.
  • Typeorm: Supports the latest JavaScript features and provides additional features to help you develop any application that uses a database.
  • ES6+ : Use ES6+ syntax, arrow functions, async/await, etc.
  • SASS(SCSS) : USING SCSS as a CSS preprocessor language allows you to create complex designs with a small amount of code in the most efficient way possible.

Why use typescript

  1. Because chat rooms involve complex data structures, using typescript can avoid many of the simplest errors in development through compiler alerts.
  2. With typescript type definitions, the system’s data structures are easy to see, and code is much more maintainable and readable.

Database table structure design

The database uses six tables, respectively

  • The user the users table
  • Group, a group of tables
  • User_group Intermediate table of users _ groups
  • Group_message Group message table
  • User_friend Intermediate list of users _ Friends
  • Friend_message Indicates the private chat message table

The middle table is used to establish relationships between groups/friends and users. Here is my mind map, I believe you will understand it.

WebSocket setup logic

User room setup

Each user entering the chat room will automatically join a WebSocket room named public and a WebSocket room named after the user ID. The user room is set up to facilitate the system to individually broadcast events for the user. If you are not familiar with the concept of a room, you can assume that only the people in the room can receive the broadcast in the room. For more information, visit the website of socket. IO.

The establishment of group chat rooms

With groupId as the name of the WebSocket room, every time a new user joins the group, the event of the user joining the group will be broadcast in the group room along with the details of the new user, and then other users will store the information of the new user. When a new user sends a message, other users can find the details of the user based on the userId of the message. This ensures that other users can quickly know the owner of a message after it is sent.

Private chat room establishment

Whenever a request is made to add a friend, the WebSocket will use the string of the user’s userId and the friend’s userId as the room name, thus establishing a private chat room.

The backend architecture

The backend uses NestJS, the Node.js framework that has been developing rapidly in recent years. Nestjs has many advantages, but I’ll just name the following:

  1. Built on TypeScript and compatible with plain ES6.
  2. Nestjs’s idea of dependency injection and modularization makes the code structure clear and easy to maintain.
  3. Nestjs’s @nestJS/WebSockets package encapsulates the handling of WebSocket events, which has great advantages for developing chat rooms.

Here is some logic on the back end.

  1. Use NestJS to establish WebSocket connections
// chat.gateway.ts
@WebSocketGateway()
export class ChatGateway {
  // Socket connects to the hook
  async handleConnection(client: Socket): Promise<string> {
    let userRoom = client.handshake.query.userId;
    // Join the public room by default
    client.join('public');
    // User only message room according to userId
    if(userRoom) {
      client.join(userRoom);
    }
    return 'Connection successful'}}Copy the code
  1. Encapsulating global middleware for easy debugging during development.
// middleware.js
export function logger(req, res, next) {
  const { method, path } = req;
  console.log(`${method} ${path}`);
  next();
};

// main.js Use global Middleware app.use(Logger)Copy the code
  1. Static resource configuration for NestJS
// main.jsApp.usestaticassets (join(__dirname,'.. /public/'.'static'), {
  prefix: '/static/'});Copy the code
  1. Nestjs custom exception filter
// http-exception.filter.ts
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    const status = exception.getStatus();
    const exceptionRes: any = exception.getResponse();
    const {
      error,
      message,
    } = exceptionRes;
    // The following format is returned to the front end in case of an error request
    response.status(status).json({
      status,
      timestamp: new Date().toISOString(),
      path: request.url, error, message, }); }}Copy the code

The front-end architecture

Page initialization

Initialization will call up the WebSocket connection function, and then get all the user’s group information and all the friends information, and then join the corresponding room through the establishment of WebSocket communication rules, and then distribute the latest data using VUex.

The data processing

The data type of the group

/ / group
interface Group {
  groupId: string;
  userId: string; / / group manager id
  groupName: string;
  notice: string;
  messages: GroupMessage[];
  createTime: number;
}
Copy the code

The data type of the friend

/ / friends
interface Friend {
  userId: string; username: string; avatar: string; role? : string; tag? : string; messages: FriendMessage[]; createTime: number; }Copy the code

I used to use object arrays [friend1, friend2… This structure manages all the group/friend data, but querying and updating the group/friend data can become costly when the data volume is large. Every time your friend’s name changes or your avatar changes, you have to go through the array to update that information. Later I used the object structure to optimize the chat room code. I use an object called Gather to manage group/friend information. The key of Gather is groupId/userId and the value is the data of the corresponding group/friend. The structure is as follows

gather = {
 'userId': {
   userId: 'userId'
   username: 'xxx'
   messages: []; . }}Copy the code

Each group and user has a unique ID, so there is no need to worry about duplication. Using this structure, updating data is very easy, just take the ID you want to update and override the values of the Gather

vuex

Vuex is strong at dealing with business scenarios where chatrooms involve real-time updates of data and data synchronization between vUE components. I wrote the function to establish WebSocket connection in vuex action, and called the connection function after the user login successfully. The following is the simplified code.

// actions.ts
const actions: ActionTree<ChatState, RootState> = {
  // Initialize the WebSocket
  async connectSocket({commit, state, dispatch, rootState}, callback) {
    // The WebSocket connection is established
    socket.on('connect'.async() = > {// Subscribe to group message time
      socket.on('groupMessage'.(res: any) = > {
        console.log('on groupMessage', res)
        if(! res.code) {// Group messages are processed
          commit(ADD_GROUP_MESSAGE, res.data)
        }
      })
    }
  }
Copy the code

I have to say that the vuex-Class library has helped me a lot. It is a great glue for vuex development with typescript. With vuex-class, the method to call vuex from a VUE component simply reads:

// GenalChat.vue
import { namespace } from 'vuex-class'
const appModule = namespace('app')
export default class GenalChat extends Vue {
  @appModule.Getter('user') user: User;
  @appModule.Action('login') login: Function;
}
Copy the code

conclusion

🖖🏻 chat room has a complete chat function, at the same time, I will continue to develop more cool features in the future, like a friend to encourage me star!

The project address

Online: www.genal.fun github: Genal-chat