How to manage errors on vue - vue.js

i'm not sure on how to handle errors and global errors in vue. It's not specifically about the code, it is about the logic.
What i mean is, at the moment i placed a global error handler in the vue, something like:
app.config.errorHandler = function (err, vm, info) {
notyf.error(
"Something went wrong. Please try again."
);
}
with this, every error that propagates up to the root will be shown to the user with a small popup.
This works great for unexpected errors.
What confuses me is how should i manage "expected fails", let me give you an example:
Let's take the standard "refreshToken" method that takes care of the jwt token refresh when it is expired.
So, to manage this i'll make an interceptor in the axios client, so that every time i get a 401, i'll try to refresh:
if everything works fine
i'll proceed with the request
otherwise
i'll redirect to the login.
So the simplified code should be something like:
api.interceptors.response.use(
async res => {
return res;
}
,
async err => {
const originalRequest = err.config;
if (err.response.status === 401) {
if (session.accessToken && !isRefreshing) {
isRefreshing = true;
const tokenRefreshed = await tryRefreshToken(api, session);
if (tokenRefreshed === true) {
isRefreshing = false;
return api(originalRequest);
}
else {
router.push({ name: 'auth-login' }); <---------------------------
return Promise.reject("Unable to refresh token. Need login."); <-----------
}
}
}
else {
return Promise.reject(err);
}
},
)
Now, looking at the arrows, once i see that i'm unable to refresh i redirect the user, and from a logical point of view, this means that i've "managed" the problem. But since i'm in a async method i've to return a rejected promise, that clearly propagates upward until it hit the global error handler and gets finally managed.
This looks strange to me because global handler should be hit only by "unexpected" exception, not from things that i've already managed.
Looking at the logical hirerarchy here we can see:
level-1 app ( where the global handler resides )
level-2 page component ( user.vue )
level-3 page method ( saveUser(user) )
level-4 module method ( userService.updateUser(user) )
level-5 axios method ( axios.patch(....) )
level-6 axios.interceptor
level-7 tryRefreshToken method
What's the correct way to "manage" this situation?
From my point of view redirecting the user to the login page because it wansn't able to refresh the token means that i've managed the problem, but i totally understand that i've a chain of methods waiting a promise response ( that must be fullfilled )
The solutions i came up with are:
SOLUTION 1)
At level 7 reject passing a particular standard error "unable to refresh token"
At level 3 wrap the await saveUser(user) in a try catch and in case of "unable to refresh error" manage the redirect there.
This should totally work but what does it mean? That every single time i've an axios call i've to manage a potential "unable to refresh error", this seems tremendously wrong.
SOLUTION 2)
At level 7 reject passing a particular standard error "unable to refresh token"
At level 1, in the global handler, perform a switch case and in case of "unable to refresh error" manage the redirect there.
This should totally work too, but why should i manage a problem so far away from where it manifested? ( It manifested in the level-7 and i'm actually managing it at level 1 ).
From my point of view the global handler should manage only unamanaged errors and errors should be taken care as soon as possible ( so at level 7 in this example ).
SOLUTION 3) ( i don't know how to implement it )
At level 7 i manage the problem ( like i'm doing ) and then i find a way to "abort" the whole upward chain so that, on the redirect everything waiting is aborted.
This looks "kind of logically correct" but i'm not sure it really is and i don't know how to do that.
Do you have any suggestion or consideration on the matter?
Someone can help me understand better how to proceed?
Thank you very much in advance
Have a nice day

Related

StimulusJS - how to ensure controller has all the necessary targets?

I'm trying to find a way to make my Stimulus controller more robust and maintainable by checking that all the required targets are present. If something is missing, I would like it to fail fast and loud.
Below is what I'm using so far:
export default class extends Controller {
static targets = ['name'];
connect() {
if (!that.hasNameTarget) {
throw new Error('expected to find name target');
}
}
}
Perhaps someone knows of a more idiomatic/clean solution?
Option 1 - use the Stimulus debugger tooling
Stimulus has a debug mode that logs out info/warnings etc for Stimulus controllers. You can enable this by stimulus.debug = true;
You can call this in your own controllers via this.application.logDebugActivity() - see https://github.com/hotwired/stimulus/blob/main/src/core/application.ts#L95
export default class extends Controller {
static targets = ['name'];
connect() {
if (!that.hasNameTarget) {
this.logDebugActivity(this.identifier, 'error', { message: 'target missing'});
throw new Error('expected to find name target');
}
}
}
Option 2 - Use the window.onerror callback
If you keep your current code where an error is thrown, Stimulus will not 'break' anything where possible as all calls within Stimulus use try/catch.
However, you can ensure that your error does something 'loud' by creating a onerror function.
See docs - https://stimulus.hotwired.dev/handbook/installing#error-handling
See an example where this can be used for something like Sentry https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror
You could also just be really loud and block the UI with something similar to this.
window.onerror = (message, url, lineNo, columnNo, error) => {
document.body.style.backgroundColor = 'red';
window.alert(message);
}
Reminders
Remember to only enable these debugging features in local development, you can do this with something like Webpack environment variables but this will be different depending on your tooling.
In production though you may want to push your onerror calls to whatever logging infrastructure you have.
stimulus.debug mode is quite 'noisy' and may be too much information, depending on your set up.

How to show firebase auth error messages different in UI

I am using the firebase auth now I want to show a different message in UI for every error message
You have to check for specific error messages in your catch block and add custom handling.
You don't mention the language you're working in (and I'm not familiar with all of the different libraries), but C# will throw a FirebaseAuthException containing the property AuthErrorCode which is an enum representing the error. You could check that in, say, a switch statement to get the required message.
try {
userRecord = await _FirebaseAuth.GetUserByEmailAsync(email, token)
.ConfigureAwait(false);
}
catch (FirebaseAuthException ex) {
if (ex.AuthErrorCode == AuthErrorCode.UserNotFound) {
DisplayError($"Error retrieving user record for {email}");
}
}

Unable to login with a specific user in FaunaDB

It always worked before, but it is no longer working.
I have a rate limiting logic, but even if I clear all rate limiting data, it still happens. Only with a specific user.
I created another account on FaunaDB for testing purposes and a new database. If I restore the old database data to that new database, everything works!
So I recreated the entire database on the old FaunaDB account and the problem persists.
Is anyone experiencing something similar?
Is there any information in cache?
Login(Match(Index("accounts_by_email"), "email#email.com"), {
password: "secret",
})
/* returns
Error: [
{
"position": [],
"code": "authentication failed",
"description": "The document was not found or provided password was incorrect."
}
]
*/
The password is not incorrect. It works on the other FaunaDB account with the data restored.
./fdm -source path=backup -dest key={admin_key}
Yes, that was a temporary problem. I experienced it was well at a certain moment in my own Fwitter example. Our uniqueness detection didn't play well with code that created/updated/deleted things in one complex FQL flow which the code is doing :). I ticketed that and it should be fixed in the meantime.
It's good to know that the rate limiting in there was a bit me experimenting with events. It can also be written much simpler, I guess I was a bit to deep zoned and to be fair.. I just joined FaunaDB back then. I'm working on a skeleton app that will contain that simpler version. In the meantime here is the code:
Simpler rate limiting
import { rateLimiting } from '../../fauna-queries/helpers/errors'
import faunadb from 'faunadb'
/*
* Ideally we limit the amount of calls that come to Login.
*/
const q = faunadb.query
const {
If,
Epoch,
Match,
Index,
Collection,
Let,
Var,
Paginate,
Select,
TimeDiff,
Or,
GTE,
Abort,
Create,
IsEmpty,
Count,
LT,
Do,
Now,
Subtract
} = q
function AddRateLimiting(action, FqlQueryToExecute, Identifier, calls, perMilliseconds) {
const ExecuteAndCreateLog = Do(
Create(Collection('logs'), {
data: {
action: action,
identity: Identifier
}
}),
FqlQueryToExecute
)
return Let(
{
logsPage: Paginate(Match(Index('logs_by_action_and_identity_ordered_by_ts'), action, Identifier), {
size: calls
})
},
If(
Or(IsEmpty(Var('logsPage')), LT(Count(Select(['data'], Var('logsPage'))), calls)),
// If no logs exist yet, create one.
ExecuteAndCreateLog,
Let(
{
// the page looks like { data: [timestamp1, timestamp2,...]},
// we will retrieve the last timestamp of that page. If the pagesize would be 3, it would be the oldest of these 3 events.
// since the index is ordered from new to old.
timestamp: Select(['data', Subtract(calls, 1)], Var('logsPage')),
// transform the Fauna timestamp to a Time object
time: Epoch(Var('timestamp'), 'microseconds'),
// How long ago was that event in ms
ageInMs: TimeDiff(Var('time'), Now(), 'milliseconds')
},
If(
GTE(Var('ageInMs'), perMilliseconds),
// Then great we execute
ExecuteAndCreateLog,
// Else.. Abort! Rate-limiting in action
Abort(rateLimiting)
)
)
)
)
}
Blocking after faulty logins
I also separated blocking three faulty logins since I was kinda abusing that rate-limiting system for it. A few undefines in this code of course, it's just meant to give you an idea of how it looks for more info, keep an eye out for the skeletons + blogs to come out.
// Let's wrap some other functionality around the login.
const BlockThreeFaultyLogins = Do(
If(
GTE(Count(Match(Index('logs_by_action_and_identity'), 'faulty_login', email)), MAX_LOGIN_ATTEMPTS),
// Abort if exceeded
Abort(tooManyFaultyLogins),
// Else, just continue as usual!
Let(
{
login: LoginFQL
},
Do(
If(
Equals(false, Var('login')),
// if the login is faulty, we'll add a log entry
Create(Collection('logs'), {
data: {
action: 'faulty_login',
identity: email
}
}),
// Else, we will clean up the faulty_login logs
q.Map(
Paginate(Match(Index('logs_by_action_and_identity'), 'faulty_login', email)),
Lambda(['logRef'], Delete(Var('logRef')))
)
),
Var('login')
)
)
)
)

Parse Promises in Cloud Code / Express.js [duplicate]

I had a look at the bluebird promise FAQ, in which it mentions that .then(success, fail) is an antipattern. I don't quite understand its explanation as for the try and catch.
What's wrong with the following?
some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })
It seems that the example is suggesting the following to be the correct way.
some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })
What's the difference?
What's the difference?
The .then() call will return a promise that will be rejected in case the callback throws an error. This means, when your success logger fails, the error would be passed to the following .catch() callback, but not to the fail callback that goes alongside success.
Here's a control flow diagram:
To express it in synchronous code:
// some_promise_call().then(logger.log, logger.log)
then: {
try {
var results = some_call();
} catch(e) {
logger.log(e);
break then;
} // else
logger.log(results);
}
The second log (which is like the first argument to .then()) will only be executed in the case that no exception happened. The labelled block and the break statement feel a bit odd, this is actually what python has try-except-else for (recommended reading!).
// some_promise_call().then(logger.log).catch(logger.log)
try {
var results = some_call();
logger.log(results);
} catch(e) {
logger.log(e);
}
The catch logger will also handle exceptions from the success logger call.
So much for the difference.
I don't quite understand its explanation as for the try and catch
The argument is that usually, you want to catch errors in every step of the processing and that you shouldn't use it in chains. The expectation is that you only have one final handler which handles all errors - while, when you use the "antipattern", errors in some of the then-callbacks are not handled.
However, this pattern is actually very useful: When you want to handle errors that happened in exactly this step, and you want to do something entirely different when no error happened - i.e. when the error is unrecoverable. Be aware that this is branching your control flow. Of course, this is sometimes desired.
What's wrong with the following?
some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })
That you had to repeat your callback. You rather want
some_promise_call()
.catch(function(e) {
return e; // it's OK, we'll just log it
})
.done(function(res) {
logger.log(res);
});
You also might consider using .finally() for this.
The two aren't quite identical. The difference is that the first example won't catch an exception that's thrown in your success handler. So if your method should only ever return resolved promises, as is often the case, you need a trailing catch handler (or yet another then with an empty success parameter). Sure, it may be that your then handler doesn't do anything that might potentially fail, in which case using one 2-parameter then could be fine.
But I believe the point of the text you linked to is that then is mostly useful versus callbacks in its ability to chain a bunch of asynchronous steps, and when you actually do this, the 2-parameter form of then subtly doesn't behave quite as expected, for the above reason. It's particularly counterintuitive when used mid-chain.
As someone who's done a lot of complex async stuff and bumped into corners like this more than I care to admit, I really recommend avoiding this anti-pattern and going with the separate handler approach.
By looking at advantages and disadvantages of both we can make a calculated guess as to which is appropriate for the situation.
These are the two main approaches to implementing promises. Both have it's pluses and minus
Catch Approach
some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })
Advantages
All errors are handled by one catch block.
Even catches any exception in the then block.
Chaining of multiple success callbacks
Disadvantages
In case of chaining it becomes difficult to show different error messages.
Success/Error Approach
some_promise_call()
.then(function success(res) { logger.log(res) },
function error(err) { logger.log(err) })
Advantages
You get fine grained error control.
You can have common error handling function for various categories of errors like db error, 500 error etc.
Disavantages
You will still need another catch if you wish to handler errors thrown by the success callback
Simple explain:
In ES2018
When the catch method is called with argument onRejected, the
following steps are taken:
Let promise be the this value.
Return ? Invoke(promise, "then", « undefined, onRejected »).
that means:
promise.then(f1).catch(f2)
equals
promise.then(f1).then(undefiend, f2)
Using .then().catch() lets you enable Promise Chaining which is required to fulfil a workflow. You may need to read some information from database then you want to pass it to an async API then you want to manipulate the response. You may want to push the response back into the database. Handling all these workflows with your concept is doable but very hard to manage. The better solution will be then().then().then().then().catch() which receives all errors in just once catch and lets you keep the maintainability of the code.
Using then() and catch() helps chain success and failure handler on the promise.catch() works on promise returned by then(). It handles,
If promise was rejected. See #3 in the picture
If error occurred in success handler of then(), between line numbers 4 to 7 below. See #2.a in the picture
(Failure callback on then() does not handle this.)
If error occurred in failure handler of then(), line number 8 below. See #3.b in the picture.
1. let promiseRef: Promise = this. aTimetakingTask (false);
2. promiseRef
3. .then(
4. (result) => {
5. /* successfully, resolved promise.
6. Work on data here */
7. },
8. (error) => console.log(error)
9. )
10. .catch( (e) => {
11. /* successfully, resolved promise.
12. Work on data here */
13. });
Note: Many times, failure handler might not be defined if catch() is
written already.
EDIT: reject() result in invoking catch() only if the error
handler in then() is not defined. Notice #3 in the picture to
the catch(). It is invoked when handler in line# 8 and 9 are not
defined.
It makes sense because promise returned by then() does not have an error if a callback is taking care of it.
Instead of words, good example. Following code (if first promise resolved):
Promise.resolve()
.then
(
() => { throw new Error('Error occurs'); },
err => console.log('This error is caught:', err)
);
is identical to:
Promise.resolve()
.catch
(
err => console.log('This error is caught:', err)
)
.then
(
() => { throw new Error('Error occurs'); }
)
But with rejected first promise, this is not identical:
Promise.reject()
.then
(
() => { throw new Error('Error occurs'); },
err => console.log('This error is caught:', err)
);
Promise.reject()
.catch
(
err => console.log('This error is caught:', err)
)
.then
(
() => { throw new Error('Error occurs'); }
)

