4
\$\begingroup\$

I build a RESTful CRUD API with Node.js and Express. This application allows, for example, to create a table on the client side with the ability to add, update and remove data by ID. I generate an array of random data for the availability of data when you start working on the client side with the table. I will be grateful for tips on improving the style.

const express = require('express')
const bodyParser = require('body-parser')
const crypto = require("crypto");
const cors = require('cors')

const getId = () => crypto.randomBytes(16).toString("hex");
const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min)) + min;
const nameList = ['Andrew', 'John', 'Sebastian', 'Owen', 'Luke', 'Anthony', 'Dylan']
const cityList = ['Riga', 'Berlin', 'Paris', 'Tokyo', 'London', 'Milan', 'Madrid']
const colorList = ['red', 'blue', 'green']
const getRandomStr = (str) => (str[getRandomInt(0, str.length)])

const app = express()

const names = [
  { id: getId(), 
    name: 'Li', 
    city: 'Tokyo', 
    color: 'blue', 
    answers: '10', 
    reputation: '1725' }
]

const generateNames = () => {
  for (let i=0; i<50; i++){
  const newId = getId()
  const newName = getRandomStr(nameList) 
  const newCity = getRandomStr(cityList)
  const newColor = getRandomStr(colorList)
  const newAnswers = getRandomInt(10, 20).toString()
  const newReputation = getRandomInt(1500, 2000).toString()
  names.push({ id: newId, name: newName, city: newCity, 
  color: newColor, answers: newAnswers, reputation: newReputation })
  }
}
generateNames()

app.use(bodyParser.json())

app.use(cors())

app.get('/names', (req, res) => {
  res.json(names)
})

app.post('/names', (req, res) => {
  const newId = getId() 
  const newName = req.body.name 
  const newCity = req.body.city
  const newColor = req.body.color
  const newAnswers = req.body.answers
  const newReputation = req.body.reputation
  names.push({ id: newId, name: newName, city: newCity, 
  color: newColor, answers: newAnswers, reputation: newReputation })
  res
    .status(201) 
    .json({ id: newId }) 
})

app.put('/names', (req, res) => {
  const { id } = req.body
  const indexToEdit = names.findIndex(obj => obj.id === id )
  names[indexToEdit].name = req.body.name
  names[indexToEdit].city = req.body.city
  names[indexToEdit].color = req.body.color
  names[indexToEdit].answers = req.body.answers
  names[indexToEdit].reputation = req.body.reputation
  const editName = names[indexToEdit].name
  const editCity = names[indexToEdit].city
  const editColor = names[indexToEdit].color
  const editAnswers = names[indexToEdit].answers
  const editReputation = names[indexToEdit].reputation
  res
    .status(200)
    .json({ name: editName, city: editCity, 
      color: editColor, answers: editAnswers, reputation: editReputation})
})

app.delete('/names', (req, res) => {
  const { id } = req.body 
  const indexToRemove = names.findIndex(obj => obj.id === id )
  names.splice(indexToRemove, 1) 
  res
    .status(200)
    .json({ status: 'success'})
})

app.listen(3000, () => {
  console.log('listening on 3000')
})
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

I know you are generating data for the sake of completion but it does complicate things on what aspect of the code you'd like to focus on. But here are a couple thoughts.

With Express, I often like to treat the routes as controllers in an MVC pattern. So any fetching of data and mutating data, I would create at least a model per resource to encapsulate that part of the code. So the interface may look something like the below:

app.get('/names', async (req, res) => {
  // assuming we are getting data from an async call to a db
  const names = await Name.all(); 
  res.json(names)
})

app.post('/names', async (req, res) => {
  const newName = await Name.create(req.body);

  res
    .status(201) 
    .json({ id: newName.id }) 
})

app.put('/names', async (req, res) => {
  const { id } = req.body
  const name = await Name.findAndUpdate(id, req.body);
  res
    .status(200)
    .json(name)
})

app.delete('/names', async (req, res) => {
  const { id } = req.body 
  const deleted = await Name.deleteById(id)
  res
    .status(200)
    .json({ status: 'success'})
})

Notice that we can implement "Name" class with whatever database(or file or no database) we want. As long as we keep the interface the same, we can swap different "Name" class implementations without having to change our Express routes.

One last point, since there is no "view" suggested here but we can have it if we want. For example, if for some reason, our model doesn't return us exactly what we need, maybe the key is slightly off from our API specifications, we can introduce a view layer that maps whatever the model returns us. This way we can continue to keep our Express routes very thin and handling specific to request/response related responsibilities.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.