let's say I have a REST API in express. For example this endpoint
async function getStoreController(req, res, next) {
try {
const { id } = req.params;
const storeData = await Stores.getStoreById(id);
if (!Object.keys(storeData).length) {
return next(
new OperationError(`There is no sotre with id ${id}`, 404, "error")
);
}
res.status(200).json({
status: "success",
data: storeData,
});
} catch (err) {
next(err);
}
}
I want to use the same data in my view in another endpoint and when I use the previous function as a middleware or even call it then render the view an error will occur because I finished the request already. Is there a solution ? and should I make a function that only compute and return the data so if there is an REST API or web application wants this data then both of them can call the same function but the first one return JSON and the second one render a view ?
I tried a solution which is sending a fetch request to the previous endpoint and get the data then render it in my view.
Edit
async function store(req, res, next) {
try {
// Get the data from that endpoint
const data = getStoreData(); // simulate calling that endpoint
res.render("store", data);
} catch (err) {
next(err);
}
}
for example that endpoint uses the same data but suppose the function must validate and some other logic so I don't wanna repeat myself
let's say I have a REST API in express. For example this endpoint
async function getStoreController(req, res, next) {
try {
const { id } = req.params;
const storeData = await Stores.getStoreById(id);
if (!Object.keys(storeData).length) {
return next(
new OperationError(`There is no sotre with id ${id}`, 404, "error")
);
}
res.status(200).json({
status: "success",
data: storeData,
});
} catch (err) {
next(err);
}
}
I want to use the same data in my view in another endpoint and when I use the previous function as a middleware or even call it then render the view an error will occur because I finished the request already. Is there a solution ? and should I make a function that only compute and return the data so if there is an REST API or web application wants this data then both of them can call the same function but the first one return JSON and the second one render a view ?
I tried a solution which is sending a fetch request to the previous endpoint and get the data then render it in my view.
Edit
async function store(req, res, next) {
try {
// Get the data from that endpoint
const data = getStoreData(); // simulate calling that endpoint
res.render("store", data);
} catch (err) {
next(err);
}
}
for example that endpoint uses the same data but suppose the function must validate and some other logic so I don't wanna repeat myself
I see what you're trying to do but it's probably a bit too much.
You want to go from this:
async function store(req, res, next) {
try {
const { id } = req.params;
const storeData = await Stores.getStoreById(id);
if (!storeData) {
return next(
new OperationError(`There is no store with id ${id}`, 404, "error")
);
}
res.render("store", data);
} catch (err) {
next(err);
}
}
To something like this:
const storeMiddleware = (req, res, next) => {
const { id } = req.params
const storeData = await Stores.getStoreById(id)
if (!storeData) {
return next(
new OperationError(`There is no store with id ${id}`, 404, "error")
);
}
req.store = storeData
next()
}
app.get('/store/:id', storeMiddleware, store)
app.get('/api/store/:id', storeMiddleware, getStoreController)
async function store(req, res, next) {
try {
const storeData = req.storeData
res.render("store", data);
} catch (err) {
next(err);
}
}
It seems nice, we removed 6 lines of code. But we also added 4. So we really only saved 2 lines.
Also we created indirection. Looking at the store
function we can't immediately see where it's getting the storeData
from. We're redirected to route declaration (app.get
) and then the middleware. It's not really worth it for 2 lines of code less if you ask me.
It would make more sense if you send different responses based on the Accept
header:
app.get('/store/:id', (req, res, next) => {
const { id } = req.params;
const storeData = await Stores.getStoreById(id);
if (!storeData) {
return next(
new OperationError(`There is no store with id ${id}`, 404, "error")
);
}
// will check whether Accept http header contains 'application/json'
if (req.accepts('json')) {
return res.status(200).json({
status: "success",
data: storeData,
});
}
res.render("store", data);
})
So now the user can explicitly ask your server for a JSON representation. Otherwise you render an html page.