How to decide between RPC and REST for APIs? - node.js

I have made an application where the user will send the Program in either C or Python and Input via STDIN, and my API will return back the output of Program in JSON Format.
Nodejs/Expressjs Code :
route.post('/exec', (req, res, next) => {
if(req.body.lang === 'c')
cExec(req, res)
if(req.body.lang === 'py3')
py3Exec(req, res)
})
And after the code execution in py3Exec() and cExec(), I wrote this to send back the output, using Exit Codes:
if(code === 0)
{
if(stdout === req.body.output)
res.send({
"result": "AC",
"output": stdout
})
else
res.send({
"result": "WA",
"output": stdout
})
}
else
{
res.send({
"result": "ERR",
"error": stderr
})
}
https://github.com/vkartik97/Online-IDE-API/blob/master/routes/run.js#L41
So, this API is REST. I wanted to know if this is Perfect way for this Use Case or if RPC should have been used as REST is used only for Resources from Server whereas RPC is used for Function Invocation(which is my case, maybe).
Thanks!

In my opinion it's not really a big deal which way you go, a lot will depend on the rest of the application. If all your application does is consume code, run it, and provides an output then either would most likely be fine.
You obviously can see how this might be an RPC call, with something like /exec, but it could also easily be a REST call, especially if you wanted to add some form of logging. For example: POST /program could be designed to consume some code, create a "program" in a database, run the code, and log the output along side the program in the database. You'd then return the program and it's response to the client.
If you're literally just consume code, running it, and giving an output back across HTTP then I'd be tempted to go with a JSON-RPC sort of endpoint for simplicity and to reveal intent (simply triggering an action).
Much depends on the future of your application and how much bigger it might become, and what other functionality you will have. If you're going to have a 90% RESTful API and a couple endpoints as a JSON-RPC, then I'd just go with a REST API for everything for consistency.

Related

Good practice to handle useless response in express js/nodejs?

Am using express js on node for my api service ! In which am using sequelize for query handling purposes !
So in some usecase like creating record, or updating record its simply returning "1" or sometimes nothing !
In this case , am just using
res.sendStatus(200);
or sometimes
res.send("success");
Is there any better way or this is the correct way to handle ? Or should in need .end() in order to end the process ??
which is a good way to handle these kind of useless responses which we dont need to send back ?
This is where Status 204 comes in to play: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_success
It states: everything is OK (like in 200), but there is simple no need to send a body.
Using express, it's just as simple as: res.sendStatus(204)
Usually a json is send back to the client with different information in it that lets the client-side know that the operation that they request through the api is successfull or failed. for e.g. here, you can define a standard json response like this
//in case of success of the request
{
requestStatus: 1,
...other data if you want to like new Id of the created data/updated data or info about it if you want or depending upon use case
}
or
// in case of failure of the request
{
requestStatus: 0,
...other data if you want to like reason for failure, error message or depending upon your use case
}
Just add this line of code, should be fine:
//For all bad endpoints
app.use('*', (req, res) => {
res.status(404).json({ msg: 'Not a good endpoint, why are you here? Go play FIFA.'})
})
If you want you can generate an error HTML file, but since it's back-end, JSON format is strongly suggested. You can also add success: false for more clarity.

typescript fetch response streaming

