Получить всех юзеров из файла users.json, если файла нет, получить данные с сервиса
Нужна помощь с серверной частью. Имеется такое задание: Создать следующее api get /api/v1/users - получает всех юзеров из файла users.json, если его нет - получает данные с сервиса https://jsonplaceholder.typicode.com/users, заполняет файл users.json полученными данными и возвращает эти данные пользователю.
И вся проблема в том, что я не понимаю, чего я не понимаю. Пока что какие-то такие мысли:
readFile(`${__dirname}/test.json`, { encoding: "utf8" })
.then(text => {
const { users } = req.params
res.json({ users })
})
.catch(err => {
axios('https://jsonplaceholder.typicode.com/users')
.then((response) => response.data)
.then(data => {
/* тело ответа со стороннего сервера */
writeFile(`${__dirname}/test.json`, data, { encoding: "utf8" });
res.json({ users })
})
})
Ответы (1 шт):
Нужно проверить есть ли файл на диске. Затем, если файл есть на диске, его нужно вернуть вызовом res.sendFile. Если файла на диске нет, запросить у сервиса список пользователей, записать данные в файл и отдать результатом запроса.
При запросе списка пользователей у сервиса нет смысла разделять "получение и запись данных" и "оправку результатов запроса", всё пустим параллельно, используя stream-ы.
const path = require('path');
const fs = require('fs');
const express = require('express');
const { default: axios } = require('axios');
const app = express();
app.get('/api/v1/users', (req, res) => {
const filepath = path.join(__dirname, 'users.json');
fs.promises.access(filepath, fs.constants.R_OK)
.then(() => res.sendFile(filepath))
.catch(() => axios.get(
'https://jsonplaceholder.typicode.com/users',
{ responseType: 'stream' }
).then(({ data }) => {
console.log('by_service');
data.pipe(fs.createWriteStream(filepath));
data.pipe(res);
}))
.catch((err) => {
console.error('c', err);
res.sendStatus(500);
});
});
const EXPRESS_HOST = 'localhost';
const EXPRESS_PORT = 3000;
app.listen(EXPRESS_PORT, EXPRESS_HOST, () => console.log(`Server started on port http://${EXPRESS_HOST}:${EXPRESS_PORT}`));
И более современный вариант с async/await
app.get('/api/v1/users', async (req, res) => {
const filepath = path.join(__dirname, 'users.json');
try {
await fs.promises.access(filepath, fs.constants.R_OK);
return res.sendFile(filepath);
} catch (err) { /* */ }
const { data } = await axios.get(
'https://jsonplaceholder.typicode.com/users',
{ responseType: 'stream' }
);
console.log('by_service');
data.pipe(fs.createWriteStream(filepath));
data.pipe(res);
});
И ещё вариант, который будет корректно работать в случае когда users.json отсутствует и несколько клиентов одновременно делают запрос к вашему api:
app.get('/api/v1/users', async (req, res) => {
const filepath = path.join(__dirname, 'users.json');
try {
await fs.promises.access(filepath, fs.constants.R_OK);
return res.sendFile(filepath);
} catch (err) { /* */ }
const { data } = await axios.get(
'https://jsonplaceholder.typicode.com/users',
{ responseType: 'stream' }
);
// клиенту сразу отдаётся ответ сервиса
data.pipe(res);
// запись данных во временный файл и переименование после записи
const tempfile = `${filepath}.${Math.random()}`;
const stream = fs.createWriteStream(tempfile);
stream.on('error', console.error);
stream.on('finish', () => {
try {
fs.accessSync(filepath, fs.constants.R_OK);
} catch (error) {
fs.renameSync(tempfile, filepath);
}
});
data.pipe(stream);
});