Skip to content

laizeferraz/api-challenge-node

Repository files navigation

Node.js Challenge API

Lightweight Fastify + TypeScript API used in the Rocketseat challenge. It provides a small "courses" API backed by Postgres via Drizzle ORM. The project includes authentication (JWT), password hashing (argon2), tests (Vitest + Supertest), factories (Faker), seeding, and OpenAPI docs in development.

Tech stack

  • Node.js (ESM) — project uses a Node 22+ runtime which has native TypeScript support
  • TypeScript
  • Fastify
  • Drizzle ORM (Postgres)
  • Zod (validation)
  • jsonwebtoken (JWT)
  • argon2 (password hashing)
  • Vitest + Supertest + Faker for tests
  • @scalar/fastify-api-reference / @fastify/swagger for API docs

Requirements

  • Node.js 22.x or newer (or a Node runtime that supports running .ts directly)
  • PostgreSQL for development / production
  • A .env file with at least:
DATABASE_URL=postgresql://user:password@localhost:5432/challenge
JWT_SECRET=your_jwt_secret
NODE_ENV=development

There is also a .env.test file configured for tests (points to challenge_test).

Quickstart

  1. Install dependencies
npm install
  1. Prepare environment (.env / .env.test)

  2. (Optional) run local Postgres via Docker Compose

docker compose up -d
  1. Run migrations
npm run db:migrate
  1. Start server in development
npm run dev

When NODE_ENV=development the OpenAPI docs are available at /docs and the OpenAPI JSON is exposed by Fastify Swagger.

Available npm scripts

  • npm run dev — start server in dev mode (uses node --env-file .env --watch src/server.ts). Node 22+ supports running .ts directly so the experimental flag is not required.
  • npm run db:seed — run src/db/seed.ts to populate development DB with sample data.
  • npm run db:generatedrizzle-kit generate
  • npm run db:migratedrizzle-kit migrate
  • npm run db:studiodrizzle-kit studio (Drizzle Studio)
  • npm run pretest — runs migrations against the test DB using .env.test before tests
  • npm run test — runs the test suite (Vitest). The test script uses .env.test by default.

Project layout (high level)

  • src/server.ts — server bootstrap
  • src/app.ts — Fastify instance, plugins and docs registration
  • src/http/routes/* — route handlers and hooks
  • src/db/* — Drizzle client, schema and seed
  • src/tests/factories/* — test factories (Faker) used by tests

API Endpoints (summary)

All endpoints are JSON-based and validated using Zod. The server uses Fastify's type provider for Zod, so request bodies/params and responses follow the schemas defined in the route files.

  • POST /courses — create a course (title required, validated with Zod)
  • GET /courses — list courses
  • GET /courses/:id — get course by id
  • POST /login — authenticate a user and receive a JWT

See the route files in src/http/routes for full request/response schemas. OpenAPI docs are available at /docs in development.

Authentication & Security

  • JWT: the app issues and verifies JWTs using jsonwebtoken and expects JWT_SECRET in the environment. Routes that require authentication use a checkJWTRequest hook.
  • Password hashing: user passwords are hashed with argon2 (see src/db/seed.ts and test factories).

Ensure JWT_SECRET is set in your environment for both development and tests (the test factories will sign tokens during tests).

Testing

  • Test runner: Vitest
  • HTTP assertions: Supertest
  • Factories / fake data: @faker-js/faker (factories are in src/tests/factories)
  • Coverage: configured with @vitest/coverage-v8 (see coverage/ directory generated by test runs)

Recommended local test flow (uses test DB from .env.test):

# start local Postgres (if needed)
docker compose up -d

# run migrations against test DB (pretest does this automatically when using npm test)
npm run pretest

# run tests (script already uses .env.test)
npm test

Note: pretest runs drizzle-kit migrate using .env.test so tests operate against the challenge_test database.

Seeding

Populate dev DB with sample users and courses:

npm run db:seed

Seed uses argon2 for password hashing and Faker for generated data.

Local Postgres (Docker)

  • docker-compose.yml is included to run Postgres locally. The included docker/setup.sql creates the challenge_test database for tests.
  • Default connections used by the repo:
    • Dev DB: postgresql://postgres:postgres@localhost:5432/challenge
    • Test DB: postgresql://postgres:postgres@localhost:5432/challenge_test

Start Postgres:

docker compose up -d

Drizzle Studio

Run Drizzle Studio to inspect DB schema and data:

npm run db:studio

Architecture

sequenceDiagram
  participant C as Client
  participant S as Fastify Server
  participant V as Zod Validator
  participant DB as Drizzle + PostgreSQL

  C->>S: POST /courses {title}
  S->>V: Validate body
  V-->>S: OK or Error 400
  alt valid
    S->>DB: INSERT INTO courses (title)
    DB-->>S: {id}
    S-->>C: 201 {courseId}
  else invalid
    S-->>C: 400
  end

  C->>S: GET /courses
  S->>DB: SELECT id,title FROM courses
  DB-->>S: list
  S-->>C: 200 {courses: [...]} 

  C->>S: GET /courses/:id
  S->>V: Validate param id (uuid)
  V-->>S: OK or Error 400
  alt found
    S->>DB: SELECT * FROM courses WHERE id=...
    DB-->>S: course
    S-->>C: 200 {course}
  else not found
    S-->>C: 404
  end
Loading

Note: GitHub and many Markdown renderers support Mermaid diagrams — if the diagram doesn't render in your viewer, open the file in VS Code with the "Markdown Preview Mermaid Support" extension or view it on GitHub.

OpenAPI / Swagger

  • The project registers OpenAPI (Fastify Swagger) and an alternative UI (@scalar/fastify-api-reference) in development. Docs are served at /docs when NODE_ENV=development.

Deployment notes

  • The app expects DATABASE_URL and JWT_SECRET to be set in the target environment.
  • Long‑running container hosts (Fly, Render, Docker) work well. Serverless platforms (Vercel) require a pooling strategy for Postgres connections.

Troubleshooting

  • Tests writing to the wrong DB: ensure npm test uses .env.test (this project uses dotenv -e .env.test in test and pretest).
  • ERR_UNKNOWN_FILE_EXTENSION: using a Node build that supports TypeScript (Node 22+ in this project) removes the need for experimental flags; otherwise compile with tsc or use ts-node.

License

This project is released under the ISC license (see package.json).