Propagating server side exceptions to Lightning Components.

An exception is a error condition or unexpected behavior that is encountered by an executing block of code. Exceptions can be thrown because of a error in executing code or in code that is being called by your code. Exception handling strategy is to gracefully handle error condition.

Apex provides try, catch, and finally statements to gracefully recover from exceptions and throw statements to generate exception. If an end user runs into an exception that occurred in apex code, an error message appears on user interface with description of error. This kind of error message might not be user friendly. You need to display some user friendly message and log actual message for more technical team to look into the error.

Lets take an example of sending a request from a Lightning component to an Apex Controller this has several failure points. Visualforce provided us with tags apex:pageMessage and apex:pageMessages for rendering messages and following apex to set message to be displayed. ApexPages.addmessage(new ApexPages.message(ApexPages.severity.FATAL,’Last name is required field’));

Lightning components, don’t provide same functionality of Visualforce page to display error messages.

The Lightning Component communicates with Apex via server-side actions. These actions are declared as static methods annotated with @AuraEnabled. It is recommended to wrap the code that can trigger exceptions in a try-catch block and re-throw an exception of type AuraHandledException in the catch block. This allows you to provide a custom user-friendly error message.

Below is the sample code that we have used to display custom messages in lightning components when exception gets thrown in apex code.

<!-- 
    /**
     * @name       : displayCustomMessage.cmp
     * @category   : Lightning Component
     * @description: Lightning custom component for displaying custom messages eg. error messages
     * @author     : Accletron Consulting   
     * @copyright  : Copyright © 2019 Accletron Consulting; All Rights Reserved
     */
 -->
<aura:component access="global">
        <!-- 
        /**
         * @param message : message to display
         * @param messageType : Valid values [default|info|success|warning|error|offline|inverse|shade]
         * default : Sets the background color to white
         * info    : Sets the background color to gray-ish blue
         * success : Sets the background color to green
         * warning : Sets the background color to yellow
         * error   : Sets the background color to red
         * offline : Sets the background color to black
         * inverse : Sets the background color to dark blue
         * shade   : Sets the background color to gray
         */
        -->
        <aura:attribute name='message'     type="String" default=""/>
        <aura:attribute name="messageType" type="String" default="info"/>
        <div class="container">
            <article class="slds-card">
                <div class="{!'slds-box slds-theme_shade slds-theme_alert-texture slds-theme_'+ v.messageType}">
                    <span class="slds-text-body_regular">
                        <aura:unescapedHtml value="{!v.message}"/>
                    </span>
                </div>
            </article>
        </div>
</aura:component>
<!-- 
    /**
     * @name       : demoComponent.cmp
     * @category   : Lightning Component
     * @description: Lightning custom component for displaying custom messages eg. error messages
     * @author     : Accletron Consulting   
     * @copyright  : Copyright © 2019 Accletron Consulting; All Rights Reserved
     */
 -->
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId"  access="global" controller="DemoController">
    <aura:attribute name="message"     type="String"  default=""/>
    <aura:attribute name="messageType" type="String"  default="info"/> 
    <aura:attribute name="isError"     type="Boolean" default="false"/>    
        <div class="container">
            <article class="slds-card">
                <!-- Base variant: Makes a button look like a link -->
                <lightning:button label="default" title="default" onclick="{! c.handleClick }"/>
                <lightning:button label="warning" title="warning"  iconName="utility:warning" onclick="{! c.handleClick }"/>
                <lightning:button label="offline" title="offline"  iconName="utility:offline" onclick="{! c.handleClick }"/>
                <lightning:button label="shade"   title="shade"   onclick="{! c.handleClick }"/>
                
                <!-- Neutral variant (default) -->
                <lightning:button label="info" title="Info"  iconName="utility:info" onclick="{! c.handleClick }"/>
                
                <!-- Brand variant: Identifies the primary action in a group of buttons -->
                <lightning:button variant="brand" label="inverse" title="Inverse" onclick="{! c.handleClick }" />
                
                <!-- Destructive variant: Identifies a potentially negative action -->
                <lightning:button variant="destructive" label="error" title="Destructive action" iconName="utility:error" onclick="{! c.handleClick }"/>
                
                <!-- Success variant: Identifies a successful action -->
                <lightning:button variant="success" label="success" title="Success"  iconName="utility:success" onclick="{! c.handleClick }"/>
                <lightning:button label="No Error" title="default" iconName="utility:close" onclick="{! c.handleNoError }"/>
            </article>
            <div id="demo">
                <aura:if isTrue="{!v.isError}">
                            <c:displayCustomMessage messageType='{!v.messageType}' message='{!v.message}' />
                </aura:if> 
            </div>
        </div>
</aura:component>

/**
 * @name       : demoComponentController.js
 * @category   : Lightning Component Controller
 * @description: Client side controller for displaying custom messages eg. error messages
 * @author     : Accletron Consulting   
 * @copyright  : Copyright © 2019 Accletron Consulting; All Rights Reserved
 */

({
    handleClick : function (component, event, helper) {
        let action = component.get("c.makeError");
        action.setCallback(this, function(response) {
            let state = response.getState();
            if (state === "SUCCESS") {
            }else if (state === "ERROR") {
                let errors = response.getError();
                let message = 'Unknown error';
                if (errors && Array.isArray(errors) && errors.length > 0) {
                    message = errors[0].message;
                }
                //alert(event.getSource().get("v.label"));
                component.set('v.messageType',event.getSource().get("v.label") );
                component.set('v.message',message );
                component.set('v.isError','true' );
                console.error(message);
            }else {
            }
        });  
        $A.enqueueAction(action);    
    },
    handleNoError : function (component, event, helper) {
        component.set('v.isError','false' );
    }
})
/**
 * @name       : DemoController.cls
 * @category   : Apex Controller
 * @description: Server side controller for throwing exception
 * @author     : Accletron Consulting   
 * @copyright  : Copyright © 2019 Accletron Consulting; All Rights Reserved
 */
public with sharing class DemoController {
    public DemoController() {

    }
    @AuraEnabled
    public static string makeError(){
      throw new AuraHandledException('This is test for handling exception thrown from server side controller');
    }
}

Lightning notification library can also be utilized but there is limitation when displaying toast or notice message in Lightning Experience and Salesforce1.
https://success.salesforce.com/issues_view?id=a1p3A000000mCRqQAM&title=lightning-component-force-showtoast-displays-toast-message-behind-action-window-in-lightning-experience-and-salesforce1

References