How to make realtime APIs with NodeJS and ReactJS using Socket.io
Tags: node, react, javascript, webdev
Published: 2021-08-25 20:32:25

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8i97vqaa47t04jvmdn07.png)

We all love design patterns, and we all wonder when it's best to use them, I'm going to use one of them to apply one business case that you might stumble upon in work. The pattern I'm talking about is "[Publisher Subscriber](https://dev.to/jamesrweb/the-publisher-subscriber-pattern-pb)".

Today I'm going to make a **realtime API** that updates all the connected clients to it whenever and actions takes place on the db, so a super admin user using a dashboard can instantly know if other admins have signed in or out without refreshing the page every couple of seconds, other case is instantly knowing that an order is received on the platform you are working on.

### This tutorial, I'm going to use:
* NodeJS with Express for server side logic
* ReactJS to build a simple client app
* Socket.io for realtime connection between both sides

### To follow along, you can write the code step by step as I'll cover most of it, or you can clone the two repos:
* [server](https://github.com/omar-diaa-48/real-time-API-server)
* [client](https://github.com/omar-diaa-48/real-time-API-client)


First lets setup our server we start by initializing the folder structure
```
npm init -y
```

then we add the packages we use, in this tutorial I'm going to use ES6 syntax in the backend so we need babel to bundle our code, beside some other libraries we will use later on.
```
npm add nodemon dotenv  babel-loader 
@babel/preset-env @babel/node @babel/core -D
```

these are devDependencies, that's why we use -D flag because we dont need them for more than development.

1.nodemon for hot running 
2.dotenv for .env configuration
3.babel stuff for bundling 

now for the heavy lifters
```
npm add express mongoose socket.io
```

1.express to setup our server
2.mongoose to connect to our mongodb 
3.socket.io the one responsible for the realtime connection

now that was a bit boring, let's write some Javascript

`index.js`
```
import express from 'express'
import dotenv from 'dotenv'

dotenv.config()

const app = express()


app.get('/', (req,res)=>{
   res.send('Hello')
})

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
  console.log(`Server up and running on port ${PORT}`);
})
```


before running this code you have to setup some configuration

`.env`
```
PORT=5000
MONGO_DB_URL=mongodb://localhost:27017
MONGO_DB_DBNAME=store
```

`.babelrc`
```
{
  "presets": [
    "@babel/preset-env"
  ]
}
```

`package.json`
```
....
  "scripts": {
    "start": "babel-node index.js",
    "dev": "nodemon --exec babel-node index.js"
  },
....
```

now when you type `npm run dev`, you will find the server up and running and if you type in your browser `http://localhost:5000` you will get the following:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdysy1ucisb952fbvptv.png)


now let's make three folders and adjust our code as follows:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ujk2bazt40uihht45x6.JPG)


then for better environment variables handling
`config/variables.js`
```
import dotenv from 'dotenv'
dotenv.config()

const DB_URL = `${process.env.MONGO_DB_URL}/${process.env.MONGO_DB_DBNAME}`;
const PORT = process.env.PORT;

export {
  DB_URL,
  PORT
}
```

initialize and connect to database
`config/db.js`
```
import {DB_URL} from '../config/variables'

mongoose.connect(DB_URL, {
  useNewUrlParser:true,
  useUnifiedTopology:true
}, () => {
  console.log(DB_URL);
  console.log(`DB up and running`);
})
```

order model
`models/order.js`
```
import mongoose, {Schema} from 'mongoose'

const schema = new Schema({
  customer:{
    type:String,
    required:true
  },
  price:{
    type:Number,
    required:true
  },
  address:{
    type:String,
    required:true
  }
}, {
  timestamps:true
}) 

const Order = mongoose.model('order', schema)

export default Order;
```

order controller
`controllers/order.js`
```
import express from 'express'
import Order from '../models/order'
import {io} from '../index' 

const router = express.Router()

router.get('/', async (req, res) => {
  try {
    const orders = await Order.find()
    res.send(orders)
  } catch (error) {
    res.send(error)
  }
})

router.post('/', async (req, res) => {
  try {
    const order = new Order(req.body)
    await order.save()
    res.status(201).send(order)
  } catch (error) {
    res.send(error)
  }
})

export default router
```

now the important part 
`index.js`
```
import express from 'express'
import {PORT} from './config/variables'
import cors from 'cors'
import http from 'http'
// import { Server } from 'socket.io';
import socketIO from 'socket.io';
// import './config/sockets'
import './config/db'

import orderRouter from './controllers/order'

const app = express()
const server = http.createServer(app)
const io = socketIO(server, {
  transports:['polling'],
  cors:{
    cors: {
      origin: "http://localhost:3000"
    }
  }
})

io.on('connection', (socket) => {
  console.log('A user is connected');

  socket.on('message', (message) => {
    console.log(`message from ${socket.id} : ${message}`);
  })

  socket.on('disconnect', () => {
    console.log(`socket ${socket.id} disconnected`);
  })
})

export {io};


app.use(express.json())
app.use(cors())
app.use('/orders', orderRouter)

app.get('/', (req,res) => {
  res.send('Hello')
})

server.listen(PORT, () => {
  console.log(`Server up and running on port ${PORT}`);
})
```


## let me explain what happened here


the way we configure the server will differ when using socket.io because it deals with the server instance itself so 
```
const server = http.createServer(app)
```
then we wrap it with io, allow some cors which will be the client side after a short while on port 3000
```
const io = socketIO(server, {
  transports:['polling'],
  cors:{
    cors: {
      origin: "http://localhost:3000"
    }
  }
})
```

configuring io and exporting it to be used in the order controller
```
io.on('connection', (socket) => {
  console.log('A user is connected');

  socket.on('message', (message) => {
    console.log(`message from ${socket.id} : ${message}`);
  })

  socket.on('disconnect', () => {
    console.log(`socket ${socket.id} disconnected`);
  })
})

export {io};
```

then we go the order controller and change the code to
`controllers/order.js`
```
router.post('/', async (req, res) => {
  try {
    const order = new Order(req.body)
    await order.save()
    const orders = await Order.find()
    io.emit('order-added', orders)
    res.status(201).send(order)
  } catch (error) {
    res.send(error)
  }
})
```
which means that whenever someone will add an order, it will be posted to all clients connected the socket, so will be updated instantly with the orders array in the db


Now we can go to the client side and consume this API, we use `create-react-app` because we don't need a complex app we just need to demonstrate the behavior

here, I made a simple ui components called Orders, for the code you can easily find it in the repo, but I'm interested in this part 
```
  const [orders, setOrders] = useState([])

  useEffect(() => {
    const getOrders = async () => {
      const response = await axios.get('http://localhost:5000/orders')
      const ordersData = response.data;
      setOrders(ordersData)
    } 

    getOrders()
  }, [])

  useEffect(() => {
    const socket = io('ws://localhost:5000')

    socket.on('connnection', () => {
      console.log('connected to server');
    })

    socket.on('order-added', (newOrders) => {
      setOrders(newOrders)
    })

    socket.on('message', (message) => {
      console.log(message);
    })

    socket.on('disconnect', () => {
      console.log('Socket disconnecting');
    })

  }, [])
  
```

first we have the state which is an empty array initially

the first useEffect call is a call to the get orders endpoint we have just made to get all orders and then we populate the view with it

the second useEffect call, we connect using `socket.io-client` which we will install on the client side using `npm i socket.io-client`, then we specify that on *order-added* event from the socket we will have the orders being sent with the event and set it to be the new array, so whenever a new order is added we will be notified with the new array of orders in the db.


to test it, I opened the browser on port 3000 to open my react app then used postman to make a post to my server on port 5000 to add an order and viola my react-app updated instantly

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kyer42z499frld7ozqhg.gif)


**That was my first post, I hope you liked it.**