1

I'm trying to create a websocket endpoint for router in my express app with express-ws module but getting an error.

Here is an entry point of the server index.ts:

import express from 'express';
import expressWs from 'express-ws';
import * as dotenv from 'dotenv';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import mongoose from 'mongoose';
import auth from './routes/auth.route.js';
import data from './routes/data.route.js';
import user from './routes/user.route.js';
import logger from './utils/logger.js';
import errorMiddleware from './middlewares/error.middleware.js';

dotenv.config()

const PORT = process.env.PORT;
const ORIGIN = process.env.DOMAIN_URL;

const app = expressWs(express()).app;

const corsOptions = {
    origin: ORIGIN,
    credentials: true
}

app.use(express.json());
app.use(cookieParser());
app.use(cors(corsOptions));

app.use('/api/auth', auth);
app.use('/api/data', data);
app.use('/api/user', user);

app.use(errorMiddleware);

async function start() {
    try {
        await mongoose.connect(process.env.DB_URL)
    } catch (err) {
        logger.error(`An error occured while connecting to database: ${err}`)
    }
    app.listen(PORT, () => {
        console.log(`Server started on port ${PORT}`);
    });
}

start();

And here is data.route.ts:

import express from 'express';
import dataController from '../controllers/data.controller.js';
import authMiddleware from '../middlewares/auth.middleware.js';

const router = express.Router();

router.use(authMiddleware);

...
router.ws('/ws/tradebars', (ws, req) => {}); // TypeError: router.ws is not a function

export default router;
lian
  • 399
  • 1
  • 3
  • 16

2 Answers2

1

I had a similar issue. Problem is you're calling the ws function on router before you've called this line (because of the import): const app = expressWs(express()).app;

A possible solution is to wrap the ws call in a function that you can export, and call at a later point. For example (data.route.ts):

...
export const mountRouter = () => {
  router.ws('/ws/tradebars', (ws, req) => {}); 
}
export default router;

And then in your index.ts

import data, {mountRouter} from './routes/data.route.js'

const app = expressWs(express()).app;
mountRouter()

app.use('/api/data', data);
QWERTYUIOP
  • 46
  • 1
0

This option would be greater and faster, you don't want to create extra functions and you have nested paths for this websocket router like "api/calls/stream" which are divided into subrouter files. If you want to connect to router.ws via a function like mountRouter, you'll need to write the full path there.

In short, you can just import your routers directly in app.use():


// app.ts

import express from "express"
import initExpressWebsocket from "express-ws"|
import { sendErrorMessage } from './modules/errors/controller/error'

export async function createApp() {
    const app = express()
    initExpressWebsocket(app)

    app.get('/send-error-message', sendErrorMessage)

    // // In my case, I have general router index file with another subrourtes (it's better for architecture), however you can imports all routers manually:
    app.use("/api", (await import("./routes/index")).default)
    return app
}

// server.ts
import envConfig from './configs/config'
import { createApp } from './app'

async function main() {
  const app = await createApp()
  await initializeMongoose()
  const port = envConfig.port
  app.listen(port, async () => {
    logger.info(`Server listening on port ${port}`)
  })
}

main()

OneIvan
  • 11
  • 2