Setup Node.js and TypeScript can be straightforward. Our daily work sometimes involves a lot of debugging and we also need to reload our server.
Let's walk through how we can setup Node.js with TypeScript + Nodemon + Debug inside VSCode (as an extra, you can debug it inside Chrome DevTools).
Less talk, more code! π€
π¦ Dependencies and Configurations
$ mkdir devto-node-typescript
$ cd $_
Adding our dependencies:
$ yarn init -y
$ yarn add @types/express@^4.16.1 express@^4.16.4 nodemon@^1.18.10 ts-node@^8.0.3 typescript@^3.3.4000
Here is our dependencies list:
@types/express@^4.16.1
-
express@^4.16.4
nodemon@^1.18.10
ts-node@^8.0.3
typescript@^3.3.4000
Now, let's create our nodemon.json
file:
$ touch nodemon.json
$ vim $_
And paste the following:
{
"restartable": "rs",
"ignore": [".git", "node_modules/**/node_modules"],
"verbose": true,
"execMap": { // [A]
"ts": "node --require ts-node/register"
},
"watch": ["src/"],
"env": {
"NODE_ENV": "development"
},
"ext": "js,json,ts"
}
-
// [A]: Here we're saying to nodemon: "Hey nodemon, if we run "nodemon" with a ".ts" file, please, use this line to execute the file". Because of that, now we can do
nodemon server.ts
and it will usets-node
to compile our file.
To learn more about Nodemon config, you can check the sample in their repository
Now, let's create our tsconfig.json
file:
$ touch tsconfig.json
$ vim $_
And use:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./dist",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
There's no much difference from the default config (e.g. when you run tsc --init
)
We are almost there, let's create a npm script to start our future server:
// ...
"scripts": {
"dev": "nodemon src/entry.ts"
}
// ...
Now, it's time to create our server.
π» Writing our server and some routes
As we saw in our nodemon and npm scripts configuration, we need to create a folder called src
and file called entry.ts
:
$ mkdir src
$ cd $_
$ touch entry.ts
$ vim $_
Let's write the following:
import express from 'express';
const server = express();
server.use('/_healthcheck', (_req, res) => {
res.status(200).json({ uptime: process.uptime() });
});
server.listen(4004, () => { console.log('Running at localhost:4004') })
Now we can run:
$ yarn dev
And see our Node.js with TypeScript + Nodemon server running:
Fantastic! π
But wait... one important part is missing, how I can debug this thing? π±
π Attaching Debug configuration to our project
Using nodemon we simply pass the --inspect
flag to make our Node process debuggable. Let's create a new npm script for that:
// ...
"scripts": {
// ...
"dev:debug": "nodemon --inspect src/entry.ts"
}
// ...
If we run:
$ yarn dev:debug
We can see the debug option in our logs:
Great! Let's create our VSCode configuration:
$ mkdir .vscode
$ cd $_
$ touch launch.json
$ vim $_
And paste the following:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Node: Nodemon",
"processId": "${command:PickProcess}",
"restart": true,
"protocol": "inspector"
}
]
}
This configuration is based in one of Microsoft Recipes for VSCode.
Let's open our VSCode editor and do go the "Debug" panel:
With our yarn dev:debug
running, let's press the green arrow shown in the screenshot above.
First, we need to select the process pointing to our server:
After we select it, we can see a floating debug panel and some UI changes:
Now you can add a breakpoint in your code and have fun inside your editor with server reload by nodemon:
Delightful experience! β€οΈ π π
π Extra: It also works in Google Chrome DevTools
Yep. We can use the following steps:
- Navigate to
chrome://inspect
- Click
inspect
inside Remote Target section pointing to your server and have fund debugging your server using Chrome DevTools:
NOTE: I tried to use "Open dedicated DevTools for Node" option, but it doesn't work well :( If you know how to make it work, share with us!
βοΈ I want it all and more!
What about tests? Folder structure? There's a default project?
Say no more! For my personal use, I've a small non-opinionated default project in:
https://github.com/oieduardorabelo/node-typescript
You will find:
- Node.js + TypeScript
- Jest
- Nodemon
Feel free to fork, contribute and use!
Happy coding! π
Top comments (10)
Nice, thanks for sharing.
For anyone having problems with vscode not showing the node process, do this:
nodemon --inspect:9229 src/server.ts
"processId": "${command:PickProcess}"
with:"port": 9229
inside the launch.json file. Thats all.This is very handy to know, and I've used some of this to debug Angular Universal server side code.
In the GitHub project you linked, I notice you use
webpack
for production build. Is it necessary to usewebpack
when using TypeScript server side?oh, thanks mate! didn't noticed it, it should be in a separated branch, not master.
it is not necessary, it is my personal approach to always bundle my server-side code, inspired by github.com/jaredpalmer/backpack
i'll update my master branch
Exactly what I was looking for... Thank you Eduardo!
Thanks for sharing Eduardo.
Thank you for doing God's work. This is amazing.
My biggest issue has been getting nodemon working with path aliases defined in tsconfig.json
Awesome, thanks
But can you please explain, how do I add support for module aliases?
thanks for your comment,
absolutely, for this setup it would be a little bit tricky, because:
let's put the production scenario above aside, and focus on "dev" only, we can do:
let's clone the repo:
and install tsconfig-paths
inside our
tsconfig.json
we can definebaseUrl
andpaths
:now we can use the alias
#domains
in any file, let's updateserver.ts
:now, let's register the module alias in our
nodemon.json
:now we can start the server with aliases:
and it should work as before, but with aliases!
now, for the Jest mappings, you need to keep the same mappings from
tsconfig.json
but in the Jest syntax/config, we need to usemoduleNameMapper
. I'll add the Jest config inpackage.json
for this example:and now you can run your tests as before:
so far, so good.
now, for the production scenario, I do not have a good straight answer, because it requires a considerable amount of work to adapt it per project.
but thinking about the process to make it work, we could use something like module-alias
and we would need:
npm run build
(e.g. typescript => node.js in dist/)package.json
withmodule-alias
insidedist/
tsconfig.json
insidedist/package.json
but usingmodule-alias
syntaxcd dist/ && npm install
to installmodule-alias
for productionnode --require module-alias/register entry.js
phew, what a setup! ahahah
i hope it helped! π
It would be nice if you could do an example with an async/await application on node and typescript.
Thank you, Eduardo!
This help me a lot.