Docs
Coding conventions
Error handling

Error handling

  • Use try/catch blocks to handle errors that could occur during runtime. Render an error snackbar to the user with a descriptive error message when an error occurs.

    try {
      await savePatientData(patient);
      showSnackbar({ title: t('patientSaved', 'Patient data saved successfully') });
    } catch (error) {
      showSnackbar({
        title: t('errorSavingPatient', 'Error saving patient'),
        kind: 'error',
        description: error?.message ?? t('unknownError', 'An unknown error occurred'),
      });
    }
  • Don't check the response status of a request to determine whether to show a success snackbar. Instead, always show a success snackbar after a request is successful. openmrsFetch will throw an error if the request fails, so you don't need to check the response status to determine whether to show a success or error snackbar.

    // Don't do this
    saveVisit(visitPayload).then((response) => {
      if (response.status === 201) {
        // Handle the success case
      } else {
        // Handle the error case
      }
    });
    // Instead, do this:
    saveVisit(visitPayload).then(() => {
      // Handle the success case
    }).catch((error) => {
      // Handle the error case
    });

    Alternatively, you can use async/await syntax to handle the success and error cases:

    try {
      await saveVisit(visitPayload);
      // Handle the success case
    } catch (error) {
      // Handle the error case
    }
  • When handling errors in async functions, prefer async/await over promise chains for better readability and error handling clarity. For example:

    // Avoid promise chains
    savePatientData(patient)
      .then((response) => {
        showSnackbar({ kind: 'success', title: t('patientSaved', 'Patient data saved successfully') });
        return response;
      })
      .then(processPatientData)
      .catch((error) => {
        showSnackbar({
          title: t('errorSavingPatient', 'Error saving patient'),
          kind: 'error',
          description: error?.message
        });
      });
     
    // Prefer async/await
    async function handleSavePatient(patient) {
      try {
        await savePatientData(patient);
        showSnackbar({ kind: 'success', title: t('patientSaved', 'Patient data saved successfully') });
      } catch (error) {
        showSnackbar({
          title: t('errorSavingPatient', 'Error saving patient'),
          kind: 'error',
          description: error?.message
        });
      }
    }

    The async/await approach:

    • Makes the code flow more readable and sequential.
    • Simplifies error handling with a single try/catch block.
    • Makes it easier to debug since stack traces are more meaningful.
    • Allows easier handling of multiple async operations.

    Use a finally block to execute cleanup code that should run regardless of whether the operation succeeded or failed:

    async function handleSavePatient(patient) {
      try {
        // ...
      } catch (error) {
        // Handle the error case
      } finally {
        // Do any additional cleanup work
        closeModal();
      }
    }