A REST API for managing professional development, courses, learning modules, job opportunities, and job applications.
The project was built with a strong focus on layered architecture, secure authentication, data validation, automated testing, interactive documentation, Docker-based local execution, production deployment on Vercel, and Prisma Postgres.
- API: https://career-tracker-api.vercel.app
- Swagger Documentation: https://career-tracker-api.vercel.app/api-docs
- API Health: https://career-tracker-api.vercel.app/health
- Database Health: https://career-tracker-api.vercel.app/db-health
- Production Database: Prisma Postgres
- Deployment Platform: Vercel
- User registration and authentication with JWT
- Authenticated user profile retrieval
- Complete course management
- Course module management
- Job opportunity management
- Job searching, filtering, and pagination
- Job application management
- Automatic synchronization between applications and job status
- Per-user resource ownership protection
- Data validation with Zod
- Automated integration testing
- Interactive Swagger/OpenAPI documentation
- PostgreSQL and API execution with Docker Compose
- Automatic Prisma migrations on startup
- Node.js
- TypeScript
- Express
- PostgreSQL
- Prisma ORM
- JWT
- bcryptjs
- Zod
- Vitest
- Supertest
- Swagger / OpenAPI
- Docker
- Docker Compose
- pnpm
The API follows a layered architecture:
Request
↓
Routes
↓
Validation Middleware
↓
Controllers
↓
Services
↓
Repositories
↓
Prisma ORM
↓
PostgreSQL
- Routes: define endpoints and middlewares.
- Validation: validates request bodies and query parameters.
- Controllers: receive HTTP requests and return HTTP responses.
- Services: contain business rules.
- Repositories: execute database operations.
- Prisma: communicates with PostgreSQL.
career-tracker-api/
├── prisma/
│ ├── migrations/
│ └── schema.prisma
├── src/
│ ├── controllers/
│ ├── docs/
│ ├── errors/
│ ├── generated/
│ ├── lib/
│ ├── middlewares/
│ ├── repositories/
│ ├── routes/
│ ├── services/
│ ├── validations/
│ ├── app.ts
│ └── server.ts
├── tests/
│ ├── helpers/
│ ├── integration/
│ └── setup.ts
├── .dockerignore
├── .env.example
├── .env.test.example
├── compose.yaml
├── Dockerfile
├── Makefile
├── package.json
├── pnpm-lock.yaml
├── prisma.config.ts
├── tsconfig.json
└── vitest.config.ts
User
├── Courses
│ └── Course Modules
└── Jobs
└── Application
- Every resource belongs to an authenticated user.
- Users cannot access or modify resources owned by other users.
- A job can have at most one application.
- Creating an application automatically changes the related job status to
APPLIED. - Deleting an application resets the related job status to
SAVED. - Related operations are executed with Prisma transactions.
For Docker-based execution:
- Docker
- Docker Compose
- Make
For local execution:
- Node.js 22+
- pnpm 11+
- PostgreSQL
Create a .env file in the project root:
PORT=3000
NODE_ENV=development
POSTGRES_USER=career_user
POSTGRES_PASSWORD=career_pass
POSTGRES_DB=career_db
POSTGRES_PORT=5432
DATABASE_URL=postgresql://career_user:career_pass@localhost:5432/career_db?schema=public
JWT_SECRET=replace_with_a_secure_random_secret
JWT_EXPIRES_IN=1dNever commit the .env file.
An example file is available at:
.env.example
The recommended way to run the project is with Docker Compose.
make rebuildOr directly:
docker compose up --build -dThis command:
- builds the API image;
- starts PostgreSQL;
- waits for the database to become healthy;
- applies Prisma migrations;
- starts the API.
make psExpected:
career_tracker_database healthy
career_tracker_api healthy
make logsAPI logs:
make logs-apiDatabase logs:
make logs-dbmake downmake cleanWarning:
make cleanalso deletes the PostgreSQL data volume.
make help Show available commands
make up Start containers
make build Build Docker images
make rebuild Rebuild and start containers
make down Stop containers
make restart Restart containers
make logs Show all logs
make logs-api Show API logs
make logs-db Show PostgreSQL logs
make ps Show container status
make test Run build and tests
make clean Remove containers, networks, and database volume
Install dependencies:
pnpm installGenerate Prisma Client:
pnpm prisma generateApply migrations:
pnpm prisma migrate deployStart in development mode:
pnpm devThe API will be available at:
http://localhost:3000
pnpm build
pnpm startThe TypeScript source code is compiled into the dist directory.
The project includes integration tests for:
- user registration and login;
- JWT authentication;
- protected routes;
- courses;
- course modules;
- jobs;
- searching and filtering;
- pagination;
- resource ownership;
- applications;
- duplicate application prevention;
- transactions;
- automatic job status synchronization.
pnpm test:migratepnpm testpnpm test:watchpnpm test:coverageInteractive API documentation is available at:
http://localhost:3000/api-docs
The OpenAPI JSON specification is available at:
http://localhost:3000/openapi.json
Using Swagger UI, you can:
- register a user;
- log in;
- copy the JWT token;
- click
Authorize; - test protected endpoints.
GET /healthExample response:
{
"status": "ok",
"message": "Career Tracker API is running"
}GET /db-healthExample response:
{
"status": "ok",
"database": "connected",
"usersCount": 0
}POST /auth/register
POST /auth/loginGET /users/mePOST /courses
GET /courses
GET /courses/:id
PATCH /courses/:id
DELETE /courses/:idPOST /courses/:courseId/modules
GET /courses/:courseId/modules
GET /course-modules/:id
PATCH /course-modules/:id
DELETE /course-modules/:idPOST /jobs
GET /jobs
GET /jobs/:id
PATCH /jobs/:id
DELETE /jobs/:idFilter example:
GET /jobs?status=applied&remote=true&q=backend&page=1&limit=10POST /jobs/:jobId/application
GET /applications
GET /applications/:id
PATCH /applications/:id
DELETE /applications/:idNOT_STARTED
IN_PROGRESS
COMPLETED
SAVED
APPLIED
INTERVIEW
REJECTED
OFFER
- Passwords are stored using bcrypt hashes.
- Authentication is based on JWT.
- Private routes are protected by middleware.
- Resource ownership is validated.
- Request data is validated with Zod.
- Sensitive variables are kept outside Git.
- The test database is separate from the main database.
- Password hashes are never exposed in responses.
- Transactions prevent partial updates.
docker compose exec database \
psql -U career_user -d career_db -c "\dt"Expected tables:
_prisma_migrations
users
courses
course_modules
jobs
applications
Mauricio Morais Zage
Backend Developer focused on Node.js, TypeScript, PostgreSQL, Prisma, REST APIs, automated testing, and Docker.