549

I'm trying to run a dev server with TypeScript and an Angular application without transpiling ts files every time.

What I found is that I can run .ts files with ts-node but I want also to watch .ts files and reload my app/server. An example of this is the command gulp watch.

25 Answers 25

1106

You can now simply npm install --save-dev ts-node nodemon and then run nodemon with a .ts file and it will Just Work:

nodemon app.ts

Previous versions:

I was struggling with the same thing for my development environment until I noticed that nodemon's API allows us to change its default behaviour in order to execute a custom command.

For example, for the most recent version of nodemon:

nodemon --watch "src/**" --ext "ts,json" --ignore "src/**/*.spec.ts" --exec "ts-node src/index.ts"

Or create a nodemon.json file with the following content:

{
  "watch": ["src"],
  "ext": "ts,json",
  "ignore": ["src/**/*.spec.ts"],
  "exec": "ts-node ./src/index.ts"      // or "npx ts-node src/index.ts"
}

and then run nodemon with no arguments.

By virtue of doing this, you'll be able to live-reload a ts-node process without having to worry about the underlying implementation.


And with even older versions of nodemon:

nodemon --watch 'src/**/*.ts' --ignore 'src/**/*.spec.ts' --exec 'ts-node' src/index.ts

Or even better: externalize nodemon's config to a nodemon.json file with the following content, and then just run nodemon, as Sandokan suggested:

{
  "watch": ["src/**/*.ts"],
  "ignore": ["src/**/*.spec.ts"],
  "exec": "ts-node ./index.ts"
}
Sign up to request clarification or add additional context in comments.

19 Comments

if index.ts is a express instance, how can i kill it and restart
you can also create a nodemon.json file with all the mentioned options in it like this: { "watch": ["src/**/*.ts"], "ignore": ["src/**/*.spec.ts"], "exec": "ts-node ./app-server.ts" } and just type nodemon
I made the mistake of adding ./ before the folder names and it broke. This worked for me: { "verbose": true, "watch": ["server/**/*.ts"], "ext": "ts js json", "ignore": ["server/**/*.spec.ts"], "exec": "ts-node index.ts" }. And command line: nodemon --watch server/**/*.ts --ignore server/**/*.spec.ts --verbose --exec ts-node index.ts
I would just like mention, that you also have to set the ext in the config file, so its look for ts changes. My config file look like this: { "watch": ["src/**/*.ts"], "ignore": ["src/**/*.spec.ts"], "ext": "ts js json", "_exec": "node dist/startup.js", "exec": "ts-node src/startup.ts" }
On Windows machines, DON'T use single quotes in your package.json. Replacing those with \" makes the script run fine: "nodemon --watch \"./src/**/*.ts\" -r dotenv/config --exec \"ts-node\" src/index.ts"
|
303

I've dumped nodemon and ts-node in favor of a much better alternative, ts-node-dev https://github.com/whitecolor/ts-node-dev

Just run ts-node-dev src/index.ts

[EDIT] Since I wrote this answer, nodemon has improved a lot, the required config is much lighter now and performance is much better. I currently use both (on different projects, obviously), and am satisfied with both.

19 Comments

And why is this better?
It's faster, and automatically detects which files need to be watched, no config needed.
This is the best (if not the only) option for ts-node, especially for larger projects. It doesn't recompile all files from scratch, but does an incremental compilation, like tsc --watch.
ts-node-dev is a nice idea but it has a lot of problems. It's really just not reliable enough for real-world use. The maintainer seems underequipped to deal with the issues, unfortunately. Back to nodemon I go...
I used this, but eventually I realized it's having major issues with picking up changed files. Saving a file, process is restarted, but somehow old files are still being used. Super annoying. Not an issue with accepted answer.🤷‍♂️
|
135

Node.js now supports TypeScript!

Node has added TypeScript support: Running TypeScript Natively

To use it:

  1. Install Node 22.7.0 or later

  2. Start Node with the appropriate flags

    • If you're using Node 22, you'll need to use --experimental-strip-types (this is no longer needed with Node 24)
    • If your TypeScript code uses enum or namespace, you'll also need to use --experimental-transform-types

