Javascript

Revisão + Projeto: API tipada e testada Já leu

13 min de leitura

Revisão + Projeto: API tipada e testada
Chegamos ao fim do Módulo 5. Quatro artigos densos — testes com Jest, padronização com ESLint e Prettier, fluxo profissional com Git e tipagem estática com TypeScript. Agora vamos unir tudo isso em um único projeto que d

Chegamos ao fim do Módulo 5. Quatro artigos densos — testes com Jest, padronização com ESLint e Prettier, fluxo profissional com Git e tipagem estática com TypeScript. Agora vamos unir tudo isso em um único projeto que demonstra como esses pilares de qualidade trabalham juntos.

O projeto é uma API de gerenciamento de produtos construída com TypeScript, Express, MongoDB, completamente testada e com pipeline de qualidade automático.


Revisão rápida — Módulo 5


Jest

// Anatomia de um teste
describe("nomeDaFunção()", () => {
  beforeEach(() => jest.clearAllMocks());

  it("descrição do comportamento esperado", async () => {
    // Arrange
    const entrada = criarDadosDeTeste();

    // Act
    const resultado = await funcaoTestada(entrada);

    // Assert
    expect(resultado).toMatchObject({ sucesso: true });
    expect(mockDependencia).toHaveBeenCalledTimes(1);
  });
});

// Tipos: unitários, integração (Supertest), E2E
// Mocks: jest.fn(), jest.mock(), mockResolvedValue
// Coverage: npm run test:coverage

ESLint + Prettier

# ESLint — encontra problemas
npx eslint src/ --fix

# Prettier — formata consistentemente
npx prettier --write src/

# Juntos no package.json
# "quality": "npm run format:check && npm run lint"

# Husky + lint-staged — valida no commit
# commitlint — valida a mensagem do commit

Git semântico

# feat(scope): descrição curta
# fix | docs | refactor | test | chore | perf | ci

git switch -c feature/nome-descritivo
git commit -m "feat(produtos): adiciona endpoint de busca por categoria"
git rebase origin/main  # histórico linear antes do PR
git push origin feature/nome-descritivo
# → Pull Request → Revisão → Merge → Delete branch

TypeScript

// Interfaces e types
interface IProduto extends Document { nome: string; preco: number; }
type ProdutoPublico = Omit<IProduto, "senha">;
type RespostaAPI<T> = { dados: T; sucesso: boolean; };

// Generics
function buscar<T>(id: string): Promise<T | null> { ... }

// Utility Types
Partial<T>  Omit<T, K>  Pick<T, K>  Record<K, V>  NonNullable<T>

// Express tipado
async function handler(
  req: Request<Params, {}, Body, Query>,
  res: Response
): Promise<void> { ... }

O projeto — API de Produtos com qualidade completa

Uma API REST para gerenciamento de catálogo de produtos com:

  • TypeScript em todo o projeto
  • Testes unitários e de integração com Jest + Supertest
  • ESLint + Prettier configurados e funcionando
  • Commits semânticos com Husky e commitlint
  • Estrutura profissional e escalável

Setup completo

mkdir api-produtos && cd api-produtos
npm init -y

# Produção
npm install express mongoose dotenv bcrypt jsonwebtoken

# Desenvolvimento
npm install -D typescript ts-node-dev
npm install -D @types/node @types/express @types/bcrypt @types/jsonwebtoken
npm install -D jest ts-jest @types/jest supertest @types/supertest
npm install -D eslint @eslint/js prettier eslint-config-prettier
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional

Configurações

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "declaration": true,
    "sourceMap": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}
// jest.config.json
{
  "preset": "ts-jest",
  "testEnvironment": "node",
  "testMatch": ["**/tests/**/*.test.ts"],
  "collectCoverageFrom": ["src/**/*.ts", "!src/index.ts"],
  "coverageThreshold": {
    "global": {
      "branches": 70,
      "functions": 80,
      "lines": 80,
      "statements": 80
    }
  },
  "setupFiles": ["./tests/setup.ts"]
}
// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5",
  "tabWidth": 2,
  "printWidth": 100,
  "arrowParens": "always"
}
// eslint.config.js
const js = require('@eslint/js');
const prettier = require('eslint-config-prettier');

