Host a TypeScript Discord bot

The Node.js application can't run a .ts file directly, so this bot compiles itself on every start — here's the recipe, why it works, and what to edit.

Skip the setup This guide has a one-click starter template that installs everything below onto your server.

TypeScript on the Node.js application has one gotcha worth understanding before you start, and it's better to hear it up front than to fight it. This guide gives you a working typed discord.js bot and explains the trick that makes it run.

At a glance
You need comfort with the discord.js bot guide, a bot token from Your first Discord bot, and a Falix server running the Node.js application
Time about twenty minutes

The bot logic is identical to the discord.js guide — only the language and build step differ.

The trap, honestly

The Node.js application starts your app by running your Main file. If that file ends in .js it runs node yourfile.js; anything else falls back to a ts-node path — and that path fails on current Node versions with ERR_UNKNOWN_FILE_EXTENSION.

⚠️ Heads up: Pointing the Main file straight at a .ts file simply does not work here.

The fix isn't to run TypeScript directly — it's to compile TypeScript to JavaScript first, then run the JavaScript. This template does exactly that, automatically, and it's a pattern that works for any TypeScript project on the Node app, not just bots.

How the self-compiling trick works

Remember that the Node.js application runs npm install on every start whenever a package.json is present. This template hooks into that:

  1. Its package.json defines a postinstall script that runs tsc (the TypeScript compiler), and lists typescript as a dev dependency.
  2. Its tsconfig.json sets rootDir: src and outDir: dist with module: commonjs — read source from src/, write compiled .js into dist/.
  3. The template has already set the server's Main file variable to dist/index.js.

So every start goes: npm install runs → postinstall fires → tsc compiles src/ into fresh JavaScript in dist/ → Node runs dist/index.js. You never build by hand; starting the server is the build.

1. Deploy the starter

Use the template on this page. Besides the files (src/index.ts, package.json, tsconfig.json, .env), it sets the Main file to dist/index.js for you. Deploying onto a server running a different application switches it to Node.js first — a reinstall that wipes files, with a warning.

2. Paste in your token

In the File Manager, edit .env and replace YOUR_TOKEN_HERE with your token. Save.

3. Start it

Press Start. The console runs npm install, then you'll see tsc compile, and finally:

Listening as YourBot#0000

As with any Node bot, that Listening line is also what flips the server to online. Type /ping in your server to confirm (give global commands a few minutes on the very first run).

What to edit — and what never to touch

Edit src/index.ts. That's your bot. The logic is the same discord.js you'd write in plain JavaScript, just typed:

import 'dotenv/config';
import { Client, Events, GatewayIntentBits } from 'discord.js';

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.once(Events.ClientReady, async (c) => {
  await c.application.commands.set([
    { name: 'ping', description: 'Check that the bot is alive' },
  ]);
  console.log(`Listening as ${c.user.tag}`);
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;
  if (interaction.commandName === 'ping') {
    await interaction.reply(`Pong! ${client.ws.ping} ms`);
  }
});

client.login(process.env.DISCORD_TOKEN);

Adding commands works exactly as in the discord.js guide: register in the set([...]) array, handle in InteractionCreate, then restart so it recompiles.

⚠️ Heads up: Never edit dist/. Everything in there is generated by tsc and overwritten on the next start — your changes would vanish (dist/ is even in .gitignore for that reason). The rule: src/ is yours, dist/ belongs to the compiler.

To add libraries, use the Packages page as usual; it updates package.json and a restart recompiles and reloads.

The simpler alternative: Bun

If the build step feels like overhead, the Bun application runs TypeScript files natively — no tsc, no dist/, no postinstall. You'd point the Main file straight at src/index.ts and it just runs. If you're starting fresh and don't need Node specifically, it's the lighter path. See Bun on Falix.

Troubleshooting

  • error TS... during install — a compile error, printed while postinstall runs. Don't be fooled if the bot still comes up: it may be running the previous successful build from dist/. Fix the code in src/ and restart until the install is clean.
  • Edits seem to do nothing — you edited dist/ instead of src/. dist/ is regenerated on every start. Make the change in src/ and restart.
  • TokenInvalid — same as any bot: the token in .env is wrong or was reset. Paste a fresh one from the Developer Portal.
  • /ping doesn't appear — first-time global registration is slow; wait a few minutes, and confirm the bot was invited with the applications.commands scope.

Next steps

Was this guide helpful?