添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

In the last article, we cover the setup for the client-side of the application. Today we are going to look closely at server-side part. 🚀

Application API is written with Express framework for file serving and Websocket for communication. Entry file for server:

// ...
dotenv.config();
const port = process.env.PORT;
const app: Express = express();
const server = http.createServer(app);
app.use(express.static(path.join(__dirname, 'public')));
app.get('(/*)?', async (req, res, next) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
AppDataSource.initialize().then(async () => {
  console.info('Database connected');
}).catch((error) => {
  console.error(error);
const io = new Server<IncomingEvents, OutgoingEvents, {}, User>(server, {
  transports: ['websocket', 'polling'],
io.on('connection', (socket: Socket<IncomingEvents, OutgoingEvents, {}, User>) => {
  registerUsersHandlers(io, socket);
  registerCardsHandlers(io, socket);
  registerBoardsHandlers(io, socket);
server.listen(port, () => {
  // eslint-disable-next-line no-console
  console.log(`Server is running at http://localhost:${port}`);
export type IncomingUsersEvents = {
  Join: (data: {boardId: string, nickname: string; avatar: number;}) => void;
  SetSelectedPlanningCard: (data: {selectedPlanningCard: number}) => void;
  ToggleReady: () => void;
  ChangeUserData: (data: {nickname: string, avatar: number}) => void;
export type OutgoingUsersEvents = {
  Joined: (data: {
    localUser: RawUser,
    users: RawUser[],
    cards: RawCard[],
    board: {id: string, stage: number, maxVotes: number, timerTo: number, mode: string},
  }) => void;
  UserState: (data: {user: RawUser}) => void;
  UsersState: (data: {users: RawUser[]}) => void;
export type IncomingCardsEvents = {
  CreateCard: (data: {content: string, column: number}) => void;
  UpdateCard: (data: {cardId: string, content: string}) => void;
  DeleteCard: (data: {cardId: string}) => void;
  GetCards: () => void;
  GroupCards: (data: {cardId: string, stackedOn: string}) => void;
  UngroupCards: (data: {cardId: string}) => void;
  UpvoteCard: (data: {cardId: string}) => void;
  DownvoteCard: (data: {cardId: string}) => void;
export type OutgoingCardsEvents = {
  CardState: (data: {card: RawCard}) => void;
  DeleteCard: (data: {cardId: string}) => void;
  CardsState: (data: {cards: RawCard[]}) => void;
export type IncomingBoardsEvents = {
  SetTimer: (data: {duration: number}) => void;
  SetBoardMode: (data: { mode: string }) => void;
  SetMaxVotes: (data: {maxVotes: number}) => void;
  SetStage: (data: {stage: number}) => void;
export type OutgoingBoardsEvents = {
  BoardConfig: (data: {board: {
    stage: number,
      timerTo: number,
      maxVotes: number,
      mode: string,
  }}) => void;
export type IncomingEvents = IncomingUsersEvents & IncomingCardsEvents & IncomingBoardsEvents;
export type OutgoingEvents = OutgoingUsersEvents & OutgoingCardsEvents & OutgoingBoardsEvents;
    Enter fullscreen mode
    Exit fullscreen mode
const registerCardsHandlers = (
  io: Server<IncomingEvents, OutgoingEvents, {}, User>,
  socket: Socket<IncomingEvents, OutgoingEvents, {}, User>,
) => {
  socket.on('CreateCard', async ({ content, column }) => {
    try {
      if (Joi.string().min(1).max(512).validate(content).error) {
        console.error(`CreateCard: Invalid content: ${content}`);
        return;
      if (Joi.number().allow(0, 1, 2).validate(column).error) {
        console.error(`CreateCard: Invalid column: ${column}`);
        return;
      const card = await Cards.create({
        content,
        column,
        board: {
          id: socket.data.boardId,
        user: {
          id: socket.data.userId,
        stackedOn: '',
        votes: [],
      }).save();
      io.to(socket.data.boardId || '')
        .emit('CardState', { card: getRawCard(card) });
    } catch (error) {
      console.error(error);
// ...
export default registerCardsHandlers;
    Enter fullscreen mode
    Exit fullscreen mode

To communicate with the backend server is using TypeORM. Initially, it has been connecting to Postgres but for my purpose, it was overkill so I switched to SQLite which is faster to provision, develop and maintain in this small app. If you want to switch back to Postgres it's just changing a few lines in the dataSource config.

import { DataSource } from 'typeorm';
import dotenv from 'dotenv';
import Boards from './Boards';
import Cards from './Cards';
import Users from './Users';
import Votes from './Votes';
dotenv.config();
const AppDataSource = new DataSource({
  type: 'sqlite',
  database: './db.sqlite',
  synchronize: true,
  logging: true,
  entities: [Boards, Cards, Users, Votes],
  subscribers: [],
  migrations: [],
export default AppDataSource;
    Enter fullscreen mode
    Exit fullscreen mode
  RETRO= 'retro',
  PLANNING_HIDDEN = 'planning_hidden',
  PLANNING_REVEALED = 'planning_revealed',
@Entity()
export default class Boards extends BaseEntity {
  @PrimaryGeneratedColumn('uuid')
    id: string;
  @OneToMany(() => Cards, (card) => card.board)
    cards: Cards[];
  @OneToMany(() => Users, (user) => user.board)
    users: Users[];
  @Column({
    type: 'integer',
    name: 'stage',
    stage: number;
  @Column({
    type: 'integer',
    name: 'max_votes',
    maxVotes: number;
  @Column({
    type: 'varchar',
    name: 'mode',
    mode: string;
  @Column({
    name: 'timer_to',
    timerTo: Date;
  @CreateDateColumn({
    name: 'created_at',
    createdAt: Date;
  @UpdateDateColumn({
    name: 'updated_at',
    updatedAt: Date;
    Enter fullscreen mode
    Exit fullscreen mode
              How I created AI-powered ORM for PostgreSQL, MySQL and SQLite and why you shouldn't use it
                  #webdev
                  #programming
                  #javascript
              How to update a few years old outdated project in Node and React?
                  #webdev
                  #javascript
                  #tutorial
                  #react
              Data Structures using JavaScript for beginners
                  #javascript
                  #beginners
                  #webdev
                  #tutorial
      

Built on Forem — the open source software that powers DEV and other inclusive communities.

Made with love and Ruby on Rails. DEV Community © 2016 - 2024.