Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ jobs:

- run: npm ci

- run: npm test
- run: npm run test:ci
119 changes: 117 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,118 @@
# tabnews-clone
# SaaS-Base

Implementação do tabnews.com.br para o curso.dev.
A **lean, fully wired foundation for SaaS development**. It provides a complete Next.js setup, **Docker-managed PostgreSQL**, structured **migrations**, and automated environment orchestration - so your project boots with a single command and stays consistent across machines.

The foundation also includes **integration testing**, plus enforced code quality through **ESLint** and **Prettier**. For Git hygiene, it uses **Husky**, **Commitlint**, and **Commitizen** to keep your commit history clean, predictable, and automation-friendly.

Everything is preconfigured to eliminate setup friction, prevent “works on my machine” headaches, and let you focus directly on building product features - not infrastructure.

---

## 🚀 Features

### 🌟 Automated Dev Workflow

The automated development workflow ensures you never waste time manually preparing your environment.

Instead of juggling multiple terminal commands, connection waits, and database setup steps, a single script handles everything: starting infrastructure, validating service readiness, applying migrations, and booting the application. This removes entire classes of “it works on my machine” problems, keeps onboarding effortless for new developers, and makes day-to-day development dramatically smoother and more reliable.

The `dev` script performs everything in sequence:

1. Starts Docker services
2. Waits for PostgreSQL
3. Applies migrations
4. Runs Next.js in development mode

Run it with:

```bash
npm run dev
```

Under the hood, what this command will do is:

```
npm run services:up && npm run services:wait:database && npm run migrations:up && next dev
```

### 🌟 Husky + Commitlint + Commitizen

To ensure your project’s Git history stays clean, consistent, and reliable.

Husky enforces checks before bad commits sneak in, Commitlint guarantees every commit message follows a predictable and searchable format, and Commitizen gives contributors an easy, structured way to write proper commit messages without memorizing conventions. Together, they prevent chaotic commit logs, improve collaboration, power automated changelogs, and make long-term maintenance far less painful.

- Husky hooks installed automatically through `prepare` script
- Commitlint enforces conventional commits
- Commitizen interactive prompts via:

```bash
npm run commit
```

### 🌟 Jest Test Suite (Integration-Ready)

- Jest fully configured for ESM
- Integration tests located in `tests/integration/api/v1/...`
- Tests call real Next.js API routes
- CI test flow automatically spins up services and runs migrations
- Scripts:
- `npm test`
- `npm run test:watch`
- `npm run test:ci` — runs Next.js + Jest concurrently

### 🏅 GitHub Actions to ensure quality

Made to ensure the project grows in a clean, secure and professional way, running this GitHub Action on all pull requests:

- Automated tests
- Commit linting
- Code quality linting
- Code style linting

If you or someone else ever make a bad PR, GitHub will let you know.

![alt text](image.png)

> Tip: You can make the tests Required for merge in GitHub's interface. That way, if some check don't pass, the Merge button won't be available, adding some extra security for the project.

### 🐳 Dockerized Local Environment

Docker Compose (`infra/compose.yaml`) that provides a PostgreSQL container.

Service commands:

- `npm run services:up` — start infrastructure containers
- `npm run services:stop` — stop containers
- `npm run services:down` — remove containers and volumes

### 🐘 PostgreSQL + Migrations

- PostgreSQL managed via Docker Compose
- Automatic database readiness check (`infra/scripts/wait-for-postgres.js`)
- Migration system powered by **node-pg-migrate**
- Migration files stored in `infra/migrations`
- Scripts:
- `npm run migrations:create` — create a new migration file
- `npm run migrations:up` — apply pending migrations

### 🏘️ Next.js Application Structure

- API routes organized under `pages/api/v1/...`
- Example endpoints (such as `status`)
- Ready-to-expand modular folder structure
- ESM-first setup (`"type": "module"` in package.json)

### 🧼 ESLint + Prettier

- ESLint with Next.js, Jest, and Prettier integrations
- Prettier config prewired
- Linting commands:
- `npm run lint:eslint:check`
- `npm run lint:prettier:check`
- `npm run lint:prettier:fix`

---

## 📜 License

MIT License.
4 changes: 3 additions & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module.exports = {
const commitlintConfig = {
extends: ["@commitlint/config-conventional"],
};

export default commitlintConfig;
Binary file added image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 0 additions & 7 deletions infra/migrations/1764719520015_test-migration.js

This file was deleted.

19 changes: 19 additions & 0 deletions infra/migrations/1765591892999_test-migration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-disable no-unused-vars */
/**
* @type {import('node-pg-migrate').ColumnDefinitions | undefined}
*/
export const shorthands = undefined;

/**
* @param pgm {import('node-pg-migrate').MigrationBuilder}
* @param run {() => void | undefined}
* @returns {Promise<void> | void}
*/
export const up = (pgm) => {};

/**
* @param pgm {import('node-pg-migrate').MigrationBuilder}
* @param run {() => void | undefined}
* @returns {Promise<void> | void}
*/
export const down = (pgm) => {};
4 changes: 3 additions & 1 deletion infra/scripts/orchestrator.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import retry from "async-retry";
import database from "infra/database";
import database from "../database";

// Web server must be running in order for the tests to work. Otherwise they will take some time and then timeout. Make sure you ran `npm run dev` on another terminal.

async function waitForAllServices() {
await waitForWebServer();
Expand Down
7 changes: 5 additions & 2 deletions infra/scripts/wait-for-postgres.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const { exec } = require("node:child_process");
import { exec } from "node:child_process";

function checkPostgres() {
exec("docker exec postgres-dev pg_isready --host localhost", handleReturn);

function handleReturn(error, stdout) {
if (stdout.search("accepting connections") === -1) {
const postgresIsReady = stdout.search("accepting connections") !== -1;

if (!postgresIsReady) {
process.stdout.write(".");
checkPostgres();
return;
Expand All @@ -15,4 +17,5 @@ function checkPostgres() {
}

process.stdout.write("\n\n🔴 Waiting for Postgres to accept connections...");

checkPostgres();
10 changes: 5 additions & 5 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const dotenv = require("dotenv");
dotenv.config({ path: ".env.development" });
import dotenv from "dotenv";
import nextJest from "next/jest.js";

const nextJest = require("next/jest");
dotenv.config({ path: ".env.development" });

const createJestConfig = nextJest({
dir: ".",
});

const jestConfig = createJestConfig({
moduleDirectories: ["node_modules", "<rootDir>"],
testTimeout: 60000,
testTimeout: 15000,
});

module.exports = jestConfig;
export default jestConfig;
Loading