Jednym z etapów pisania aplikacji powinno być poprawne obsługiwanie błędów - nie możemy zakładać, że użytkownik nie będzie wysyłał dziwnych requestów i nie wywoła błędów. Pisząc aplikację w Koa.js mamy kilka możliwości wyłapywania błędów, zapobieganiu zawieszeniu się aplikacji oraz informowania użytkownika, że coś poszło nie tak jak powinno.
Domyślna obsługa jest najprostszym rozwiązaniem i tak naprawdę obowiązkowym - nie wyobrażam sobie by aplikacja tego nie miała. Aby ją zaimplementować wystarczy wykorzystać mechanizmy dostarczone razem z biblioteką Koa.js. Aby zwrócić błąd możemy skorzystać z następującej funkcji ctx.throw
, która przyjmuje 3 opcjonalne parametry:
Validation Error
Jak to wygląda w praktyce?
ctx.throw(); // Status Code: 500 Internal Server Error
ctx.throw(400); //Status Code: 400 Bad Request
ctx.throw(400, 'Validation Error') // 400 z Response: Validation Error
ctx.throw(400, JSON.stringify({
message: 'name required',
errors: {
firstName: 'Required'
}
}, {
headers: {
'custom-header': 'custom-header-value'
}
}));
To o czym warto jeszcze pamiętać to że treść w błędach musi być tekstem. Co zrobić w takim razie jak chcemy wysłać więcej informacji np.: opis błędów dla poszczególnych pól? Musimy skomponować to wszystko w pojedynczy obiekt i skorzystać z funkcji JSON.stringify()
- zamieni nam to obiekt w tekst i wiadomość będzie poprawnie wysłana.
Oprócz ctx.throw
mamy też ctx.assert
. Jest ona bardzo podobna do poprzednio opisanej ale posiada doadtkowy parametr na początku. Parametr ten steruje czy błąd zostanie rzucony - jeśli wartość parametru zrzutowana na wartość boolean
daje false
to zostanie rzucony błąd (czyli na przykład użytkownik nie podał jakiegoś parametru, nie ma uprawnień itd.).
ctx.assert(true, 400, 'Validation Error') // błąd nie jest rzucony
ctx.assert(false, 400, 'Validation Error') // błąd jest rzucony
Jak już wspomniałem taka obsługa błędów jest podstawowym minimum, co nie znaczy że nie możemy tego rozbudować. Podczas przetwarzania danych dobrze jest rzucać wyjątki typowo domenowe - czyli związane z logiką biznesową ale niekoniecznie z aktualnie wykorzystywaną biblioteką. W przypadku Node’a najprościej jest to zrobić przy pomocy rozszerzenia klasy Error.
class ValidationError extends Error {}
Teraz możemy w dużo bardziej czytelny sposób rzucać błędy
throw new ValidationError('foo');
Oczywiście to jest najprostszy przypadek ale budując w ten sposób wyjątki jesteśmy w stanie tworzyć bardziej skomplikowane błędy, które będą bardziej opisowe a więc i będzie prościej śledzić błędy. Teraz pozostało tylko obsłużyć te błędy z poziomu biblioteki Koa.js. Aby to zrobić należy jako pierwszy middleware dodać asynchroniczną funkcję, która będzie łapać błędy. Wystarczy że opakujemy funkcję next() w blok try-catch
app.use(async (ctx, next)=>{
try {
await next();
} catch (error) {
if(error instanceof ValidationError){
ctx.throw(400, 'error')
}
}
})
Teraz w bloku catch
możemy zrobić wszystko co chcemy - mając konkretne błędy jesteśmy reagować unikalnie dla każdego.
Na sam koniec coś co, może nie będzie wykorzystywane często ale warto wiedzieć o takiej funkcjonalności. Dzięki systemowi zdarzeń w Koa.js jest możliwość nasłuchiwania na każdy błąd w aplikacji. Dzięki takiemu mechanizmowi możemy na przykład wpiąć globalne logowanie błędów. Aby coś takiego uzyskać wystarczy, że wpiszemy taki kod
app.on('error', (error)=>{
})
Teraz możemy robić co chcemy.
To by było na wszystko jeśli o chodzi o obsługę błędów. Mechanizm jest prosty ale dzięki temu jesteśmy w stanie go szybko dostosować do naszych potrzeb. Najważniejsze by błędy w aplikacji były obsługiwane oraz zwracane w zrozumiały sposób tak aby dało się je obsłużyć na frontendzie lub naprawić te które się nie powinny pojawić.