Reputation: 2766
I'm building a website in the MERN stack for educational purposes. It has a questionnaire with 40 "yes or no" questions (statements that can be either true or false) for one specific subject. There can be many subjects, but the statements are always the same for all subjects.
After the user is done answering, the answers should be POSTed to (or GET from?) a rest API.
The backend just gets the correct answers from the DB and then verifies the users answers against those and responds with a result.
The model in the backend is simply documents each consisting of subject id
and 40 boolean as1
to as40
.
(New to nosql, so I may have butched my model completely, in that case I would happily stand corrected!)
What would be the proper way to call this rest service?
I guess I'm going with a POST body like so:
{
"answers": {
"uas1": true,
"uas2": false,
...
"uas39": true,
"uas40": false
}
}
... but what should the path look like?
/statements/:id/answer
?
Upvotes: -1
Views: 639
Reputation: 57214
What would be the proper way to call this rest service?
The primary question you need to address is understanding whether the client is fetching a resource (an effectively read only operation) or modifying a resource. Fetching a resource might mean getting the resource out of a cache, rather than letting the request go all the way through to your server.
The backend just gets the correct answers from the DB and then verifies the users answers against those and responds with a result.
This sounds to me a lot like the effectively read only case: given a list of answers, find the matching answer document. So the usual answer for this kind of resource model would be to use a GET.
On the web, this might look like a big form with input controls for the 40 different questions. The user would make their selections, and then hit the submit button. The browser would use the inputs to compute a query string, and perform an HTTP GET request, with all of the user's inputs encoded into the query part (according to the metadata of the form itself.
?a1=Y&a2=N&a3=T...&a40=Y
Using HTML forms, we can use whatever spelling for the path we want, and likewise whatever spelling we want for the keys in the query part, because that information becomes part of the metadata of the form: the browser can just look at the form definition, and that describes how to create the effective request URI (it's part of the standard for HTML form processing).
For something like the subject id, you have a choice between encoding that information into the path (by making that information part of the form action) or into the query part (as a form input - possibly "hidden").
what should the path look like?
Anything you like - REST does not care what spelling conventions you use for your resource identifiers.
GET /83eeecdd-a680-475f-913b-07aa0239cec0?a1=Y&a2=N&a3=T...&a40=Y
... is fine. You probably want something nicer for human beings though - the operators reading the log, the user scanning their browser history, the writers trying to document the resource model for other developers, etc.
GET /subject/:subject_id/answers?a1=Y&a2=N&a3=T...&a40=Y
Also fine.
I guess I'm going with a POST body like so:
Not my first choice, based on your description.
A request like
POST /statements/:id/answer
Content-Type: application/json
{...}
has, from the point of view of a general purpose component, very few constraints; a general purpose component cannot assume that a POST is effectively read only. That means we can't pull the answer out of a cache. In fact, the opposite is true: a successful POST request will cause general purpose caches to invalidate previously stored responses using the same effective uri.
A general purpose component can't even assume that POST requests have idempotent semantics, so the HTTP application can't automatically help us by re-sending a request if the response is lost.
In a case where effective read only semantics aren't what you want, then it is okay to use POST. With HTML forms, that would be our only real option.
But it would be better if you can design your resource model such that each set of submitted answers is its own resource. You might imagine it this way: the user GETs a unique questionnaire in which all of the answers are blank. They fill in their own answers, and return a new representation of that unique questionnaire.
Realizing your API as edits to documents allows you to use PUT; PUT has idempotent semantics, which means that general purpose components can automatically assist when responses get lost on the unreliable network.
PUT /statements/123/answers/Bob
Content-Type: application/json
{
"answers": {
"uas1": true,
"uas2": false,
...
"uas39": true,
"uas40": false
}
}
Here, we have a resource that is specific to Bob's answers, and the request describes an edit to that resource.
Technically, you could have everybody edit the same resource
PUT /statements/123/answers
...
It's not great, but you get a lot of the benefits. Every successfully submitted request would invalidate the target resource, but you might not care very much.
I wouldn't call this approach REST-compliant, but it probably counts as REST-close-enough-that-you-get-away-with-it.
Upvotes: 1
Reputation: 52
You should design your API endpoints (or 'paths') in a way that would make them both really descriptive and specific to each scenario. so with that in mind one of the endpoints might look something like:
/subject/:subject_id/answers
then on your frontend, you would send the POST request to e.g.
http://127.0.0.1/3000/subject/23/answers
with a request body that would look like this:
{
"answers": {
"uas1": true,
"uas2": false,
...
"uas39": true,
"uas40": false
}
}
What this endpoint would essentially do is that it would send all the 40 boolean answers for a subject with id 23 to your Express.js server.
Then your server would parse the request and get the answers from your req.body and then check them against your MongoDB database. The result from that check can then be sent back in a response like this:
res.status(200).send(`For subject ${subject_name}, you scored ${correct_answers}/40`)
Upvotes: 0