module.exports = [
  js.configs.recommended,
  {
    languageOptions: {
      ecmaVersion: 2024,
      sourceType: 'commonjs',
      globals: {
        require: 'readonly', module: 'readonly',
        exports: 'readonly', __dirname: 'readonly',
        process: 'readonly', console: 'readonly',
        Buffer: 'readonly', setTimeout: 'readonly',
      },
    },
    rules: {
      'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
      'eqeqeq': ['error', 'always'],
      'no-var': 'error',
      'prefer-const': 'error',
      'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],
      'no-throw-literal': 'error',
    },
    ignores: ['node_modules/', 'dist/', 'coverage/'],
  },
  prettier,
];
// package.json — scripts e configurações completas
{
  "name": "api-produtos",
  "version": "1.0.0",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "ts-node-dev --respawn --transpile-only src/index.ts",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "lint": "eslint src/",
    "lint:fix": "eslint src/ --fix",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "quality": "npm run format:check && npm run lint && npm test",
    "prepare": "husky"
  },
  "lint-staged": {
    "src/**/*.ts": ["prettier --write", "eslint --fix", "eslint"],
    "*.{json,md}": ["prettier --write"]
  }
}
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
};

Estrutura do projeto

src/
├── types/
│   └── index.ts          ← todos os tipos e interfaces
├── config/
│   └── database.ts
├── models/
│   └── Produto.ts
├── middlewares/
│   ├── auth.ts
│   ├── validar.ts
│   └── erros.ts
├── controllers/
│   └── produtoController.ts
├── routes/
│   └── produtos.ts
└── index.ts

tests/
├── setup.ts
├── unit/
│   └── validacoes.test.ts
└── integration/
    └── produtos.test.ts

Tipos centralizados

// src/types/index.ts
import { Document } from 'mongoose';

// ── Produto ────────────────────────────────────────

export type Categoria =
  | 'eletronicos'
  | 'roupas'
  | 'alimentos'
  | 'livros'
  | 'outros';

export interface IProduto extends Document {
  nome: string;
  descricao: string;
  preco: number;
  estoque: number;
  categoria: Categoria;
  ativo: boolean;
  tags: string[];
  criadoEm: Date;
  atualizadoEm: Date;
}

export type CriarProdutoBody = Pick<
  IProduto,
  'nome' | 'descricao' | 'preco' | 'estoque' | 'categoria'
> & { tags?: string[] };

export type AtualizarProdutoBody = Partial<CriarProdutoBody>;

export type ProdutoFiltros = {
  categoria?: Categoria;
  precoMin?: number;
  precoMax?: number;
  busca?: string;
  ativo?: boolean;
};

// ── Paginação ──────────────────────────────────────

export interface Paginacao {
  total: number;
  pagina: number;
  por_pagina: number;
  total_paginas: number;
}

export interface RespostaPaginada<T> {
  dados: T[];
  paginacao: Paginacao;
}

// ── API ────────────────────────────────────────────

export interface RespostaErro {
  erro: string;
  detalhes?: string[];
  status: number;
}

export interface QueryPaginacao {
  pagina?: string;
  por_pagina?: string;
  ordenar?: string;
}

export type QueryProdutos = ProdutoFiltros & QueryPaginacao;

// ── Express helpers ────────────────────────────────

export interface ParamsId {
  id: string;
}

Model tipado

// src/models/Produto.ts
import mongoose, { Model, Schema } from 'mongoose';
import { IProduto, Categoria } from '../types';

const CATEGORIAS: Categoria[] = [
  'eletronicos', 'roupas', 'alimentos', 'livros', 'outros',
];

