API backend em Node.js + Express + TypeScript, seguindo Clean Architecture e princípios SOLID, com suporte a multi-tenant por empresa, autenticação JWT, convites e gestão de membros.
- Linguagem: TypeScript
- Runtime: Node.js
- Framework HTTP: Express
- ORM: Prisma (PostgreSQL)
- Auth: JWT + Cookies httpOnly
- Validação: Zod
- Lint/Format: ESLint + Prettier
- Execução TS: tsx
- Commits: Commitizen (Conventional Commits)
- Git Hooks: Husky + lint-staged
-
Signup (
POST /api/auth/signup)- Cria um novo usuário
- Retorna usuário + JWT
- Seta cookie
tokenhttpOnly
-
Login (
POST /api/auth/login)- Autentica por e-mail/senha
- Retorna usuário + JWT
- Seta cookie
tokenhttpOnly
-
Logout (
POST /api/auth/logout)- Limpa cookie
token
- Limpa cookie
-
Quem sou eu (
GET /api/auth/me)- Retorna o usuário autenticado
-
Criar empresa (
POST /api/company)- Cria uma empresa
- Cria
membershipdo usuário comoOWNER - Se o usuário não tiver
activeCompanyId, seta essa empresa como ativa
-
Listar minhas empresas (
GET /api/companies)- Retorna lista paginada de empresas nas quais o usuário é membro
- Inclui papel do usuário em cada empresa (
userRole)
-
Selecionar empresa ativa (
POST /api/company/:id/select)- Atualiza
activeCompanyIddo usuário
- Atualiza
-
Detalhes da empresa (
GET /api/company/:id)- Retorna dados da empresa + membros (memberships)
-
Listar membros (
GET /api/company/:companyId/members)- Lista membros da empresa (paginado)
- Qualquer membro pode ver (OWNER, ADMIN, MEMBER)
- Retorna role e dados básicos do usuário
-
Alterar role de membro (
PATCH /api/membership/:membershipId/role)- Regras de permissão:
MEMBERnão pode alterarADMINpode alterarMEMBER, mas nãoOWNER(nem promover/degradar paraOWNER)OWNERpode alterar qualquer membro- Ninguém pode alterar o próprio role (
CannotChangeOwnRoleError)
- Regras de permissão:
-
Remover membro (
DELETE /api/membership/:membershipId)- Regras de permissão:
MEMBERnão pode removerADMINnão pode removerOWNER- Não é permitido remover a si mesmo (
CannotRemoveSelfError) - Não é permitido remover o último
OWNERda empresa (CannotRemoveLastOwnerError)
- Regras de permissão:
-
Criar convite (
POST /api/company/:companyId/invite)- Somente
OWNEReADMIN ADMINnão pode convidar com roleOWNER- Gera token único com expiração de 7 dias
- Impede convites duplicados ativos para o mesmo e-mail na mesma empresa
- Somente
-
Listar convites (
GET /api/company/:companyId/invites)- Somente
OWNEReADMIN - Lista convites (ativos, expirados, usados) com paginação
- Somente
-
Cancelar convite (
DELETE /api/company/:companyId/invite/:token)- Somente
OWNEReADMIN - Remove o convite (independente de usado/expirado)
- Somente
-
Aceitar convite (
POST /api/invite/:token/accept)- Público (não exige auth)
- Corpo:
- Para novo usuário:
{ "name": string, "password": string } - Para usuário já autenticado: body vazio (usa o
req.user)
- Para novo usuário:
- Fluxos:
- Se usuário NÃO autenticado:
- Cria usuário com
name,passworde e-mail do invite - Cria membership com role do invite
- Marca invite como usado
- Define empresa do invite como
activeCompanyIddo usuário - Gera JWT e seta cookie
tokenhttpOnly
- Cria usuário com
- Se usuário autenticado:
- Verifica se e-mail do usuário é igual ao do invite
- Verifica se já não é membro da empresa
- Cria membership
- Marca invite como usado
- Se usuário NÃO autenticado:
Camadas principais:
-
src/domainentities: modelos de domínio (User, Company, Membership, Invite)errors: erros de domínio (UserAlreadyExistsError, CompanyNotFoundError, etc.)enums: enums comoRole
-
src/repositories- Abstrações de acesso a dados usando Prisma
UserRepository,CompanyRepository,MembershipRepository,InviteRepository
-
src/use-cases- Casos de uso focados em regras de negócio
- Ex.:
CreateCompanyUseCase,SelectActiveCompanyUseCase,ListCompanyMembersUseCase,CreateInviteUseCase,AcceptInviteUseCaseetc.
-
src/controllers- Adaptadores HTTP (Express) que chamam os use cases
-
src/routes- Definição das rotas Express e wiring de dependências
-
src/middlewaresauth.middleware.ts— valida JWT (cookie ou header), carregareq.user
-
src/configenv.ts— valida variáveis de ambiente com Zodprisma.ts— client do Prisma
src/
config/
env.ts
prisma.ts
domain/
entities/
user.entity.ts
company.entity.ts
membership.entity.ts
invite.entity.ts
enums/
role.enum.ts
errors/
user.errors.ts
company.errors.ts
membership.errors.ts
invite.errors.ts
repositories/
user.repository.ts
company.repository.ts
membership.repository.ts
invite.repository.ts
use-cases/
user/
create-user.use-case.ts
authenticate-user.use-case.ts
get-user-profile.use-case.ts
update-active-company.use-case.ts
company/
create-company.use-case.ts
list-user-companies.use-case.ts
select-active-company.use-case.ts
get-company-details.use-case.ts
membership/
list-company-members.use-case.ts
update-member-role.use-case.ts
remove-member.use-case.ts
invite/
create-invite.use-case.ts
accept-invite.use-case.ts
list-company-invites.use-case.ts
cancel-invite.use-case.ts
controllers/
auth.controller.ts
company.controller.ts
membership.controller.ts
invite.controller.ts
middlewares/
auth.middleware.ts
routes/
auth.routes.ts
company.routes.ts
membership.routes.ts
invite.routes.ts
utils/
jwt.util.ts
types/
express.d.ts
app.ts
server.ts
prisma/
schema.prisma
seed.ts
Arquivo .env na raiz:
PORT=3333
CORS_ORIGIN=*
NODE_ENV=development
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/altaa_db?schema=public"
JWT_SECRET="uma-chave-bem-grande-e-segura-aqui-com-32+caracteres"
JWT_EXPIRES_IN="7d"- Usa Zod para garantir que:
DATABASE_URLé uma URL válidaJWT_SECRETexiste e tem tamanho mínimoPORTé convertido para númeroNODE_ENVédevelopment | production | test
pnpm installpnpm prisma:generate
pnpm prisma:migrateOu diretamente:
npx prisma generate
npx prisma migrate devpnpm prisma:seedOu via Prisma:
npx prisma db seedPara resetar banco e rodar seed automaticamente:
pnpm prisma:resetO seed cria:
- 5 usuários com senha
password123 - 4 empresas
- 9 memberships com roles variados (
OWNER,ADMIN,MEMBER) - 5 convites em diferentes estados (válido, expirado, usado, para usuário existente etc.)
Principais usuários de teste:
joao.silva@techsolutions.com– senhapassword123maria.santos@techsolutions.com– senhapassword123pedro.oliveira@digitalmarketing.com– senhapassword123ana.costa@startup.com– senhapassword123carlos.mendes@freelancer.com– senhapassword123
pnpm dev- Usa
tsxpara rodarsrc/server.tscom watch - Loga no console o status da conexão com o banco e a porta
pnpm build
pnpm startcurl -X POST http://localhost:3333/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "joao.silva@techsolutions.com",
"password": "password123"
}'curl -X POST http://localhost:3333/api/company \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SEU_TOKEN_AQUI" \
-d '{
"name": "Minha Nova Empresa",
"logoUrl": "https://ui-avatars.com/api/?name=Nova+Empresa"
}'curl -X GET "http://localhost:3333/api/companies?page=1&pageSize=10" \
-H "Authorization: Bearer SEU_TOKEN_AQUI"curl -X GET "http://localhost:3333/api/company/COMPANY_ID/members" \
-H "Authorization: Bearer SEU_TOKEN_AQUI"curl -X POST "http://localhost:3333/api/company/COMPANY_ID/invite" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SEU_TOKEN_AQUI" \
-d '{
"email": "novo.dev@example.com",
"role": "MEMBER"
}'curl -X POST "http://localhost:3333/api/invite/TOKEN_DO_CONVITE/accept" \
-H "Content-Type: application/json" \
-d '{
"name": "Novo Desenvolvedor",
"password": "password123"
}'- ESLint com TypeScript
- Prettier (sem ponto e vírgula, aspas simples)
- Plugins:
eslint-plugin-import-helpers- organização de importseslint-plugin-perfectionist- ordenação alfabéticaeslint-plugin-unused-imports- remove imports não usados
Scripts disponíveis:
pnpm lint # Verifica problemas
pnpm lint:fix # Corrige automaticamente
pnpm format # Formata código
pnpm format:check # Verifica formatação
pnpm type-check # Verifica tipos TypeScript- Pre-commit: roda ESLint e Prettier automaticamente nos arquivos staged
- Commit-msg: valida mensagem de commit (Conventional Commits)
- Prepare-commit-msg: abre Commitizen para commits padronizados
Para fazer commits padronizados:
pnpm commitOu use o git hook automático:
git commitpnpm dev # Desenvolvimento com watch
pnpm build # Build TypeScript
pnpm start # Produção (após build)
pnpm lint # Lint
pnpm lint:fix # Lint + fix
pnpm format # Format
pnpm format:check # Verifica formatação
pnpm type-check # Type check
pnpm prisma:generate # Gera Prisma Client
pnpm prisma:migrate # Roda migrations (dev)
pnpm prisma:migrate:prod # Roda migrations (prod)
pnpm prisma:studio # Abre Prisma Studio
pnpm prisma:seed # Roda seed
pnpm prisma:reset # Reset DB + migrations + seed
pnpm commit # Commit com Commitizen- Helmet: headers de segurança HTTP
- CORS: configurável via
CORS_ORIGIN - Rate Limiting: proteção contra DDoS
- JWT: tokens com expiração configurável
- Cookies httpOnly: proteção contra XSS
- Bcrypt: hash de senhas com salt rounds
MIT
Igor Sasaki
- GitHub: @IgorSasaki
- Email: igor-sasaki@hotmail.com