Combined with the addition of watch mode, you can watch and reload TypeScript in Node without any special tools. For example, this will allow you to run npm run dev in your project to run and watch TypeScript files without any extra packages installed (typescript, tsx, ts-node, nodemon, etc.):

  "scripts": {
    "dev": "node --experimental-strip-types --watch src/index.ts"
  },

(Note that I would still recommend using the TypeScript compiler to check types as part of your build process as detailed below)

Alternative options

If you're not comfortable using Node's TypeScript support while it's still experimental, here are some other recommendations.

For ease of use: tsx

tsx is the easiest way to run TypeScript with Node, but it has limited configurability. Under the hood it uses esbuild, a JavaScript bundler written in Go that also transpiles TypeScript.

  1. Install tsx

    npm install --save-dev tsx
    
  2. Update your package.json, e.g.

    "scripts: {
      "dev": "tsx watch src/index.ts",
    
  3. Run it

    npm run dev
    

    (Adjust these steps if you just want to install tsx globally and run it directly)

For more configurability: nodemon/node-dev + ts-node + swc

This is just as fast as tsx and requires more setup but is also more configurable. It consists of using ts-node with swc, a TypeScript-compatible transpiler implemented in Rust which is an "order of magnitude faster" than the TypeScript transpiler.

  1. Install nodemon or node-dev (whichever you prefer)

    • nodemon

      npm install --save-dev nodemon 
      
    • node-dev

      npm install --save-dev node-dev 
      
  2. Set up ts-node with swc integration

    https://github.com/TypeStrong/ts-node#swc-1

    1. Install necessary packages

      npm install --save-dev ts-node @swc/core @swc/helpers regenerator-runtime
      
    2. Add this to tsconfig.json

        "ts-node": {
          "swc": true
        }
      
  3. Run nodemon or node-dev, e.g

    nodemon --watch src src/index.ts
    

    or:

    node-dev src/index.ts
    

A note on type checking and compilation

Very few tools outside the TypeScript compiler (tsc) itself actually do any type checking or compilation of TypeScript code; instead, they strip or convert it so it can be run as plain JavaScript. This includes most of the tools listed here, even Node's own native TypeScript support. This is typically not a problem because most editors have type checking built-in and running tsc to do type checking should still be part of your build process. You can also do type checking separately alongside your tests or as a pre-push hook via tsc --noEmit.

In some cases you may also wish to use tsc during your build process to compile your TypeScript if you rely on it for more than type checking.

10 Comments

If you install nodemon locally, you cannot run nodemon on cmd. Instead, install it globally with -g.
You can still run it without it being installed globally (e.g. node_modules/.bin/nodemon), but given that I almost always need to provide flags to nodemon, it's much more convenient to add a script in package.json that runs nodemon with all the flags I need (as in the example in Alternative 3). That also makes it more convenient when working with multiple projects; you can just call npm run dev and not have to remember how each project needs to be configured. Of course you're more than welcome to install it globally if that suits your fancy.
What do you mean by "type checking should still be part of your build process". And how would I include type checking in my build process?
@thegoodhunter-9115 The tools recommended by the answers on this page are for local development. But for production, it's generally recommended to transpile the TypeScript to JavaScript, which is the "build" step. If you're using the TypeScript compiler (tsc), it does type checking by default (actually I don't think there's any way to turn it off). Since you might not want to wait until build time to find out if there are any type errors, you could also use tsc to do type checking as part of your testing process or as a pre-push hook.
tsx doesn't support emitDecoratorMetadata for now
|
78

Here's an alternative to the HeberLZ's answer, using npm scripts.

My package.json:

  "scripts": {
    "watch": "nodemon -e ts -w ./src -x npm run watch:serve",
    "watch:serve": "ts-node --inspect src/index.ts"
  },
  • -e flag sets the extenstions to look for,
  • -w sets the watched directory,
  • -x executes the script.

--inspect in the watch:serve script is actually a node.js flag, it just enables debugging protocol.

7 Comments

