Skip to content

Commit 6b91b03

Browse files
committed
Change PUT method implementation to fix validation
1 parent 4233d7d commit 6b91b03

File tree

6 files changed

+106
-103
lines changed

6 files changed

+106
-103
lines changed

src/content/3/en/part3c.md

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,24 @@ Once we introduce a database into the mix, it is useful to inspect the state per
618618

619619
You can find the code for our current application in its entirety in the <i>part3-4</i> branch of [this GitHub repository](https://github.com/fullstack-hy2020/part3-notes-backend/tree/part3-4).
620620

621+
### A true full stack developer's oath
622+
623+
It is again time for the exercises. The complexity of our app has now taken another step since besides frontend and backend we also have a database.
624+
There are indeed really many potential sources of error.
625+
626+
So we should once more extend our oath:
627+
628+
Full stack development is <i> extremely hard</i>, that is why I will use all the possible means to make it easier
629+
630+
- I will have my browser developer console open all the time
631+
- I will use the network tab of the browser dev tools to ensure that frontend and backend are communicating as I expect
632+
- I will constantly keep an eye on the state of the server to make sure that the data sent there by the frontend is saved there as I expect
633+
- <i>I will keep an eye on the database: does the backend save data there in the right format</i>
634+
- I progress with small steps
635+
- I will write lots of _console.log_ statements to make sure I understand how the code behaves and to help pinpoint problems
636+
- If my code does not work, I will not write more code. Instead, I start deleting the code until it works or just return to a state when everything was still working
637+
- When I ask for help in the course Discord channel or elsewhere I formulate my questions properly, see [here](https://fullstackopen.com/en/part0/general_info#how-to-get-help-in-discord) how to ask for help
638+
621639
</div>
622640

623641
<div class="tasks">
@@ -858,56 +876,56 @@ app.delete('/api/notes/:id', (request, response, next) => {
858876
In both of the "successful" cases of deleting a resource, the backend responds with the status code <i>204 no content</i>. The two different cases are deleting a note that exists, and deleting a note that does not exist in the database. The _result_ callback parameter could be used for checking if a resource was actually deleted, and we could use that information for returning different status codes for the two cases if we deem it necessary. Any exception that occurs is passed onto the error handler.
859877

860878

861-
The toggling of the importance of a note can be easily accomplished with the [findByIdAndUpdate](https://mongoosejs.com/docs/api/model.html#model_Model-findByIdAndUpdate) method.
879+
Let's implement the functionality to update a single note, allowing the importance of the note to be changed. The note updating is done as follows:
862880

863881
```js
864882
app.put('/api/notes/:id', (request, response, next) => {
865-
const body = request.body
883+
const { content, important } = request.body
866884

867-
const note = {
868-
content: body.content,
869-
important: body.important,
870-
}
885+
Note.findById(request.params.id)
886+
.then(note => {
887+
if (!note) {
888+
return response.status(404).end()
889+
}
871890

872-
Note.findByIdAndUpdate(request.params.id, note, { new: true })
873-
.then(updatedNote => {
874-
if (updatedNote) {
891+
note.content = content
892+
note.important = important
893+
894+
return note.save().then((updatedNote) => {
875895
response.json(updatedNote)
876-
} else {
877-
response.status(404).end()
878-
}
896+
})
879897
})
880898
.catch(error => next(error))
881899
})
882900
```
883901

884-
In the code above, we also allow the content of the note to be edited.
902+
The note to be updated is first fetched from the database using the _findById_ method. If no object is found in the database with the given id, the value of the variable _note_ is _null_, and the query responds with the status code <i>404 Not Found</i>.
885903

886-
Notice that the <em>findByIdAndUpdate</em> method receives a regular JavaScript object as its argument, and not a new note object created with the <em>Note</em> constructor function.
904+
If an object with the given id is found, its _content_ and _important_ fields are updated with the data provided in the request, and the modified note is saved to the database using the _save()_ method. The HTTP request responds by sending the updated note in the response.
887905

888-
There is one important detail regarding the use of the <em>findByIdAndUpdate</em> method. By default, the <em>updatedNote</em> parameter of the event handler receives the original document [without the modifications](https://mongoosejs.com/docs/api/model.html#model_Model-findByIdAndUpdate). We added the optional <code>{ new: true }</code> parameter, which will cause our event handler to be called with the new modified document instead of the original.
906+
One notable point is that the code now has nested promises, meaning that within the outer _.then_ method, another [promise chain](https://javascript.info/promise-chaining) is defined:
889907

890-
After testing the backend directly with Postman or the VS Code REST client, we can verify that it seems to work. The frontend also appears to work with the backend using the database.
908+
```js
909+
.then(note => {
910+
if (!note) {
911+
return response.status(404).end()
912+
}
891913

892-
You can find the code for our current application in its entirety in the <i>part3-5</i> branch of [this GitHub repository](https://github.com/fullstack-hy2020/part3-notes-backend/tree/part3-5).
914+
note.content = content
915+
note.important = important
893916

894-
### A true full stack developer's oath
895-
896-
It is again time for the exercises. The complexity of our app has now taken another step since besides frontend and backend we also have a database.
897-
There are indeed really many potential sources of error.
917+
// highlight-start
918+
return note.save().then((updatedNote) => {
919+
response.json(updatedNote)
920+
})
921+
// highlight-end
922+
```
898923
899-
So we should once more extend our oath:
924+
Usually, this is not recommended because it can make the code difficult to read. In this case, however, the solution works because it ensures that the _.then_ block following the _save()_ method is only executed if a note with the given id is found in the database and the _save()_ method is called. In the fourth part of the course, we will explore the async/await syntax, which offers an easier and clearer way to handle such situations.
900925
901-
Full stack development is <i> extremely hard</i>, that is why I will use all the possible means to make it easier
926+
After testing the backend directly with Postman or the VS Code REST client, we can verify that it seems to work. The frontend also appears to work with the backend using the database.
902927
903-
- I will have my browser developer console open all the time
904-
- I will use the network tab of the browser dev tools to ensure that frontend and backend are communicating as I expect
905-
- I will constantly keep an eye on the state of the server to make sure that the data sent there by the frontend is saved there as I expect
906-
- <i>I will keep an eye on the database: does the backend save data there in the right format</i>
907-
- I progress with small steps
908-
- I will write lots of _console.log_ statements to make sure I understand how the code behaves and to help pinpoint problems
909-
- If my code does not work, I will not write more code. Instead, I start deleting the code until it works or just return to a state when everything was still working
910-
- When I ask for help in the course Discord channel or elsewhere I formulate my questions properly, see [here](https://fullstackopen.com/en/part0/general_info#how-to-get-help-in-discord) how to ask for help
928+
You can find the code for our current application in its entirety in the <i>part3-5</i> branch of [this GitHub repository](https://github.com/fullstack-hy2020/part3-notes-backend/tree/part3-5).
911929
912930
</div>
913931

src/content/3/en/part3d.md

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -84,27 +84,6 @@ When validating an object fails, we return the following default error message f
8484

8585
![postman showing error message](../../images/3/50.png)
8686

87-
We notice that the backend has now a problem: validations are not done when editing a note.
88-
The [documentation](https://mongoosejs.com/docs/validation.html#update-validators) addresses the issue by explaining that validations are not run by default when <i>findOneAndUpdate</i> and related methods are executed.
89-
90-
The fix is easy. Let us also reformulate the route code a bit:
91-
92-
```js
93-
app.put('/api/notes/:id', (request, response, next) => {
94-
const { content, important } = request.body // highlight-line
95-
96-
Note.findByIdAndUpdate(
97-
request.params.id,
98-
{ content, important }, // highlight-line
99-
{ new: true, runValidators: true, context: 'query' } // highlight-line
100-
)
101-
.then(updatedNote => {
102-
response.json(updatedNote)
103-
})
104-
.catch(error => next(error))
105-
})
106-
```
107-
10887
### Deploying the database backend to production
10988

11089
The application should work almost as-is in Fly.io/Render. We do not have to generate a new production build of the frontend since changes thus far were only on our backend.

src/content/3/fi/osa3c.md

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -865,34 +865,52 @@ app.delete('/api/notes/:id', (request, response, next) => {
865865

866866
Vastauksena on molemmissa "onnistuneissa" tapauksissa statuskoodi <i>204 No Content</i> eli jos olio poistettiin tai olioa ei ollut mutta <i>id</i> oli periaatteessa oikea. Takaisinkutsun parametrin _result_ perusteella olisi mahdollisuus haarautua ja palauttaa tilanteissa eri statuskoodi, jos sille on tarvetta. Mahdollinen poikkeus siirretään jälleen virheenkäsittelijälle.
867867

868-
Muistiinpanon tärkeyden muuttamisen mahdollistava olemassa olevan muistiinpanon päivitys onnistuu helposti metodilla [findByIdAndUpdate](https://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate):
868+
Toteutetaan vielä yksittäisen muistiinpanon muokkaustoiminto, jotta muistiinpanon tärkeyden muuttaminen mahdollistuu. Muistiinpanon muokkaus tapahtuu seuraavasti:
869869

870870
```js
871871
app.put('/api/notes/:id', (request, response, next) => {
872-
const body = request.body
872+
const { content, important } = request.body
873873

874-
const note = {
875-
content: body.content,
876-
important: body.important,
877-
}
874+
Note.findById(request.params.id)
875+
.then(note => {
876+
if (!note) {
877+
return response.status(404).end()
878+
}
878879

879-
Note.findByIdAndUpdate(request.params.id, note, { new: true })
880-
.then(updatedNote => {
881-
if (updatedNote) {
880+
note.content = content
881+
note.important = important
882+
883+
return note.save().then((updatedNote) => {
882884
response.json(updatedNote)
883-
} else {
884-
response.status(404).end()
885-
}
885+
})
886886
})
887887
.catch(error => next(error))
888888
})
889889
```
890890

891-
Operaatio mahdollistaa myös muistiinpanon sisällön editoinnin.
891+
Muokattava muistiinpano haetaan ensin tietokannasta metodilla _findById_. Jos kannasta ei löydy oliota annetulla id:llä, muuttujan _note_ arvo on _null_, ja kyselyyn vastataan statuskoodilla <i>404 Not Found</i>.
892+
893+
Jos annettua id:tä vastaava olio löytyy, päivitetään sen _content_- ja _important_-kentät pyynnön mukana tulleella datalla ja tallennetaan muokattu muistiinpano tietokantaan metodilla _save()_. HTTP-pyyntöön vastataan lähettämällä vastauksen mukana päivitetty muistiinpano.
892894

893-
Huomaa, että metodin <em>findByIdAndUpdate</em> parametrina tulee antaa normaali JavaScript-olio eikä uuden olion luomisessa käytettävä <em>Note</em>-konstruktorifunktiolla luotu olio.
895+
Eräs huomionarvoinen seikka on se, että koodissa on nyt ns. sisäkkäiset promiset, eli ulomman _.then_-metodin sisällä on määritelty toinen [promise-ketju](https://javascript.info/promise-chaining):
896+
897+
```js
898+
.then(note => {
899+
if (!note) {
900+
return response.status(404).end()
901+
}
902+
903+
note.content = content
904+
note.important = important
905+
906+
// highlight-start
907+
return note.save().then((updatedNote) => {
908+
response.json(updatedNote)
909+
})
910+
// highlight-end
911+
```
894912
895-
Huomioi operaatioon <em>findByIdAndUpdate</em> liittyen, että oletusarvoisesti tapahtumankäsittelijä saa parametrikseen <em>updatedNote</em> päivitetyn olion [ennen muutosta](https://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate) olleen tilan. Lisäsimme operaatioon parametrin <code>{ new: true }</code>, jotta saamme muuttuneen olion palautetuksi kutsujalle.
913+
Yleensä tällaista ei suositella, koska se voi tehdä koodista vaikealukuista. Tässä tapauksessa ratkaisu kuitenkin toimii, sillä näin voimme varmistua siitä, että _.save()_-metodin jälkeiseen _.then_-lohkoon mennään vain, jos id:tä vastaava muistiinpano on löytynyt kannasta ja _save()_-metodia on kutsuttu. Tutustumme kurssin neljännessä osassa async/await-syntaksiin, joka tarjoaa helpomman ja selkeämmän kirjoitustavan tämänkaltaisiin tilanteisiin.
896914
897915
Backend vaikuttaa toimivan Postmanista ja VS Coden REST Clientistä tehtyjen kokeilujen perusteella. Myös frontend toimii moitteettomasti tietokantaa käyttävän backendin kanssa.
898916

src/content/3/fi/osa3d.md

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,26 +84,6 @@ Validoinnin epäonnistuessa palautetaan validaattorin oletusarvoinen virheviesti
8484

8585
![Luotaessa muistiinpano jonka kenttä content on liian lyhyt, seurauksena on virheilmoituksen sisältävä JSON](../../images/3/50.png)
8686

87-
Huomaamme kuitenkin että sovelluksessa on pieni ongelma, validaatiota ei suoriteta muistiinpanojen päivityksen yhteydessä. [Dokumentaatio](https://mongoosejs.com/docs/validation.html#update-validators) kertoo mistä on kyse, validaatiota ei suoriteta oletusarvoisesti metodin <i>findOneAndUpdate</i> suorituksen yhteydessä.
88-
89-
Korjaus on onneksi helppo. Muotoillaan routea muutenkin hieman siistimmäksi:
90-
91-
```js
92-
app.put('/api/notes/:id', (request, response, next) => {
93-
const { content, important } = request.body // highlight-line
94-
95-
Note.findByIdAndUpdate(
96-
request.params.id,
97-
{ content, important }, // highlight-line
98-
{ new: true, runValidators: true, context: 'query' } // highlight-line
99-
)
100-
.then(updatedNote => {
101-
response.json(updatedNote)
102-
})
103-
.catch(error => next(error))
104-
})
105-
```
106-
10787
### Tietokantaa käyttävän version vieminen tuotantoon
10888

10989
Sovelluksen pitäisi toimia tuotannossa eli Fly.io:ssa tai Renderissä lähes sellaisenaan. Frontendin muutosten takia on tehtävä siitä uusi tuotantoversio ja kopioitava se backendiin.

src/content/4/en/part4a.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,20 @@ notesRouter.delete('/:id', (request, response, next) => {
144144
})
145145

146146
notesRouter.put('/:id', (request, response, next) => {
147-
const body = request.body
147+
const { content, important } = request.body
148148

149-
const note = {
150-
content: body.content,
151-
important: body.important,
152-
}
149+
Note.findById(request.params.id)
150+
.then(note => {
151+
if (!note) {
152+
return response.status(404).end()
153+
}
154+
155+
note.content = content
156+
note.important = important
153157

154-
Note.findByIdAndUpdate(request.params.id, note, { new: true })
155-
.then(updatedNote => {
156-
response.json(updatedNote)
158+
return note.save().then((updatedNote) => {
159+
response.json(updatedNote)
160+
})
157161
})
158162
.catch(error => next(error))
159163
})

src/content/4/fi/osa4a.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,16 +140,20 @@ notesRouter.delete('/:id', (request, response, next) => {
140140
})
141141

142142
notesRouter.put('/:id', (request, response, next) => {
143-
const body = request.body
143+
const { content, important } = request.body
144144

145-
const note = {
146-
content: body.content,
147-
important: body.important,
148-
}
145+
Note.findById(request.params.id)
146+
.then(note => {
147+
if (!note) {
148+
return response.status(404).end()
149+
}
150+
151+
note.content = content
152+
note.important = important
149153

150-
Note.findByIdAndUpdate(request.params.id, note, { new: true })
151-
.then(updatedNote => {
152-
response.json(updatedNote)
154+
return note.save().then((updatedNote) => {
155+
response.json(updatedNote)
156+
})
153157
})
154158
.catch(error => next(error))
155159
})

0 commit comments

Comments
 (0)