44

I am currently using Babel.

I did the following before with require:

try {  
    var myModule = require('my-module');
} catch (err) {
    // send error to log file
}

However when trying to do this with import:

try {  
    import myModule from 'my-module';
} catch (err) {
    // send error to log file
}

I get the error:

'import' and 'export' may only appear at the top level

Now I understand that import is different to require. From reading Are ES6 module imports hoisted? import hoists which means the imports are loaded before code execution.

What I did before was that if any requires failed a log was created which alerted me via email (sending logs to logstash etc.). So my question boils down to the following.

How does one handle import errors in a good practice fashion in nodejs? Does such a thing exist?

basickarl
  • 37,187
  • 64
  • 214
  • 335
  • Did you use Babel ? – Tugrul Aug 14 '16 at 20:09
  • @Tugrul Ah yes I do, I should of mentioned this. – basickarl Aug 14 '16 at 20:10
  • 1
    I have no question now:) – Tugrul Aug 14 '16 at 20:11
  • 1
    I think that if you need conditional loading of modules (or need to catch errors when loading them), you have to stick to using `require()`. See also [this question and its answers](http://stackoverflow.com/questions/36805664/when-using-es6-import-statment-is-there-a-way-to-protect-against-items-being-un). – robertklep Aug 14 '16 at 20:11
  • Try to look into `System.import`, it's not implemented in babel though – bren Aug 14 '16 at 20:36
  • Dynamic import combined with top-level await will make this simpler in the future: https://github.com/tc39/proposal-top-level-await#dependency-fallbacks – user2191332 Sep 17 '20 at 12:24

4 Answers4

30

You can't catch static imports errors (cf. Boris' answer)

Yet, you could use a dynamic import() for that.

It's now supported by all evergreen browsers & Node, and is part of the standards since ES2020.

class ImportError extends Error {}

const loadModule = async (modulePath) => {
  try {
    return await import(modulePath)
  } catch (e) {
    throw new ImportError(`Unable to import module ${modulePath}`)
  }
}
Caveman
  • 2,527
  • 1
  • 17
  • 18
  • The dynamic `import()` returns a Promise and thus you can also use the Promise-style of handling errors: `import(modulePath).catch(e => Promise.reject("Module " + modulePath + " not found.")).then(module => { /* use module here */ console.log(module); })`. See also https://stackoverflow.com/a/47755315/341201 – feeela Jun 26 '21 at 16:19
9

[2021 Edit] Look at Caveman answer for a more up to date answer allowing to make dynamic import

This talk give it away : https://github.com/ModuleLoader/es-module-loader/issues/280 and agree with what you said.

import only works at the base level. They are static and always load before the module is run.

So you can't do a code check.

But, the good news is that as it's static, it can be analysed, tools like webpack throw errors at build time.

Caveman
  • 2,527
  • 1
  • 17
  • 18
Boris Charpentier
  • 3,515
  • 25
  • 28
  • 2
    Note that this answer is no longer entirely correct: you can't do this for static imports but you _can_ do this for [dynamic imports](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports) which return a promise that you can either `.catch()` in a promise chain, or `try await/catch` if you're in an async function. – Mike 'Pomax' Kamermans Aug 17 '21 at 16:02
6

Supplementary dynamic import.

class ImportError extends Error {}

const loadModule = async (modulePath) => {
  try {
    return await import(modulePath)
  } catch (e) {
    throw new ImportError(`Unable to import module ${modulePath}`)
  }
}

async function main() {
  // import myDefault, {foo, bar} from '/modules/my-module.js'
  const { default: myDefault, foo, bar } = await loadModule('/modules/my-module.js')
}

or chained_promises

import("/modules/my-module.js").then(module=>{
  module.foo()
  module.bar()
}).catch(err=>
  console.log(err.message)
)

or Destructuring assignment

import("/modules/my-module.js").then(({foo, bar})=>{
  foo()
  bar()
}).catch(err=>
  console.log(err.message)
)
Carson
  • 6,105
  • 2
  • 37
  • 45
-6

A very modern answer to this now since cloud services is becoming the norm is to let the import fail and log to stderr as cloud services logs from stderr to their logging service. So basically you don't need to do anything.

basickarl
  • 37,187
  • 64
  • 214
  • 335
  • 1
    well, to give a better perspective on the problem here, in my case the failing import crashed the entire app since the "problematic" library is added in the main.js file. Now I can either complain that the library I'm using doesn't handle a specific case (incognito iframe to be precise) and to tell my users to blame them or - which I choose - to catch the error and to provide a fallback so that my users can keep using the app. And for the latter I need a way to catch errors during import which is what the original question is about. – vir us Jan 03 '23 at 23:55