const produtoSchema = new Schema<IProduto>(
  {
    nome: {
      type: String,
      required: [true, 'Nome é obrigatório.'],
      trim: true,
      minlength: [2, 'Nome deve ter pelo menos 2 caracteres.'],
      maxlength: [200, 'Nome não pode exceder 200 caracteres.'],
    },
    descricao: {
      type: String,
      trim: true,
      maxlength: [2000, 'Descrição não pode exceder 2000 caracteres.'],
      default: '',
    },
    preco: {
      type: Number,
      required: [true, 'Preço é obrigatório.'],
      min: [0, 'Preço não pode ser negativo.'],
    },
    estoque: {
      type: Number,
      required: [true, 'Estoque é obrigatório.'],
      min: [0, 'Estoque não pode ser negativo.'],
      default: 0,
    },
    categoria: {
      type: String,
      enum: { values: CATEGORIAS, message: 'Categoria inválida.' },
      required: [true, 'Categoria é obrigatória.'],
    },
    ativo: { type: Boolean, default: true },
    tags: [{ type: String, trim: true, lowercase: true }],
  },
  {
    timestamps: { createdAt: 'criadoEm', updatedAt: 'atualizadoEm' },
    versionKey: false,
  }
);

// Índices
produtoSchema.index({ nome: 'text', descricao: 'text' });
produtoSchema.index({ categoria: 1, ativo: 1 });
produtoSchema.index({ preco: 1 });

type ProdutoModel = Model<IProduto>;
export const Produto = mongoose.model<IProduto, ProdutoModel>('Produto', produtoSchema);

Controller tipado

// src/controllers/produtoController.ts
import { Request, Response, NextFunction } from 'express';
import mongoose from 'mongoose';
import { Produto } from '../models/Produto';
import {
  CriarProdutoBody,
  AtualizarProdutoBody,
  QueryProdutos,
  ParamsId,
  RespostaPaginada,
  IProduto,
} from '../types';

// ── Listar ─────────────────────────────────────────

export async function listar(
  req: Request<{}, {}, {}, QueryProdutos>,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    const {
      categoria,
      precoMin,
      precoMax,
      busca,
      ativo = 'true',
      pagina = '1',
      por_pagina = '10',
      ordenar = '-criadoEm',
    } = req.query;

    const filtros: Record<string, unknown> = {
      ativo: ativo === 'true',
    };

    if (categoria) filtros.categoria = categoria;
    if (busca) filtros.$text = { $search: busca };
    if (precoMin !== undefined || precoMax !== undefined) {
      filtros.preco = {
        ...(precoMin !== undefined && { $gte: Number(precoMin) }),
        ...(precoMax !== undefined && { $lte: Number(precoMax) }),
      };
    }

    const paginaNum = Math.max(1, parseInt(pagina, 10));
    const porPaginaNum = Math.min(50, Math.max(1, parseInt(por_pagina, 10)));
    const skip = (paginaNum - 1) * porPaginaNum;

    const [dados, total] = await Promise.all([
      Produto.find(filtros)
        .sort(ordenar)
        .skip(skip)
        .limit(porPaginaNum)
        .lean(),
      Produto.countDocuments(filtros),
    ]);

    const resposta: RespostaPaginada<IProduto> = {
      dados: dados as IProduto[],
      paginacao: {
        total,
        pagina: paginaNum,
        por_pagina: porPaginaNum,
        total_paginas: Math.ceil(total / porPaginaNum),
      },
    };

    res.json(resposta);
  } catch (erro) {
    next(erro);
  }
}

// ── Buscar um ──────────────────────────────────────

export async function buscarUm(
  req: Request<ParamsId>,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    const { id } = req.params;

    if (!mongoose.isValidObjectId(id)) {
      res.status(400).json({ erro: 'ID inválido.', status: 400 });
      return;
    }

    const produto = await Produto.findById(id);

    if (!produto) {
      res.status(404).json({ erro: 'Produto não encontrado.', status: 404 });
      return;
    }

    res.json(produto);
  } catch (erro) {
    next(erro);
  }
}

// ── Criar ──────────────────────────────────────────

export async function criar(
  req: Request<{}, {}, CriarProdutoBody>,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    const produto = await Produto.create(req.body);
    res.status(201).json(produto);
  } catch (erro) {
    next(erro);
  }
}

