Проблема в обработчике создания пользователя Node.js

Возникла проблема с "app.post" пытаюсь реализовать роут создания пользователя. Должно получиться так, что-бы при добавлении пользователя c переданными данными ему присваивался свой id, но получается так, что создается только один пользователь и все. Все остальное работает корректно, проверка, изменение, удаление и поиск по id (пользуюсь Postman)

const express = require('express');
const Joi = require('joi');
const fs = require('fs');
const path = require('path');

const app = express();

const users = [];

let uniqueID = 0;

const userScheme = Joi.object({
    firstName: Joi.string().min(2).required(),
    secondName: Joi.string().min(2).required(),
    age: Joi.number().min(0).max(150).required(),
    city: Joi.string().min(3)
})


app.use(express.json())

app.get('/users', (req, res) => {
    const pathUsers = path.join(__dirname, 'users.json');
    const usersData = JSON.parse(fs.readFileSync(pathUsers, 'utf-8'));

    res.send({usersData});
});

app.post('/users', (req, res) => {
    uniqueID += 1;
    users.push({
        id: uniqueID,
        ...req.body
    });

    fs.writeFileSync(path.join(__dirname, 'users.json'), JSON.stringify(users, null, 2));

    res.send({id: uniqueID});
});

app.put('/users/:id', (req, res) => {
    const pathUsers = path.join(__dirname, 'users.json');
    const usersData = JSON.parse(fs.readFileSync(pathUsers, 'utf-8'));

    const result = userScheme.validate(req.body);
    if (result.error) {
        return res.status(404).send({error: result.error.details})
    }

    const userId = +req.params.id;

    const user = usersData.find((user) => user.id === userId);

    if(user) {
       const {firstName, secondName, age, city} = req.body;

       user.firstName = firstName,
       user.secondName = secondName,
       user.age = age,
       user.city = city

       fs.writeFileSync(path.join(__dirname, 'users.json'), JSON.stringify(usersData, null, 2));

       res.send({user})
    } else {
        res.status(404);
        res.send({user: null});
    }
});

app.get('/users/:id', (req, res) => {
    const pathUsers = path.join(__dirname, 'users.json');
    const usersData = JSON.parse(fs.readFileSync(pathUsers, 'utf-8'));

    const userId = +req.params.id;
    const user = usersData.find((user) => user.id === userId);

    if(user) {
       res.send({user})
    } else {
        res.status(404);
        res.send({user: null});
    }
});

app.delete('/users/:id', (req, res) => {
    const pathUsers = path.join(__dirname, 'users.json');
    const usersData = JSON.parse(fs.readFileSync(pathUsers, 'utf-8'));

    const userId = +req.params.id;
    const user = usersData.find((user) => user.id === userId);

    if(user) {
        const usrIndex = users.indexOf(user);
        usersData.splice(usrIndex, 1);

        fs.writeFileSync(path.join(__dirname, 'users.json'), JSON.stringify(usersData, null, 2));
        
        res.send({user})
    } else {
        res.status(404);
        res.send({user: null});
    }
});

app.listen(3000);

Так-же пробовал:

app.post('/users', (req, res) => {
    const pathUsers = path.join(__dirname, 'users.json');
    const usersData = JSON.parse(fs.readFileSync(pathUsers, 'utf-8'));

    uniqueID += 1;
    usersData.push({
        id: uniqueID,
        ...req.body
    });

    fs.writeFileSync(path.join(__dirname, 'users.json'), JSON.stringify(usersData, null, 2));

    res.send({id: uniqueID});
});

Но в данном случае он добавляет пользователей(если файл users.json существует), но с одинаковым id и при том, что если файла users.json нет, то он не создает его и выдает ошибку


Ответы (2 шт):

Автор решения: ksa

получается так, что создается только один пользователь и все

У меня все нормально инкрементируется и создается... Для простоты все делается без учета протокола, только анализ УРЛа.

const http = require('http');

let id = 0
const arr = []

http.createServer(function (request, response) {
    const url = request.url
    response.setHeader(
        'Content-Type',
        'text/html; charset=utf-8;'
    );
    response.write('<!DOCTYPE html>');
    response.write('<html>');
    response.write('<body>');
    if (url == '/add') {
        arr.push({
            id: ++id,
            date: new Date()
        })
        response.write(`<p>Пользователь добавлен. ИД ${id}</p>`);
    } else {
        response.write('<p>Пользователи</p>');
        response.write('<ul>');
        arr.forEach(o => {
            response.write('<ll>');
            response.write(`ИД ${o.id} дата ${o.date.toString()}`);
            response.write('</ll>');
        })
        response.write('</ul>');
    }
    response.write('</body>');
    response.write('</html>');
    response.end();
}).listen(3000);
→ Ссылка
Автор решения: nörbörnën

