I've deployed a socket.io messaging app using Railway, but I'm getting a 404 error when connecting at the root URL

200 Views Asked by At

I recently built my first messaging app using Node.js, React, Express, and Socket.io. It works perfectly while in development environment on my localhost, but in production I am getting a 404 error when trying to connect to the root URL.

The app is deployed on Railway, no issues are coming up in the build or deploy logs but the 404 error appears when I try to navigate to the live URL. The server logs that it's successfully listening to a port in the deploy logs on Railway.

Chat-GPT couldn't find any issues in my code, and I'm not getting any helpful error messages in the browser console aside from a basic 404. I tried to research whether Socket.io is compatible with Railway.app, but couldn't find anything.

Here's the app and my code. If you need any other code snippets please let me know:

Repo

Deployed URL

client/Home.tsx

import React from 'react'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'

import { io } from 'socket.io-client'

export const socket = io('https://messaging-app-websockets.up.railway.app/', {
  transports: ['websocket'],
})

socket.on('connect', () => {
  console.log(socket.id)
})

socket.on('disconnect', () => {
  console.log('Disconnected', socket.id)
})

function Home() {
  const navigate = useNavigate()
  const [username, setUsername] = useState('')

  function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault()

    localStorage.setItem('username', username)
    socket.emit('newUser', { username, id: socket.id })
    navigate('/chat')
  }

  return (
    <form onSubmit={handleSubmit}>
      <h2>Sign in to start messaging</h2>
      <label htmlFor="username">Username</label>
      <input
        type="text"
        minLength={5}
        name="username"
        id="username"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
      />
      <button>Sign in</button>
    </form>
  )
}

export default Home

server/index.ts

import * as Path from 'node:path'
import * as URL from 'node:url'
import express from 'express'
import cors from 'cors'
import { createServer } from 'http'
import { Server } from 'socket.io'
import dotenv from 'dotenv'
import { User } from '../types/User'

const __filename = URL.fileURLToPath(import.meta.url)
const __dirname = Path.dirname(__filename)

const server = express()
const app = express()
const httpServer = createServer(app)
console.log(httpServer)
const io = new Server(httpServer, {
  cors: { origin: 'https://messaging-app-websockets.up.railway.app/' },
})

dotenv.config()

const port = process.env.PORT || 3000

httpServer.listen(port, () => {
  console.log('Server listening on port', port)
})

server.use(express.json())
server.use(express.static(Path.join(__dirname, 'public')))
server.use(cors())

if (process.env.NODE_ENV === 'production') {
  server.use(express.static(Path.resolve('public')))
  server.use('/assets', express.static(Path.resolve('./dist/assets')))
  server.get('*', (req, res) => {
    res.sendFile(Path.resolve('./dist/index.html'))
  })
}

let users = [] as User[]

io.on('connection', (socket) => {
  console.log(`client ${socket.id} just connected`)

  socket.on('message', (data) => {
    io.emit('messageResponse', data)
  })

  socket.on('newUser', (data) => {
    users.push(data)
    io.emit('newUserResponse', users)
  })

  socket.on('disconnect', () => {
    console.log(`client ${socket.id} disconnected`)
    users = users.filter((user) => user.id !== socket.id)
    io.emit('newUserResponse', users)
    socket.disconnect()
  })
})

export default server

Thanks in advance!

0

There are 0 best solutions below