// ── Atualizar ──────────────────────────────────────

export async function atualizar(
  req: Request<ParamsId, {}, AtualizarProdutoBody>,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    const { id } = req.params;

    if (!mongoose.isValidObjectId(id)) {
      res.status(400).json({ erro: 'ID inválido.', status: 400 });
      return;
    }

    const produto = await Produto.findByIdAndUpdate(
      id,
      { $set: req.body },
      { new: true, runValidators: true }
    );

    if (!produto) {
      res.status(404).json({ erro: 'Produto não encontrado.', status: 404 });
      return;
    }

    res.json(produto);
  } catch (erro) {
    next(erro);
  }
}

// ── Remover ────────────────────────────────────────

export async function remover(
  req: Request<ParamsId>,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    const { id } = req.params;

    if (!mongoose.isValidObjectId(id)) {
      res.status(400).json({ erro: 'ID inválido.', status: 400 });
      return;
    }

    const produto = await Produto.findByIdAndDelete(id);

    if (!produto) {
      res.status(404).json({ erro: 'Produto não encontrado.', status: 404 });
      return;
    }

    res.json({ mensagem: `Produto "${produto.nome}" removido com sucesso.` });
  } catch (erro) {
    next(erro);
  }
}

// ── Estatísticas ───────────────────────────────────

export async function estatisticas(
  _req: Request,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    const stats = await Produto.aggregate([
      { $match: { ativo: true } },
      {
        $group: {
          _id: '$categoria',
          total: { $sum: 1 },
          precoMedio: { $avg: '$preco' },
          precoMin: { $min: '$preco' },
          precoMax: { $max: '$preco' },
          estoqueTotal: { $sum: '$estoque' },
        },
      },
      {
        $project: {
          categoria: '$_id',
          total: 1,
          precoMedio: { $round: ['$precoMedio', 2] },
          precoMin: 1,
          precoMax: 1,
          estoqueTotal: 1,
          _id: 0,
        },
      },
      { $sort: { total: -1 } },
    ]);

    res.json({ categorias: stats });
  } catch (erro) {
    next(erro);
  }
}

Testes

// tests/setup.ts
import mongoose from 'mongoose';

beforeAll(async () => {
  const url = process.env.MONGODB_TEST_URL ||
    'mongodb://localhost:27017/produtos-test';
  await mongoose.connect(url);
});

afterAll(async () => {
  await mongoose.connection.dropDatabase();
  await mongoose.connection.close();
});
// tests/unit/validacoes.test.ts
import mongoose from 'mongoose';

describe('isValidObjectId()', () => {
  it('aceita ObjectId válido', () => {
    const id = new mongoose.Types.ObjectId().toString();
    expect(mongoose.isValidObjectId(id)).toBe(true);
  });

  it.each(['abc', '123', '', 'null', undefined])(
    'rejeita "%s" como ID inválido',
    (id) => {
      expect(mongoose.isValidObjectId(id)).toBe(false);
    }
  );
});

describe('formatarPreco()', () => {
  // Importar e testar função utilitária do projeto
  function formatarPreco(valor: number): string {
    return valor.toLocaleString('pt-BR', {
      style: 'currency',
      currency: 'BRL',
    });
  }

  it('formata valor positivo', () => {
    expect(formatarPreco(1500)).toContain('1.500');
  });

  it('formata zero', () => {
    expect(formatarPreco(0)).toContain('0');
  });

  it('formata centavos', () => {
    expect(formatarPreco(9.99)).toContain('9,99');
  });
});
// tests/integration/produtos.test.ts
import request from 'supertest';
import mongoose from 'mongoose';
import app from '../../src/index';
import { Produto } from '../../src/models/Produto';

// Helper para criar produto de teste
function produtoBase(overrides = {}) {
  return {
    nome: 'Notebook Pro',
    descricao: 'Notebook de alta performance',
    preco: 3500,
    estoque: 10,
    categoria: 'eletronicos',
    ...overrides,
  };
}

