Configuração Inicial do Ambiente
Antes de iniciar qualquer projeto com TypeScript e Node.js, você precisa configurar o ambiente corretamente. Comece instalando Node.js (versão 16+) e criando um novo diretório para seu projeto. Execute npm init -y e instale as dependências essenciais: TypeScript, Express e tipos do Node.
npm install express cors dotenv
npm install -D typescript ts-node @types/express @types/node ts-node-dev
npx tsc --init
O arquivo tsconfig.json gerado define como o TypeScript será compilado. Ajuste as configurações para ambiente Node.js modificando target para "ES2020" e module para "commonjs". Em compilerOptions, adicione "strict": true para ativar verificações rigorosas de tipo — essencial para APIs robustas.
Estrutura de Projeto e Tipagem
Uma API bem organizada segue uma arquitetura clara com separação de responsabilidades. Crie a seguinte estrutura:
src/
├── controllers/
├── routes/
├── models/
├── middleware/
└── index.ts
Comece definindo seus tipos e interfaces. No arquivo src/models/User.ts, declare a estrutura esperada:
export interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
export type CreateUserDTO = Omit<User, 'id' | 'createdAt'>;
Essa abordagem garante que toda comunicação de dados seja validada pelo TypeScript em tempo de desenvolvimento, evitando bugs em produção.
Criando sua Primeira API REST Tipada
Agora vamos construir uma API funcional do zero. No arquivo src/index.ts:
import express, { Express, Request, Response } from 'express';
import cors from 'cors';
const app: Express = express();
const PORT = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
// Simulando um banco de dados em memória
const users: User[] = [];
let nextId = 1;
app.get('/users', (req: Request, res: Response): void => {
res.json(users);
});
app.post('/users', (req: Request, res: Response): void => {
const { name, email }: CreateUserDTO = req.body;
if (!name || !email) {
res.status(400).json({ error: 'Name and email are required' });
return;
}
const newUser: User = {
id: nextId++,
name,
email,
createdAt: new Date()
};
users.push(newUser);
res.status(201).json(newUser);
});
app.get('/users/:id', (req: Request, res: Response): void => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
res.status(404).json({ error: 'User not found' });
return;
}
res.json(user);
});
app.listen(PORT, () => {
console.log(`Servidor rodando em http://localhost:${PORT}`);
});
Observe como cada rota possui tipos explícitos para Request e Response. Isso oferece autocompletar inteligente e previne erros de tipagem.
Validação e Middleware Tipado
Validação é crucial para APIs seguras. Crie um middleware tipado em src/middleware/validateUser.ts:
import { Request, Response, NextFunction } from 'express';
import { CreateUserDTO } from '../models/User';
export const validateUserData = (
req: Request,
res: Response,
next: NextFunction
): void => {
const { name, email } = req.body;
if (typeof name !== 'string' || name.trim().length === 0) {
res.status(400).json({ error: 'Invalid name' });
return;
}
if (typeof email !== 'string' || !email.includes('@')) {
res.status(400).json({ error: 'Invalid email' });
return;
}
next();
};
Agora aplique o middleware na rota POST:
app.post('/users', validateUserData, (req: Request, res: Response): void => {
// seu código aqui
});
Essa separação mantém o código limpo e reutilizável. TypeScript garante que você não esqueça de chamar next() ou que retorne valores inesperados.
Tratamento de Erros e Tipos Avançados
Para APIs profissionais, implemente um manipulador de erros centralizado. Crie src/middleware/errorHandler.ts:
import { Request, Response, NextFunction } from 'express';
export class ApiError extends Error {
constructor(
public statusCode: number,
message: string
) {
super(message);
this.name = 'ApiError';
}
}
export const errorHandler = (
err: Error,
req: Request,
res: Response,
next: NextFunction
): void => {
if (err instanceof ApiError) {
res.status(err.statusCode).json({ error: err.message });
return;
}
res.status(500).json({ error: 'Internal server error' });
};
Use em seu index.ts:
app.use(errorHandler);
Tipos genéricos também são poderosos. Para respostas padronizadas:
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
}
const sendResponse = <T>(res: Response, statusCode: number, data: T): void => {
res.status(statusCode).json({ success: true, data });
};
Conclusão
Você aprendeu que TypeScript transforma Node.js em uma plataforma robusta para APIs. Os três pontos principais são: (1) Tipagem explícita previne bugs antes do deploy, garantindo segurança em tempo de desenvolvimento; (2) Estrutura organizada com separação de responsabilidades (controllers, models, middleware) escala bem conforme seu projeto cresce; (3) Validação e tratamento de erros centralizados mantém o código consistente e profissional. Pratique esses conceitos construindo pequenas APIs e, progressivamente, explore bibliotecas como Zod para validação mais robusta e bancos de dados reais. O investimento inicial em tipagem rigorosa economiza horas de debugging futuramente.