Proper way to send Web API response

I read somewhere that TRY CATCH is not recommended in Web API methods.
I'm making the following call into a method and if all goes well, I want to return an Employee object along with Status 200 but if something goes wrong e.g. database call fails, etc. I want to return status 500. What's the right way to handle that code?
[HttpPost]
public async Task<IHttpActionResult> PostNewEmployeeAsync(Employee emp)
{
var newEmployee = await RegisterEmployee(emp);
return Ok(emp);
// What if I had a database error in RegisterEmployee method. How do I detect the error and send InternalServerError()
}
private async Task<Employee> RegisterEmployee(Employee emp)
{
// Call DB to register new employee, then return Employee object
}
Your code should return the error code that matches the case that you have, for example if your code couldn't find the required resource in the database return NotFound,
but if you code raises an exception, avoid wrapping your code by try/catch block and instead the exception should bubble up to the level that you can handle it globally, to do this you have many options like :
1- Implement an ExceptionFilter where you can handle all the unhandled exceptions raised in your controllers (this doesn't include any exception happens before the controllers in the pipeline).
See this for more details about ExceptionFilterAttribute.
2- If you are using Web API 2, you can implement the interface IExceptionHandler where you can handle all the exception happens anywhere in the pipeline and there you can return the errors you want.
See this for more details about Global Exception Handling in Web API 2.
Hope that helps.
You don't want to avoid try/catch entirely, you just need to be really careful about it. Wrap your code in a try block, and catch the exception you're expecting. Inside the catch, return the error response.