beforeEach(async () => {
  await Produto.deleteMany({});
});

describe('GET /produtos', () => {
  it('retorna lista vazia quando não há produtos', async () => {
    const res = await request(app).get('/produtos');
    expect(res.status).toBe(200);
    expect(res.body.dados).toHaveLength(0);
    expect(res.body.paginacao.total).toBe(0);
  });

  it('retorna produtos cadastrados', async () => {
    await Produto.create([produtoBase(), produtoBase({ nome: 'Mouse Gamer' })]);

    const res = await request(app).get('/produtos');
    expect(res.status).toBe(200);
    expect(res.body.dados).toHaveLength(2);
    expect(res.body.paginacao.total).toBe(2);
  });

  it('filtra por categoria', async () => {
    await Produto.create([
      produtoBase({ categoria: 'eletronicos' }),
      produtoBase({ nome: 'Camiseta', categoria: 'roupas' }),
    ]);

    const res = await request(app).get('/produtos?categoria=eletronicos');
    expect(res.status).toBe(200);
    expect(res.body.dados).toHaveLength(1);
    expect(res.body.dados[0].categoria).toBe('eletronicos');
  });

  it('pagina resultados corretamente', async () => {
    const produtos = Array.from({ length: 15 }, (_, i) =>
      produtoBase({ nome: `Produto ${i + 1}` })
    );
    await Produto.create(produtos);

    const res = await request(app).get('/produtos?pagina=2&por_pagina=5');
    expect(res.status).toBe(200);
    expect(res.body.dados).toHaveLength(5);
    expect(res.body.paginacao.pagina).toBe(2);
    expect(res.body.paginacao.total_paginas).toBe(3);
  });

  it('filtra por faixa de preço', async () => {
    await Produto.create([
      produtoBase({ preco: 100 }),
      produtoBase({ nome: 'Caro', preco: 5000 }),
      produtoBase({ nome: 'Médio', preco: 500 }),
    ]);

    const res = await request(app).get('/produtos?precoMin=200&precoMax=1000');
    expect(res.status).toBe(200);
    expect(res.body.dados).toHaveLength(1);
    expect(res.body.dados[0].preco).toBe(500);
  });
});

describe('POST /produtos', () => {
  it('cria produto com dados válidos', async () => {
    const res = await request(app)
      .post('/produtos')
      .send(produtoBase());

    expect(res.status).toBe(201);
    expect(res.body).toMatchObject({
      nome: 'Notebook Pro',
      preco: 3500,
      categoria: 'eletronicos',
    });
    expect(res.body._id).toBeDefined();
  });

  it('rejeita produto sem nome', async () => {
    const res = await request(app)
      .post('/produtos')
      .send(produtoBase({ nome: undefined }));

    expect(res.status).toBe(422);
    expect(res.body.detalhes).toEqual(
      expect.arrayContaining([expect.stringContaining('Nome')])
    );
  });

  it('rejeita preço negativo', async () => {
    const res = await request(app)
      .post('/produtos')
      .send(produtoBase({ preco: -10 }));

    expect(res.status).toBe(422);
  });

  it('rejeita categoria inválida', async () => {
    const res = await request(app)
      .post('/produtos')
      .send(produtoBase({ categoria: 'inexistente' }));

    expect(res.status).toBe(422);
  });
});

describe('PUT /produtos/:id', () => {
  it('atualiza produto existente', async () => {
    const produto = await Produto.create(produtoBase());

    const res = await request(app)
      .put(`/produtos/${produto._id}`)
      .send({ preco: 2999, estoque: 5 });

    expect(res.status).toBe(200);
    expect(res.body.preco).toBe(2999);
    expect(res.body.estoque).toBe(5);
    expect(res.body.nome).toBe('Notebook Pro'); // não mudou
  });

  it('retorna 404 para ID inexistente', async () => {
    const idFalso = new mongoose.Types.ObjectId();
    const res = await request(app)
      .put(`/produtos/${idFalso}`)
      .send({ preco: 100 });

    expect(res.status).toBe(404);
  });

  it('retorna 400 para ID malformado', async () => {
    const res = await request(app)
      .put('/produtos/id-invalido')
      .send({ preco: 100 });

    expect(res.status).toBe(400);
  });
});