i am trying to stream a response. But i want to be able to read the response (and work with the data) while it is still being sent. I basically want to send multiple messages in one response.
It works internally in node.js, but when i tried to do the same thing in typescript it doesnt work anymore.
My attempt was to do the request via fetch in typescript and the response is coming from a node.js server by writing parts of the response on the response stream.
fetch('...', {
...
}).then((response => {
const reader = response.body.getReader();
reader.read().then(({done, value}) => {
if (done) {
return response;
}
console.log(String.fromCharCode.apply(null, value)); //just for testing purposes
})
}).then(...)...
On the Node.js side it basically looks like this:
// doing stuff with the request
response.write(first_message)
// do some more stuff
response.write(second_message)
// do even more stuff
response.end(last_message)
In Node.js, like i said, i can just read every message once its sent via res.on('data', ...), but the reader.read in typescript only triggers(?) once and that is when the whole response was sent.
Is there a way to make it work like i want, or do i have to look for another way?
I hope it is kinda understandable what i want to do, i noticed while writing this how much i struggled explaining this :D
I found the problem, and as usual it was sitting in front of the pc.
I forgot to write a header first, before writing the response.

Bot framework async issues

I'm experimenting with the translation service on the Microsoft bot framework. I've written a method to which I pass a callback function which receives my translated text.
I've got an existing bot that calls an HTTP endpoint to create my output in English. I want to translate the output to the different language before returning it to the user. My unaltered code looks like this:
await request.post(ENDPOINT,
{
headers: HEADERS,
json: BODY
},
async function (error, response, body) {
if (response.statusCode == 202) {
var msg = body.mainResponse.text;
context.sendActivity(msg);
}
});
This runs just fine. Data passed in the HTTP response body gets parsed sent back to the user.
Now I want to plug in my translation service. I've got a single function that I call to do this called Translator.translate(text, callback). I've added this call to my existing function to get:
await request.post(ENDPOINT,
{
headers: HEADERS,
json: BODY
},
async function (error, response, body) {
if (response.statusCode == 202) {
var msg = body.mainResponse.text;
await Translator.translate(msg, function (output) {
context.sendActivity(output);
});
}
}
);
My translation process runs and I get the translation in the output variable, but nothing gets sent back to the user. Looking at the terminal, I see the error "Cannot perform 'get' on a proxy that has been revoked" relating to the context.sendActivity line in my callback.
Can anyone suggest how I keep the context object active?
Thanks in advance.
Many thanks for the assistance everyone - I never completely got to the bottom of this, but I finally fixed it with a complete re-write of the code. I think the problem was caused by a large number of nested synchronous and asynchronous calls. My ultimate solution was to completely get rid of all the nesting - first calling the translation service (and waiting for it), then doing the original call.
I think there are a number of other asynchronous threads inside the methods of both pieces of functionality. I don't have a great understanding of how this works in node, but I'm guessing that the response was getting popped off the stack at the wrong point, which is why I wasn't seeing it. The "cannot perform get" error was a bit of a red herring, it turns out. I get the same error from some of Microsoft's working demo code. I'm sure there's a separate issue there that ought to be fixed, but it wasn't actually caused by this issue. The code was running, but the output was getting lost.

What is considered standard when dealing with database errors in a REST application?

I'm currently writing a public REST service in Node.js that interfaces with a Postgres-database (using Sequelize) and a Redis cache instance.
I'm now looking into error handling and how to send informative and verbose error messages if something would happen with a request.
It struck me that I'm not quite sure how to handle internal server errors. What would be the appropriate way of dealing with this? Consider the following scenario:
I'm sending a post-request to an endpoint which in turn inserts the content to the database. However, something went wrong during this process (validation, connection issue, whatever). An error is thrown by the Sequelize-driver and I catch it.
I would argue that it is quite sensitive information (even if I remove the stack trace) and I'm not comfortable with exposing references of internal concepts (table-names, functions, etc.) to the client. I'd like to have a custom error for these scenarios that briefly describes the problem without giving away too detailed information.
Is the only way to approach this by mapping every "possible" error in the Sequelize-driver to a generic one and send that back to the client? Or how would you approach this?
Thanks in advance.
Errors are always caused by something. You should identify and intercept these causes before doing your database operation. Only cases that you think you've prepared for should reach the database operation.
If an unexpected error occurs, you should not send an informative error message for security reasons. Just send a generic error for unexpected cases.
Your code will look somewhat like this:
async databaseInsert(req, res) {
try {
if (typeof req.body.name !== 'string') {
res.status(400).send('Required field "name" was missing or malformed.')
return
}
if (problemCase2) {
res.status(400).send('Error message 2')
return
}
...
result = await ... // database operation
res.status(200).send(result)
} catch (e) {
res.status(500).send(debugging ? e : 'Unexpected error')
}
}

Returning multiple asynchronous responses

I'm currently looking to set up an endpoint that accepts a request, and returns the response data in increments as they load.
The application of this is that given one upload of data, I would like to calculate a number of different metrics for that data. As each metric gets calculated asynchronously, I want to return this metric's value to the front-end to render.
For testing, my controller looks as follows, trying to use res.write
uploadData = (req, res) => {
res.write("test");
setTimeout(() => {
res.write("test 2");
res.end();
}, 3000);
}
However, I think the issue stems from my client-side which I'm writing in React-Redux, and calling that route through an Axios call. From my understanding, it's because the axios request closes once receiving the first response, and the connection doesn't stay open. Here is what my axios call looks like:
axios.post('/api', data)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
Is there an easy way to do this? I've also thought about streaming, however my concern with streaming is that I would like each connection to be direct and unique between clients that are open for short amount of time (i.e. only open when the metrics are being calculated).
I should also mention that the resource being uploaded is a db, and I would like to avoid parsing and opening a connection multiple times as a result of multiple endpoints.
Thanks in advance, and please let me know if I can provide any more context
One way to handle this while still using a traditional API would be to store the metrics in an object somewhere, either a database or redis for example, then just long poll the resource.
For a real world example, say you want to calculate the following metrics of foo, time completed, length of request, bar, foobar.
You could create an object in storage that looks like this:
{
id: 1,
lengthOfRequest: 123,
.....
}
then you would create an endpoint in your API that like so metrics/{id}
and would return the object. Just keep calling the route until everything completes.
There are some obvious drawbacks to this of course, but once you get enough information to know how long the metrics will take to complete on average you can tweak the time in between the calls to your API.

Resources