Also be sure to have typescript locally installed for the project. Otherwise the error you might get is not very clear.
I think it should be ts-node --inspect -- src/index.ts now due to this.
This approach seems to generate considerable superfluous output.
-e ts -w ./src did the trick for me - this worked with a loopback4 CLI generated project
@Timo looks like starting from v5.0.0 for advanced node.js flags (like --inspect in this case) you need to run ts-node as node -r ts-node/register command.
|
59

This works for me:

nodemon src/index.ts

Apparently thanks to since this pull request: https://github.com/remy/nodemon/pull/1552

4 Comments

This works for me too but how? Seems kind of magical. What's compiling the typescript? I don't have ts-node installed.
@d512 Are you sure it's not in your node_modules/? For me it fails if I don't have it.
This indeed does require ts-node to be installed. Running this command without ts-node will result in an failed to start process, "ts-node" exec not found error. You likely had this as a leftover artifact in node_modules. That being said, this solution is much nicer since it doesn't require additional config.
Install ts-node globally: npm install -g ts-node
21

you could use ts-node-dev

It restarts target node process when any of required files changes (as standard node-dev) but shares Typescript compilation process between restarts.

Install

yarn add ts-node-dev --dev

and your package.json could be like this

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "tsc": "tsc",
  "dev": "ts-node-dev --respawn --transpileOnly ./src/index.ts",
  "prod": "tsc && node ./build/index.js"
}

1 Comment

Thank you! This was the easiest way I found to enable auto reload with my node server.
20

Specifically for this issue I've created the tsc-watch library. you can find it on npm.

Obvious use case would be:

tsc-watch server.ts --outDir ./dist --onSuccess "node ./dist/server.js"

4 Comments

How would this work in the case of an express or koa server since it doesn't actually kill the previous node instance?
'tsc-watch' kills and restarts the process for you.
This is exactly what I was looking for. Not sure what the purpose of ts-node-dev is, but I couldn't get it to report typescript errors. After spending hours trying to get it working, I tried tsc-watch, and it worked like a charm!
@gilamran in the documentation of your package there is a typo: "[...] similar to nodemon but for TypeCcript.":)
14

Add "watch": "nodemon --exec ts-node -- ./src/index.ts" to scripts section of your package.json.

Comments

12

Standard Approach

