24 luglio 2026

Express con TypeScript: setup pulito, typing concreto e workflow senza attriti

Dalla configurazione del progetto al primo endpoint tipizzato, con build e start script pronti per crescere.

Integrare TypeScript in un server Express non è solo “aggiungere tipi”: significa rendere più prevedibili request/response, ridurre bug banali e preparare il codice a scalare con una struttura più solida. In questo articolo vediamo un setup essenziale (ma serio) per Node + Express + TypeScript, come organizzare src/dist, come far partire il server compilando in modo consistente e come tipizzare i primi dati e handler senza complicarsi la vita.

TypeScript e Express stanno bene insieme per un motivo semplice: Express ti fa andare veloce, TypeScript ti evita di pagare quella velocità in bug stupidi e refactor dolorosi. Se l’obiettivo è costruire API manutenibili (anche piccole), vale la pena impostare da subito una base pulita.

Qui sotto trovi un percorso pratico: setup del progetto, tsconfig, primo server, compilazione e un workflow tramite script npm. Il risultato è una mini-API pronta a crescere in rotte, controller e middleware tipizzati.


1) Dipendenze: Express “runtime”, TypeScript “dev-time”

Parti come un normale progetto Node:

npm init -y

Installa Express come dipendenza runtime:

npm install express

Poi aggiungi TypeScript e i tipi di Express come devDependencies (Express è JavaScript puro: senza @types TypeScript non conosce Request, Response, ecc.):

npm install -D typescript @types/express

Un tsconfig sensato senza reinventare la ruota

Scrivere un tsconfig.json “perfetto” a mano è una perdita di tempo. Molto meglio estendere una base già collaudata per la tua versione di Node.

Ad esempio, per Node 20:

npm install -D @tsconfig/node20

Crea tsconfig.json e imposta estensione + due directory chiave:

{
  "extends": "@tsconfig/node20/tsconfig.json",
  "compilerOptions": {
    "rootDir": "source",
    "outDir": "dist"
  }
}
  • rootDir: dove vivi con i file TypeScript (qui: source/).
  • outDir: dove finiscono i JavaScript compilati (qui: dist/).

Questo pattern è semplice ma importantissimo: separa sorgente e build output, evitando confusione e import strani.


2) Struttura minima del progetto

Crea la cartella e l’entry point:

source/
  index.ts
tsconfig.json
package.json

Dentro source/index.ts, lo scheletro base di Express:

import express from "express";

const app = express();
const port = 8000;

app.listen(port, () => {
  console.log(`listening on port ${port}`);
});

Un pizzico di typing (senza forzature)

Molto è già inferito da TypeScript, ma esercitarsi a pensare per tipi aiuta. Puoi tipizzare esplicitamente l’app e il return type della callback:

import express, { type Express } from "express";

const app: Express = express();
const port = 8000;

app.listen(port, (): void => {
  console.log(`listening on port ${port}`);
});

Non è obbligatorio. È allenamento: quando arriverai a tipizzare middleware, handler, query param e payload, questa disciplina torna utilissima.


3) Compilare e avviare: Node non esegue TypeScript

Node non capisce .ts nativamente: devi compilare.

Compila con il compiler TypeScript:

npx tsc

Otterrai il build in dist/ (ad esempio dist/index.js). Poi avvii:

node dist/index.js

A questo punto il server gira, ma se non hai rotte otterrai un classico 404 su /.


4) Prima rotta: risposta JSON

Aggiungi una rotta GET / che risponde con un JSON (anche vuoto, giusto per testare la pipeline):

app.get("/", (req, res) => {
  res.json({});
});

Poi:

npx tsc
node dist/index.js

5) Modellare dati reali: tipizzare un “pet” (o qualsiasi dominio)

Restituire {} serve solo per verificare che tutto funzioni. Il passo successivo è introdurre un piccolo modello dati e tipizzarlo.

Esempio con un tipo Pet:

type Pet = {
  name: string;
  species: string;
  adopted: boolean;
  age: number;
};

const pets: Pet[] = [
  { name: "Rubik", species: "cat", adopted: true, age: 3 },
  { name: "Pickle", species: "dog", adopted: false, age: 5 }
];

app.get("/", (req, res) => {
  res.json(pets);
});

Qui TypeScript ti protegge da:

  • proprietà mancanti o con tipo sbagliato (age: "3" → errore),
  • dati incoerenti tra i vari oggetti,
  • refactor fragili quando cambi il modello.

6) Workflow migliore: script npm per build + run

Digitare ogni volta npx tsc e poi node dist/index.js diventa rapidamente noioso. Gli script npm sistemano la routine e la rendono standard.

In package.json:

{
  "scripts": {
    "build": "npx tsc",
    "start": "npx tsc && node dist/index.js"
  }
}

Ora puoi fare:

npm run start
  • prima compila
  • poi avvia l’output compilato

È una base semplice, ma già “production-shaped”: separa compilation e runtime, ed evita di eseguire TypeScript direttamente in ambienti dove non vuoi dipendenze extra.


Sintesi e implicazione pratica

Un setup Express + TypeScript ben fatto non richiede un framework aggiuntivo: bastano dipendenze corrette, un tsconfig sensato e una convenzione chiara (source/dist/). Da lì, ogni miglioramento (rotte tipizzate, controller, middleware, validazione input, gestione errori) poggia su fondamenta solide.

Se oggi stai iniziando un’API Express, il consiglio pratico è: imposta subito rootDir/outDir, aggiungi @types/express, crea gli script build/start e inizia a modellare il dominio con tipi espliciti. Il tempo risparmiato in debug e refactor ripaga molto più di quanto costi la configurazione iniziale.