You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/4/en/part4.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,8 +8,9 @@ lang: en
8
8
9
9
In this part, we will continue our work on the backend. Our first major theme will be writing unit and integration tests for the backend. After we have covered testing, we will take a look at implementing user authentication and authorization.
10
10
11
-
<i>Part updated 28th May 2025</i>
11
+
<i>Part updated 13th August 2025</i>
12
12
- <i>Node updated to version v22.3.0</i>
13
+
- <i>Express updated to version 5 and the express-async-errors library removed from part 4b</i>
You can find the code for our current application in its entirety in the <i>part4-3</i> branch of [this GitHub repository](https://github.com/fullstack-hy2020/part3-notes-backend/tree/part4-3).
334
+
333
335
### Running tests one by one
334
336
335
337
The _npm test_ command executes all of the tests for the application. When we are writing tests, it is usually wise to only execute one or two tests.
@@ -459,9 +461,19 @@ The code declares that the function assigned to _main_ is asynchronous. After th
459
461
460
462
### async/await in the backend
461
463
462
-
Let's start to change the backend to async and await. As all of the asynchronous operations are currently done inside of a function, it is enough to change the route handler functions into async functions.
464
+
Let's start to change the backend to async and await. Let's start with the route responsible for fetching all notes.
465
+
466
+
As all of the asynchronous operations are currently done inside of a function, it is enough to change the route handler functions into async functions. The route for fetching all notes
467
+
468
+
```js
469
+
notesRouter.get('/', (request, response) => {
470
+
Note.find({}).then((notes) => {
471
+
response.json(notes)
472
+
})
473
+
})
474
+
```
463
475
464
-
The route for fetching all notes gets changed to the following:
We can verify that our refactoring was successful by testing the endpoint through the browser and by running the tests that we wrote earlier.
474
486
475
-
You can find the code for our current application in its entirety in the <i>part4-3</i> branch of [this GitHub repository](https://github.com/fullstack-hy2020/part3-notes-backend/tree/part4-3).
476
-
477
-
### More tests and refactoring the backend
487
+
### Refactoring the route responsible for adding a note
478
488
479
489
When code gets refactored, there is always the risk of [regression](https://en.wikipedia.org/wiki/Regression_testing), meaning that existing functionality may break. Let's refactor the remaining operations by first writing a test for each route of the API.
480
490
@@ -667,58 +677,68 @@ after(async () => {
667
677
668
678
The code using promises works and the tests pass. We are ready to refactor our code to use the async/await syntax.
669
679
670
-
We make the following changes to the code that takes care of adding a new note (notice that the route handler definition is preceded by the _async_ keyword):
The catch block simply calls the _next_ function, which passes the request handling to the error handling middleware.
718
+
You need to add the _async_ keyword at the beginning of the handler to enable the use of _async/await_ syntax. The code becomes much simpler.
719
+
720
+
Notably, possible errors no longer need to be forwarded separately for handling. In code using promises, a possible error was passed to the error-handling middleware like this:
721
+
722
+
```js
723
+
note
724
+
.save()
725
+
.then((savedNote) => {
726
+
response.json(savedNote)
727
+
})
728
+
.catch((error) =>next(error)) // highlight-line
729
+
```
730
+
731
+
When using _async/await_ syntax, Express will [automatically call](https://expressjs.com/en/guide/error-handling.html) the error-handling middleware if an await statement throws an error or the awaited promise is rejected. This makes the final code even cleaner.
718
732
719
-
After making the change, all of our tests will pass once again.
733
+
**Note:** This feature is available starting from Express version 5. If you installed Express as a dependency before March 31, 2025, you might still be using version 4. You can check your project's Express version in the _package.json_ file. If you have an older version, update to version 5 with the following command:
720
734
721
-
Next, let's write tests for fetching and removing an individual note:
735
+
```bash
736
+
npm install express@5
737
+
```
738
+
739
+
### Refactoring the route responsible for fetching a single note
740
+
741
+
Next, let's write a test for viewing the details of a single note. The code highlights the actual API operation being performed:
722
742
723
743
```js
724
744
test('a specific note can be viewed', async () => {
@@ -734,119 +754,56 @@ test('a specific note can be viewed', async () => {
Both tests share a similar structure. In the initialization phase, they fetch a note from the database. After this, the tests call the actual operation being tested, which is highlighted in the code block. Lastly, the tests verify that the outcome of the operation is as expected.
759
+
First, the test fetches a single note from the database. Then, it checks that the specific note can be retrieved through the API. Finally, it verifies that the content of the fetched note is as expected.
758
760
759
-
There is one point worth noting in the first test. Instead of the previously used method [strictEqual](https://nodejs.org/api/assert.html#assertstrictequalactual-expected-message), the method [deepStrictEqual](https://nodejs.org/api/assert.html#assertdeepstrictequalactual-expected-message) is used:
761
+
There is one point worth noting in the test. Instead of the previously used method [strictEqual](https://nodejs.org/api/assert.html#assertstrictequalactual-expected-message), the method [deepStrictEqual](https://nodejs.org/api/assert.html#assertdeepstrictequalactual-expected-message) is used:
The reason for this is that _strictEqual_ uses the method [Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) to compare similarity, i.e. it compares whether the objects are the same. In our case, we want to check that the contents of the objects, i.e. the values of their fields, are the same. For this purpose _deepStrictEqual_ is suitable.
766
768
767
-
The tests pass and we can safely refactor the tested routes to use async/await:
You can find the code for our current application in its entirety in the <i>part4-4</i> branch of [this GitHub repository](https://github.com/fullstack-hy2020/part3-notes-backend/tree/part4-4).
796
-
797
-
### Eliminating the try-catch
782
+
### Refactoring the route responsible for deleting a note
798
783
799
-
Async/await unclutters the code a bit, but the 'price' is the <i>try/catch</i> structure required for catching exceptions.
800
-
All of the route handlers follow the same structure
784
+
Let's also add a test for the route that handles deleting a note:
801
785
802
786
```js
803
-
try {
804
-
// do the async operations here
805
-
} catch (exception) {
806
-
next(exception)
807
-
}
808
-
```
809
-
810
-
One starts to wonder if it would be possible to refactor the code to eliminate the <i>catch</i> from the methods?
811
-
812
-
The [express-async-errors](https://github.com/davidbanham/express-async-errors) library has a solution for this.
813
-
814
-
Let's install the library
815
-
816
-
```bash
817
-
npm install express-async-errors
818
-
```
819
-
820
-
Using the library is <i>very</i> easy.
821
-
You introduce the library in <i>app.js</i>, _before_ you import your routes:
787
+
test('a note can be deleted', async () => {
788
+
constnotesAtStart=awaithelper.notesInDb()
789
+
constnoteToDelete= notesAtStart[0]
822
790
823
-
```js
824
-
require('express-async-errors') // highlight-line
825
-
constexpress=require('express')
826
-
constmongoose=require('mongoose')
827
-
constconfig=require('./utils/config')
828
-
constlogger=require('./utils/logger')
829
-
constmiddleware=require('./utils/middleware')
830
-
constnotesRouter=require('./controllers/notes')
791
+
await api
792
+
.delete(`/api/notes/${noteToDelete.id}`)
793
+
.expect(204)
831
794
832
-
// ...
833
-
```
795
+
constnotesAtEnd=awaithelper.notesInDb()
834
796
835
-
The 'magic' of the library allows us to eliminate the try-catch blocks completely.
The test is structured similarly to the one that checks viewing a single note. First, a single note is fetched from the database, then its deletion via the API is tested. Finally, it is verified that the note no longer exists in the database and that the total number of notes has decreased by one.
805
+
806
+
The tests still pass, so we can safely proceed with refactoring the route:
Because of the library, we do not need the _next(exception)_ call anymore.
859
-
The library handles everything under the hood. If an exception occurs in an <i>async</i> route, the execution is automatically passed to the error-handling middleware.
You can find the code for our current application in its entirety in the <i>part4-4</i> branch of [this GitHub repository](https://github.com/fullstack-hy2020/part3-notes-backend/tree/part4-4).
Copy file name to clipboardExpand all lines: src/content/4/fi/osa4.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,8 +8,9 @@ lang: fi
8
8
9
9
Jatkamme tämän osan backendin parissa. Osan ensimmäinen iso teema on backendin yksikkö- ja integraatiotestaus. Testauksen jälkeen toteutetaan backendin logiikka käyttäjienhallintaan ja kirjautumiseen.
10
10
11
-
<i>Osa päivitetty 28.5.2025</i>
11
+
<i>Osa päivitetty 13.8.2025</i>
12
12
- <i>Node päivitetty versioon v22.3.0</i>
13
+
- <i>Express päivitetty versioon 5 ja kirjasto express-async-errors poistettu osasta 4b</i>
0 commit comments