describe('DELETE /produtos/:id', () => {
  it('remove produto existente', async () => {
    const produto = await Produto.create(produtoBase());
    const res = await request(app).delete(`/produtos/${produto._id}`);

    expect(res.status).toBe(200);
    expect(res.body.mensagem).toContain('Notebook Pro');

    const removido = await Produto.findById(produto._id);
    expect(removido).toBeNull();
  });

  it('retorna 404 para produto inexistente', async () => {
    const idFalso = new mongoose.Types.ObjectId();
    const res = await request(app).delete(`/produtos/${idFalso}`);
    expect(res.status).toBe(404);
  });
});

describe('GET /produtos/estatisticas', () => {
  it('retorna estatísticas por categoria', async () => {
    await Produto.create([
      produtoBase({ preco: 1000 }),
      produtoBase({ nome: 'Tablet', preco: 2000 }),
      produtoBase({ nome: 'Camiseta', categoria: 'roupas', preco: 100 }),
    ]);

    const res = await request(app).get('/produtos/estatisticas');
    expect(res.status).toBe(200);
    expect(res.body.categorias).toHaveLength(2);

    const eletronicos = res.body.categorias.find(
      (c: { categoria: string }) => c.categoria === 'eletronicos'
    );
    expect(eletronicos.total).toBe(2);
    expect(eletronicos.precoMedio).toBe(1500);
  });
});

O que este projeto exercitou

| Pilar | Como foi aplicado |

|---|---|

| TypeScript | Tipos centralizados, generics, utility types, Express tipado |

| Jest | Unitários para validações, integração com Supertest |

| ESLint | Regras de qualidade em todo o projeto TS |

| Prettier | Formatação automática consistente |

| Git semântico | Commits por tipo, branches descritivas |

| Husky | pre-commit com lint-staged, commit-msg com commitlint |

| Mongoose tipado | Schema com interface, índices, aggregation |

| Paginação | skip/limit com tipos corretos |

| Filtros | Query params tipados com QueryProdutos |

| Erros | Tratamento tipado em todos os controllers |


Conclusão do Módulo 5

Você agora tem as ferramentas que separam código amador de código profissional. Testes que garantem que seu código funciona. Linters que encontram bugs antes de você. Formatadores que eliminam debates de estilo. Git que documenta a história do projeto. TypeScript que torna refatorações seguras.

Esses não são detalhes — são os alicerces de qualquer projeto sério.

O Módulo 6 começa no próximo artigo com React — onde todo o JavaScript que você aprendeu nos módulos anteriores se transforma em interfaces modernas e reativas.


 

📚 Fontes e Referências

  • Jest + TypeScript — ts-jest: https://kulshekhar.github.io/ts-jest
  • Supertest: https://github.com/ladjs/supertest
  • TypeScript — Documentação: https://www.typescriptlang.org/docs
  • ESLint — TypeScript plugin: https://typescript-eslint.io
  • Mongoose + TypeScript: https://mongoosejs.com/docs/typescript.html
  • Effective TypeScript — Dan Vanderkam (O'Reilly)
  • Testing JavaScript Applications — Lucas da Costa (Manning)
  • Kent C. Dodds — Write tests, not too many: https://kentcdodds.com/blog/write-tests
  • roadmap.sh — Backend: https://roadmap.sh/backend
Comentários

Mais em Javascript

Eventos: click, input, submit e muito mais
Eventos: click, input, submit e muito mais

No artigo anterior aprendemos a selecionar e modificar elementos do DOM. Mas...

Revisão + Projeto Final: SPA Completa
Revisão + Projeto Final: SPA Completa

Chegamos ao fim de mais uma etapa. Em cinco artigos você aprendeu React do ze...

Testes automatizados com Jest
Testes automatizados com Jest

Existe um momento na vida de todo desenvolvedor em que ele mexe em um código...