API Node.js + Express (Parte 1)
Criação de uma API Node.js + Express
Este artigo foi criado como uma anotação do projeto desenvolvido no curso da Formação Node.js da DIO.
O repositório completo do projeto pode ser encontrado aqui no GitHub.
Criar uma API é uma tarefa essencial para qualquer desenvolvedor backend. Neste artigo, veremos como criar uma API utilizando Node.js e Express. Vamos construir uma API para gerenciar dados de jogadores de futebol na "Champions League".
Configuração Inicial
Primeiramente, precisamos configurar nosso ambiente de desenvolvimento. Vamos iniciar criando um projeto Node.js.
1. Inicializando o Projeto
Primeiramente, vamos criar uma nova pasta para o projeto e inicializá-lo com o comando npm init -y
. Isso criará um arquivo package.json
com as configurações básicas do projeto.
mkdir api-champions-league
cd api-champions-league
npm init -y
2. Instalando Dependências
Em seguida, precisamos instalar as dependências necessárias para o projeto. Vamos usar o Express para criar a API, o Dotenv para gerenciar variáveis de ambiente e o Cors para habilitar o compartilhamento de recursos entre diferentes origens. Além disso, instalaremos TypeScript e algumas ferramentas de desenvolvimento.
npm install express dotenv cors
npm install --save-dev typescript @types/node @types/express @types/cors tsup tsx
3. Configurando TypeScript
Vamos inicializar o TypeScript no projeto. Isso criará um arquivo tsconfig.json
com a configuração padrão.
npx tsc --init
Edite o arquivo tsconfig.json
para incluir as configurações recomendadas para projetos Node.js:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
}
}
4. Atualizando o package.json
Adicione os scripts de build e execução ao package.json
:
{
"scripts": {
"dist": "tsup src",
"start:dev": "tsx src/server.ts",
"start:watch": "tsx watch src/server.ts",
"start:dist": "npm run dist && node dist/src/index.js"
},
}
Com essas configurações, estamos prontos para começar a desenvolver nossa API. Nos próximos passos, criaremos os controladores, serviços, repositórios e demais arquivos necessários para a funcionalidade completa da aplicação.
5. Criando o .env
Precisamos criar também um arquivo .env
na raiz do projeto, onde vamos especificar a porta que será utilizada pela aplicação
PORT=3333
6. Estrutura de Diretórios
Vamos organizar nossa aplicação em várias camadas para manter o código modular e fácil de manter. Aqui está a estrutura de diretórios:
| src/
├── controllers/
│ ├── playersController.ts
├── models/
│ ├── playerModel.ts
│ └── httpResponseModel.ts
├── repositories/
│ └── playersRepository.ts
├── services/
│ └── playersService.ts
├── utils/
│ └── httpHelper.ts
├── routes.ts
├── app.ts
└── server.ts
| .env
| package.json
| tsconfig.json
Configurando o Servidor
server.ts
O arquivo server.ts
é o ponto de entrada da nossa aplicação. Ele configura e inicia o servidor:
import dotenv from 'dotenv'
import createApp from './app'
dotenv.config()
const app = createApp()
const port = process.env.PORT
app.listen(port, () => {
console.log(`🔥 Server running at port http://localhost:${port}`)
})
app.ts
O arquivo app.ts
cria e configura a instância do Express:
import express, { Request, Response } from 'express';
import router from './routes';
function createApp() {
const app = express();
app.use(express.json());
app.use("/api/v1", router);
return app;
}
export default createApp;
routes.ts
Este arquivo define as rotas da aplicação:
import { Router } from "express"
import * as PlayerController from "./controllers/playersController"
const router = Router()
router.get('/players', PlayerController.getPlayer)
router.post('/players', PlayerController.createPlayer)
router.get('/players/:id', PlayerController.getPlayerById)
router.delete('/players/:id', PlayerController.deletePlayer)
router.patch('/players/:id', PlayerController.updatePlayer)
export default router
Controllers
Os controladores lidam com as requisições HTTP e chamam os serviços apropriados.
playersController.ts
import { Request, Response } from "express"
import * as service from "../services/playersService"
import { StatisticsModel } from "../models/statisticsModel";
export const getPlayer = async (req: Request, res: Response) => {
const httpResponse = await service.getPlayerService()
res.status(httpResponse.statusCode).json(httpResponse.body)
}
export const getPlayerById = async (req: Request, res: Response) => {
const id = parseInt(req.params.id)
const httpResponse = await service.getPlayerByIdService(id)
res.status(httpResponse.statusCode).json(httpResponse.body)
}
export const createPlayer = async (req: Request, res: Response) => {
const bodyValue = req.body
const httpResponse = await service.createPlayerService(bodyValue)
res.status(httpResponse.statusCode).json(httpResponse.body)
}
export const deletePlayer = async (req: Request, res: Response) => {
const id = parseInt(req.params.id)
const httpResponse = await service.deletePlayerService(id)
res.status(httpResponse.statusCode).json(httpResponse.body)
}
export const updatePlayer = async (req: Request, res: Response) => {
const id = parseInt(req.params.id)
const bodyValue: StatisticsModel = req.body
const httpResponse = await service.updatePlayerService(id, bodyValue)
res.status(httpResponse.statusCode).json(httpResponse.body)
}
Services
Os serviços contêm a lógica de negócios da aplicação.
playersService.ts
import { PlayerModel } from "../models/playerModel"
import { StatisticsModel } from "../models/statisticsModel"
import * as PlayerRepository from "../repositories/playersRepository"
import * as HttpResponse from "../utils/httpHelper"
export const getPlayerService = async () => {
const data = await PlayerRepository.findAllPlayers()
let response = null
if (data) {
response = await HttpResponse.ok(data)
} else {
response = await HttpResponse.noContent()
}
return response
}
export const getPlayerByIdService = async (id: number) => {
const data = await PlayerRepository.findPlayersById(id)
let response = null
if (data) {
response = await HttpResponse.ok(data)
} else {
response = await HttpResponse.noContent()
}
return response
}
export const createPlayerService = async (player: PlayerModel) => {
let response = null
if (Object.keys(player).length !== 0) {
await PlayerRepository.insertPlayer(player)
response = await HttpResponse.created()
} else {
response = await HttpResponse.badRequest()
}
return response
}
export const deletePlayerService = async (id: number) => {
let response = null
const isDeleted = await PlayerRepository.deleteOnePlayer(id)
if (isDeleted) {
response = await HttpResponse.ok({ message: "deleted" })
} else {
response = await HttpResponse.badRequest()
}
return response
}
export const updatePlayerService = async (id: number, statistics: StatisticsModel) => {
const data = await PlayerRepository.findAndModifyPlayer(id, statistics)
let response = null
if (Object.keys(data).length === 0) {
response = await HttpResponse.badRequest();
} else {
response = await HttpResponse.ok(data);
}
return response
}
Repositories
Os repositórios lidam com a interação com o banco de dados ou outras formas de armazenamento de dados.
playersRepository.ts
import { PlayerModel } from "../models/playerModel"
import { StatisticsModel } from "../models/statisticsModel";
const db: PlayerModel[] = [
// Dados fictícios de jogadores
{
id: 1,
name: "Lionel Messi",
club: "Inter Miami",
nationality: "Argentina",
position: "Forward",
statistics: {
Overall: 93,
Pace: 85,
Shooting: 94,
Passing: 91,
Dribbling: 95,
Defending: 38,
Physical: 65,
},
},
];
export const findAllPlayers = async (): Promise<PlayerModel[]> => {
return db
}
export const findPlayersById = async (id: number): Promise<PlayerModel | undefined> => {
return db.find(player => player.id === id)
}
export const insertPlayer = async (player: PlayerModel) => {
db.push(player)
}
export const deleteOnePlayer = async (id: number) => {
const index = db.findIndex(player => player.id === id)
if (index !== -1) {
db.splice(index, 1)
return true
}
return false
}
export const findAndModifyPlayer = async (id: number, statistics: StatisticsModel): Promise<PlayerModel> => {
const playerIndex = db.findIndex(player => player.id === id)
if (playerIndex !== -1) {
db[playerIndex].statistics = statistics
}
return db[playerIndex]
}
Utils
httpHelper.ts
Utilitários para criar respostas HTTP.
import { HttpResponse } from "../models/httpResponseModel"
export const ok = async (data: any): Promise<HttpResponse> => {
return {
statusCode: 200,
body: data,
}
}
export const created = async (): Promise<HttpResponse> => {
return {
statusCode: 201,
body: {
message: "successful"
},
}
}
export const noContent = async (): Promise<HttpResponse> => {
return {
statusCode: 204,
body: null,
}
}
export const badRequest = async (): Promise<HttpResponse> => {
return {
statusCode: 400,
body: null,
}
}
Conclusão
Criar uma API com Node.js e Express pode ser simplificado através de uma boa organização de código e modularização. Este exemplo mostra como estruturar uma aplicação completa, desde a configuração inicial até a implementação das rotas, controladores, serviços e repositórios. Isso facilita a manutenção e a escalabilidade da aplicação.
Além disso, está em desenvolvimento a geração da documentação da API utilizando o Swagger UI. Este é um aprimoramento pessoal e não faz parte do conteúdo do curso.
A parte 2 com a implementação do Swagger UI já está disponível aqui.