В вашем коде есть две проблемы.

Первая, простая проблема, состоит в том, что счётчик uniqueID обнуляется при каждом рестарте сервиса. Решается эта проблема тоже просто - нужно хранить последнее значение счётчика в файле users.json.

Вторая, сложная проблема. В каждом обработчике запроса вы делаете одно и то же: открываете файл, совершаете магию с данными, записываете в файл что-то. Такой подход даже на ваших 5 обработчиках приводит к тому, что каждое изменение бизнес-логики должно дублироваться, что является прямой дорогой к ошибкам. Короче, запросы должны быть отдельно, работа с хранилищем данных должна быть отдельно. Посмотрите, как я написал getRepository, даже в нём работа с файлом отделена от работы с данными:

// @ts-check

/**
 * @typedef {{
 *   firstName: string;
 *   secondName: string;
 *   age: number;
 *   city?: string;
 * }} IUserModel
 */
/**
 * @template [T = Record<string, any> & { id: number }]
 * @typedef {{
 *  seq: number;
 *  records: Record<string, T & { id: number }>;
 * }} IRecordsStorage
 */
const fs = require('node:fs');
const path = require('node:path');

const express = require('express');
const Joi = require('joi');

const app = express();
app.use(express.json());

/** @type {import('joi').ObjectSchema<IUserModel>} */
const userScheme = Joi.object({
  firstName: Joi.string().min(2).required(),
  secondName: Joi.string().min(2).required(),
  age: Joi.number().min(0).max(150).required(),
  city: Joi.string().min(3)
});

/** @type {ReturnType<typeof getRepository<IUserModel>>} */
const UsersStorage = repositoryFactory('users.json');


app.get('/users', (req, res) => {
  res.json({ data: UsersStorage.findMany() });
});

app.post('/users', (req, res) => {
  const record = UsersStorage.insert(req.body);
  res.json(record);
});

app.put('/users/:id', (req, res) => {
  const { error } = userScheme.validate(req.body);
  if (error) {
    return res.status(404).send({ error: error.details })
  }
  
  const userId = +req.params.id;
  if (!UsersStorage.findOne(userId)) {
    return res.status(404).send();
  }

  const record = UsersStorage.update({ ...req.body, id: userId });
  res.json(record);
});

app.get('/users/:id', (req, res) => {
  const record = UsersStorage.findOne(+req.params.id);
  if (!record) {
    return res.status(404).send();
  }
  res.json(record);
});

app.delete('/users/:id', (req, res) => {
  const userId = +req.params.id;
  UsersStorage.delete(userId);
  res.status(204).send();
});

app.listen(3000, () => console.log('server started...'));

/**
 * @template [T = Record<string, any>]
 * @param {string} filename
 * @returns {ReturnType<typeof getRepository<T>>}
 */
function repositoryFactory(filename) {
  /** @type {Map<string, ReturnType<typeof getRepository<T>>>} */
  const repositoryMap = new Map();
  const result = repositoryMap.get(filename) || getRepository(filename);
  if (!repositoryMap.has(filename)) {
    repositoryMap.set(filename, result);
  }
  return result;
}

/**
 * @template [T = Record<string, any> & { id: number }]
 * @param {string} filename 
 */
function getRepository(filename) {
  const filepath = path.join(__dirname, filename);

  /** @type {IRecordsStorage<T>} */
  let storage = { seq: 0, records: {} };
  try {
    storage = JSON.parse(fs.readFileSync(filepath, 'utf-8'));
    storage.seq ||= 0;
    storage.records ||= {};
  } catch { }

  const sync = () => fs.writeFileSync(filepath, JSON.stringify(storage, null, 2));

  return {
    findOne(/** @type {string | number} */ id) {
      const record = storage.records?.[id];
      return record || null;
    },
    findMany() {
      const records = Object.values(storage.records);
      records.sort((a, b) => a.id - b.id);
      return records;
    },
    insert(/** @type {T} */ data) {
      const record = { ...data, id: ++storage.seq };
      storage.records[record.id] = record;
      sync();
      return record;
    },
    update(/** @type {IRecordsStorage<T>['records'][string]} */ item) {
      const record = storage.records[item.id] = item;
      sync();
      return record;
    },
    delete(/** @type {IRecordsStorage<T>['records'][string] | number | string} */ itemOrId) {
      const id = typeof itemOrId === 'number' || typeof itemOrId === 'string'
        ? itemOrId
        : itemOrId.id;
      delete storage.records[id];
      sync();
    },
  };
}

→ Ссылка