An example legacy-free Node.js, Express, TypeScript legacy-free hello world project with ES6 modules, explicitly typed parameters in callbacks, and Jest for unit tests.
Works with Node v12.16.3 and up.
This is a version of an Express framework "Hello world" example updated to:
- Use TypeScript and explicitly declare the types of callback parameters, because catching errors at compile time is good.
- Use standard (post-2015 ES6) modules, because standardization is good.
- Use Yarn for package management, because community is good.
- Use ts-jest for unit tests, because Jest is written in TypeScript and ts-jest has some JetBrains involvement.
- Use Handlebars via express-handlebars for templating, because simple is good.
Start via yarn run server
.
Run tests via yarn test
.
Also included are some IntelliJ IDEA configs so you can push a button to run.
Here are the key "tricks", some of which are still poorly or inaccurately documented on the web:
- You need to put
"type": "module"
inpackage.json
to allow standard module imports in Node.js, as of Node 12.x. - Even after doing that, you still need to use the
--experimental-modules
command line flag to enable ESM on LTS versions of Node (e.g. 12.x), so that your application's main compiled code can be loaded in module mode. - ...and
--experimental-specifier-resolution=node
even with Node 14, if you want to load modules within your application without having to specify the filename extension or renamejs
files tomjs
. (The standard doesn't seem to specify whether module names should include the extension or not, so either behavior is allowed.) - You need to enable ES6+ modules in TypeScript by setting
"module": "es6"
intsconfig.json
. - After doing that, the default module resolution for TypeScript is "classic" so you have to set
"moduleResolution": "Node"
intsconfig.json
as well. - Since Express isn't packaged as an ES6 module yet, you need to set
"allowSyntheticDefaultImports": true
intsconfig.json
so you can import the mainexpress
function. - Don't follow instructions and run
yarn ts-jest config:init
, because that seems to generate a config that doesn't run Node properly. Instead, put the Jest config inpackage.json
.
If you skip #2 above, you'll get an error like this:
internal/modules/cjs/loader.js:1174
throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
^
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /path/to/generated/javascript/main.js
At the time of writing, Yarn complains all the time about the license field in the package.json
file.
This seems to be a Yarn bug.