Starting from Node.js version v16.19.0 (Dec'22), the CLI API introduces the --watch option, eliminating the need for additional dependencies like nodemon.

For TypeScript execution, use well-established package ts-node. Here's configuration example:

// package.json
{
    "scripts": {
        "dev": "node --watch --loader=ts-node/esm ./src/app.ts"
    },
    "devDependencies": {
        "ts-node": "~10.9.0"
    }
}

Non-Standard Approach

Alternatively, you might consider tsx, although it's not as mature as ts-node.

Keep in mind that tsx currently lacks support for emitDecoratorMetadata a crucial feature for many projects that need decorators. Despite this drawback, tsx offers faster compilation (esbuild backend):

// package.json
{
    "scripts": {
        "dev": "tsx watch ./src/app.ts"
    },
    "devDependencies": {
        "tsx": "~4.6.0"
    }
}

3 Comments

Lots of errors with this for example Caused by: unknown field `noInterop`, expected `resolveFully` at line 1 column 433
Please give additional context. What specifically do you mean by "Lots of errors", share your project setup, tooling you are transitioning from, etc....
@OliverDixon This is because ts-node doesn't properly support the latest version of swc/core. you need to override the version of swc/core to 1.3.82 (at the time of writing). in package.json add "resolutions": { "@swc/core": "1.3.82" }, "overrides": { "@swc/core": "1.3.82" } (DO NOT copy versions used in this github issue link, use 1.3.82) github.com/TypeStrong/ts-node/issues/…
11

I would prefer to not use ts-node and always run from dist folder.

To do that, just setup your package.json with default config:

....
"main": "dist/server.js",
"scripts": {
  "build": "tsc",
  "prestart": "npm run build",
  "start": "node .",
  "dev": "nodemon"
},
....

and then add nodemon.json config file:

{
  "watch": ["src"],
  "ext": "ts",
  "ignore": ["src/**/*.spec.ts"],
  "exec": "npm restart"
}

Here, i use "exec": "npm restart"
So all ts file will re-compile to js file and then restart the server.

To run while in dev environment,

npm run dev

Using this setup I will always run from the distributed files and no need for ts-node.

1 Comment

Like walking on glass. But an alternative, non-the-less
9

i did with

"start": "nodemon --watch 'src/**/*.ts' --ignore 'src/**/*.spec.ts' --exec ts-node src/index.ts"

and yarn start.. ts-node not like 'ts-node'

Comments

7

Another way could be to compile the code first in watch mode with tsc -w and then use nodemon over javascript. This method is similar in speed to ts-node-dev and has the advantage of being more production-like.

 "scripts": {
    "watch": "tsc -w",
    "dev": "nodemon dist/index.js"
  },

2 Comments

Or just "dev": "( tsc -w & ) && nodemon dist/index.js".
This was a great answer for me. Simple and easy, thanks.
6

The first step - Install the below packages in deDependencies

npm i -D @types/express @types/node nodemon ts-node tsc typescript

or using yarn

yarn add -D @types/express @types/node nodemon ts-node tsc typescript

The second step - using this configuration in your tsconfig.json file

{
  "compilerOptions": {
    "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */,
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    "lib": [
      "DOM",
      "ES2017"
    ] /* Specify library files to be included in the compilation. */,
    "sourceMap": true /* Generates corresponding '.map' file. */,
    "outDir": "./dist" /* Redirect output structure to the directory. */,
    "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,

    "strict": true /* Enable all strict type-checking options. */,
    "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
    "skipLibCheck": true /* Skip type checking of declaration files. */,
    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
  },
  "exclude": ["node_modules"],
  "include": ["./src"]
}

The third step - using these scripts in your package.json file

"scripts": {
    "start": "node ./dist/server.js",
    "dev": "nodemon -L ./src/server.ts && tsc -w"
},

Comments

5

add this to your package.json file

scripts {
"dev": "nodemon --watch '**/*.ts' --exec 'ts-node' index.ts"
}

and to make this work you also need to install ts-node as dev-dependency

yarn add ts-node -D

run yarn dev to start the dev server

Comments

3

STEP 1: You can simple install nodemon and ts-node (skip if you already done)

npm install --save-dev nodemon ts-node

STEP 2: You can configure the start script in package.json

"start": "nodemon ./src/app.ts"

As now nodemon automatically identify the typescript from the project now and use ts-node command by itself. Use npm start and it will automatically compile/watch and reload.

If you get any errors like typescript module not found in the project. simple use this command in the project folder.

npm link typescript

Comments

3

I got this error : code: 'ERR_UNKNOWN_FILE_EXTENSION' i needed the esm support, so if you want ES6 support you can try bellow commands

npm i -D ts-node nodemon

in package.json add below script :

"dev": "nodemon --exec ts-node-esm ./src/index.ts"

1 Comment

I was able to run it with "dev": "nodemon --exec 'node --no-warnings=ExperimentalWarning --loader ts-node/esm src/index.ts'"
1

Just update these 3 packages

nodemon, ts-node, typescript
yarn global add nodemon ts-node typescript

or

npm install -g nodemon ts-node typescript

and now you can run this, problem solved

nodemon <filename>.ts

1 Comment

Please add your comments or instructions outside of your code. That will be more understandable and readable.
1

Clear logs of the console after changing

Javascript:

"start": "nodemon -x \"cls && node\" index.js",

Typescript:

"start": "nodemon -x \"cls && ts-node\" index.ts",

Comments

1

Probably the easiest way is to install Nodemon and run: "nodemon --exec npx ts-node --esm test.ts"

Comments

0

If you are having issues when using "type": "module" in package.json (described in https://github.com/TypeStrong/ts-node/issues/1007) use the following config:

{
  "watch": ["src"],
  "ext": "ts,json",
  "ignore": ["src/**/*.spec.ts"],
  "exec": "node --loader ts-node/esm --experimental-specifier-resolution ./src/index.ts"
}

or in the command line

nodemon --watch "src/**" --ext "ts,json" --ignore "src/**/*.spec.ts" --exec "node --loader ts-node/esm --experimental-specifier-resolution src/index.ts"

Comments

0

add --poll options on your package.json

The --poll option in ts-node-dev tells it to check the source code files for changes by periodically looking at them, instead of waiting for the file system to report changes. This ensures that changes to the files are always detected, even on file systems that don't report changes reliably.

   "start": "ts-node-dev --poll src/index.ts"

Comments

0

There's also another option if you'd like to forego just running both tsc and the payload in parallel, and want them to be more coupled together without needing to switch to a complicated tsc wrapper or alternative. You could also write a Node.js script to wait for the TypeScript compiler to complete building and then start your process when the build is complete.

This is what I wrote for one of my projects (this needs the tree-kill package installed):

#!/usr/bin/env node

import { spawn } from 'node:child_process';
import { join } from 'node:path';
import { createInterface } from 'node:readline';
import process from 'node:process';
import kill from 'tree-kill';

const cwd = process.cwd();
const tscConfigPath = join(cwd, 'tsconfig.build.json');
const tscArgs = ['tsc', '--build', '--watch', '--pretty', tscConfigPath];
const command = process.argv.slice(2);

// adjust params above as necessary

const tsc = spawn('yarn', tscArgs, { // change if not using yarn
  stdio: ['ignore', 'pipe', 'inherit'],
  cwd,
});

let proc;

const startCommand = () => {
  proc && kill(proc.pid);
  proc = spawn(command[0], command.slice(1), { stdio: 'inherit', cwd });
};

const rl = createInterface({
  input: tsc.stdout,
  terminal: false,
});

rl.on('line', (line) => {
  console.log(line);
  if (line.includes('Found 0 errors. Watching for file changes.')) {
    startCommand();
  }
});

tsc.on('exit', (code) => {
  console.log('tsc exited with code', code);
  proc && kill(proc.pid);
  process.exit(code);
});

process.on('SIGINT', () => {
  kill(tsc.pid);
  proc && kill(proc.pid);
  process.exit(0);
});

process.on('exit', () => {
  kill(tsc.pid);
  proc && kill(proc.pid);
});


I use it in other packages in my monorepo like this in the package.json manifest:

"scripts": {
  "start:dev": "run-on-tsc-build yarn node dist/main.js"
}

It works by waiting to see the "Found 0 errors. Watching for file changes." message from Typescript and then running the given command.

2 Comments

How is that 'watch' as the questioner requested?
tsc is invoked in watch mode and the script detects when it has rebuilt the application and then reloads the app. it's slightly different semantics but is an alternative way to accomplish the same effect of reloading the app when it is rebuilt.
0

With ts-node-dev version 2.0.0+ (May'22) you can use:

ts-node-dev --respawn ./src/index.ts

*Note: If you get "command not found" error, then you can run it directly from node-modules like so:

./node_modules/.bin/ts-node-dev --respawn ./src/index.ts

Comments

0

In 2024, ts-node with nodemon is consistently 3 times faster than tsx in a large project.

I wanted tsx to be faster since it's so much simpler, but it unfortunately is not. I had to change to ts-node after a couple of weeks of wasted time waiting half a minute after every code change for tsx to recompile my code.

I ran the following two scripts:

  1. "dev-server-tsx": "date && NODE_ENV=development ENV_IS_DEV=true FORCE_COLOR=1 tsx watch --clear-screen=false ./src/serverInstance.ts",
  2. "dev-server": "date && NODE_ENV=development ENV_IS_DEV=true FORCE_COLOR=1 nodemon ./src/serverInstance.ts",

with these packages:

"nodemon": "^3.1.7",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tslib": "^2.8.0",
"tsx": "^4.19.1",

Results:

  • tsx consistently takes between 32-35 seconds to start and restart
  • 🏆 nodemon (with ts-node) consistently took around 11-12 seconds to start, and only a few seconds for restarts.

The amount of time tsx takes for restarting on code changes makes it impossible to be productive when debugging, wherein you need to make frequent changes to figure out the root cause of an issue.

If you'd like to use ts-node with nodemon, you can learn more about how this works from this Github pull request on the Nodemon project: https://github.com/remy/nodemon/pull/1552

Comments

-1

With nodemon and ts-node:

nodemon --watch source --ext ts,json --exec "node --loader ts-node/esm ./source/index.ts"

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.