Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'SyntaxError: await is only valid in async function' when using top level await feature in Node v14.14.0

I'm trying to use the new top level await that was added in Node v14.8. See here and here for more info. I've not found a question on here about issues with using this new feature, but I hope this isn't a duplicate. The closest is @DanStarns answer on this question, but I have tried his solution as well (and his answer was before the 14.8 release).

  • I understand that an IIFE has been one of the go-to workarounds in the past but this question is with regards to the new top level await feature and why it is throwing an error despite having been released with Node 14.8

I'm in the process of extracting my Axios instance from my controller into a standalone service. In that service file, I'm attempting to instantiate and then export an Axios instance created with a stored authorization header from Spotify's API. Since this is then imported into my controller (passed into the constructor then it exports new SpotifyApiController(SpotifyApiService)) and imports are synchronous, I am trying to use the new top level await feature to have this fully instantiated at startup/import, but getting the following error:

[nodemon] starting `babel-node bin/www.js --harmony-top-level-await --experimental-repl-await`
E:\projects\harmonic-mixing-companion\server\services\SpotifyApiService.mjs:117
var SpotifyApiService = await createApiService();
                        ^^^^^

SyntaxError: await is only valid in async function
    at wrapSafe (internal/modules/cjs/loader.js:979:16)
    at Module._compile (internal/modules/cjs/loader.js:1027:27)
    at Module._compile (E:\projects\harmonic-mixing-companion\server\node_modules\pirates\lib\index.js:99:24)
    at Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Object.newLoader [as .mjs] (E:\projects\harmonic-mixing-companion\server\node_modules\pirates\lib\index.js:104:7)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (E:\projects\harmonic-mixing-companion\server\controllers\/SpotifyApiController.js:1:1)

From the error, it looks like babel-node is transpiling my code properly since const SpotifyApiService = ... became var SpotifyApiService = .... I have also added "@babel/plugin-syntax-top-level-await" to my babel.config.json.

The service's file extension is .mjs. I have also tried setting "type": "module" in my server's package.json too, but that was unfruitful as well. Correct me if I'm wrong, but it also doesn't sound right to me for my whole backend server to be set as a module because it doesn't sound like a modular unit to me (versus a reusable SpotifyApiService).

Double checked my node's version using console.log(process.version); at the top of my main entry file and it printed the expected 14.14.0 version.


SpotifyApiController.js Snippet:

import SpotifyApiService from '../services/SpotifyApiService.mjs';
import handleHttpError from '../handlers/handleHttpError';

class SpotifyApiController {
  constructor(spotifyApiService) {
    this.spotifyApi = spotifyApiService;
  }
  ...
}

SpotifyApiService.mjs Snippet:

...
const createApiService = async () => {
  const accessToken = await authorizeApp();

  console.log(accessToken);

  const authHeader = { Authorization: `Bearer ${accessToken}` };

  const axiosInstance = axios.create({
    baseURL: 'https://api.spotify.com/v1',
    headers: authHeader,
  });

  createAuthRefreshInterceptor(axiosInstance, authorizeApp);

  return axiosInstance;
};

const SpotifyApiService = await createApiService();

export default SpotifyApiService;

package.json Dependencies:

  "devDependencies": {
    "@babel/cli": "~7.12.1",
    "@babel/core": "~7.12.1",
    "@babel/eslint-parser": "~7.12.1",
    "@babel/node": "~7.12.1",
    "@babel/plugin-proposal-class-properties": "~7.12.1",
    "@babel/plugin-syntax-top-level-await": "~7.12.1",
    "@babel/plugin-transform-arrow-functions": "~7.12.1",
    "@babel/plugin-transform-runtime": "~7.12.1",
    "@babel/preset-env": "~7.12.1",
    "@babel/preset-react": "~7.12.1",
    "babel-loader": "~8.1.0",
    "nodemon": "~2.0.5"
  },
  "dependencies": {
    "axios": "~0.20.0",
    "axios-auth-refresh": "~3.0.0",
    "cookie-parser": "~1.4.5",
    "cors": "~2.8.5",
    "debug": "~4.2.0",
    "dotenv": "~8.2.0",
    "express": "~4.17.1",
    "querystring": "~0.2.0"
  }

Server is started with the following npm script: "dev-server": "nodemon --exec babel-node bin/www.js --ignore dist/"

like image 739
Azanadra Avatar asked Oct 30 '25 15:10

Azanadra


2 Answers

You can wrap it in async function:

var SpotifyApiService = (async()=> await createApiService())();

After you can use than, catch

like image 180
Odinn Avatar answered Nov 01 '25 07:11

Odinn


As the documentation indicates, babel only enables the "syntax" for this feature,

Syntax only

This plugin only enables parsing of this feature. Babel doesn't support transforming top-level await, but you can use Rollup's experimentalTopLevelAwait or webpack@5's experiments.topLevelAwait options.

which just means that babel won't complain when compiling your code but it doesn't actually implement the steps that will actively transform a line such as var SpotifyApiService = await createApiService(); into valid ES5.

So anything executing your code ( browser, node ) will complain since they don't know how to handle this feature.

like image 23
MimiEAM Avatar answered Nov 01 '25 05:11

MimiEAM



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!