Skip to content

rafael-vaz/questify-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Questify API

Node.js TypeScript NestJS Prisma PostgreSQL Redis JWT Vitest Docker License

📖 Project Overview

Questify is a modular Q&A backend API built with NestJS and TypeScript for collaborative learning and discussion workflows.

It provides JWT-based authentication, role-based permissions (STUDENT and INSTRUCTOR), and complete management of questions, answers, comments, attachments, and notifications.

The platform also includes domain-event-driven notifications and Redis caching to improve performance while keeping question data consistent after updates.

🛠️ Tech Stack

Category Technology
Runtime Node.js
Language TypeScript
Framework NestJS
Validation Zod, custom Zod pipe
Authentication Passport JWT, @nestjs/jwt
Authorization Roles decorator + RolesGuard
Database PostgreSQL
ORM / Data Access Prisma Client + Prisma PG Adapter
Caching Redis (ioredis), cache abstraction layer
Eventing @nestjs/event-emitter
Storage integration Firebase Admin SDK (default), fake provider for tests/local scenarios
Testing Vitest, Supertest, Nest Testing utilities
Code quality ESLint, Prettier
Build tooling Nest CLI, SWC plugin, tsconfig paths
Containerization Docker Compose

📂 Project Structure

.
├── client.http
├── docker-compose.yml
├── prisma/
│   ├── schema.prisma
│   └── migrations/
├── src/
│   ├── app.module.ts
│   ├── main.ts
│   ├── cache/
│   │   ├── application/
│   │   ├── domain/
│   │   └── infra/
│   ├── database/
│   │   └── prisma/
│   ├── env/
│   ├── middlewares/
│   ├── modules/
│   │   ├── answers/
│   │   ├── attachments/
│   │   ├── comments/
│   │   ├── notifications/
│   │   ├── questions/
│   │   ├── sessions/
│   │   └── users/
│   ├── routes/
│   └── utils/
├── test/
│   ├── e2e/
│   ├── integration/
│   ├── unit/
│   ├── setup/
│   ├── factories/
│   └── mocks/
└── vitest.*.config.ts

Directory responsibilities

Directory Responsibility
src/modules/* Business modules organized by bounded context (application, domain, infra, presentation)
src/cache Cache contract, key strategy, and Redis implementation
src/database Global database wiring and Prisma service
src/env Environment schema, parsing, and typed env service
src/routes Aggregates all HTTP controllers into app routes
prisma Data model and migration history
test/unit Isolated unit tests (use cases, entities, listeners)
test/integration Repository and persistence integration tests
test/e2e Full HTTP and module integration scenarios

⚙️ Prerequisites

Requirement Details
Node.js TODO: Add information
npm TODO: Add information
PostgreSQL Required (local instance or Docker)
Redis Required for runtime cache behavior
Docker (optional) Recommended for local PostgreSQL and Redis via Compose
Firebase credentials (optional) Required when using ATTACHMENTS_STORAGE_PROVIDER=firebase

🚀 Installation

git clone https://github.com/rafael-vaz/questify
cd questify

Install dependencies:

npm install

Alternative (if your team uses Yarn):

yarn install

Start local infrastructure with Docker Compose:

docker compose up -d

🔐 Environment Variables

The application validates environment variables at startup using Zod.

Variables reference

Variable Description Required Default
NODE_ENV Application environment (development, test, production) Yes development
PORT HTTP server port No 333
DATABASE_URL PostgreSQL connection string for runtime Yes -
DATABASE_URL_TEST PostgreSQL connection string for test setup Recommended -
REDIS_HOST Redis host (used when REDIS_URL is not provided) No localhost
REDIS_PORT Redis port (used when REDIS_URL is not provided) No 6379
REDIS_PASSWORD Redis password (if configured) No empty
REDIS_URL Redis connection string No -
REDIS_URL_TEST Redis connection string for test environment separation Recommended -
QUESTION_CACHE_TTL_SECONDS TTL for question-by-slug cache entries No 300
JWT_PRIVATE_KEY_BASE64 Base64-encoded private key for JWT signing Yes -
JWT_PUBLIC_KEY_BASE64 Base64-encoded public key for JWT verification Yes -
ATTACHMENTS_STORAGE_PROVIDER Storage provider (firebase or fake) No firebase
ATTACHMENTS_MAX_FILE_SIZE_BYTES Maximum upload size in bytes No 5242880
FIREBASE_PROJECT_ID Firebase project identifier Conditional -
FIREBASE_CLIENT_EMAIL Firebase service account email Conditional -
FIREBASE_PRIVATE_KEY_BASE64 Firebase private key in Base64 Conditional -
FIREBASE_STORAGE_BUCKET Firebase bucket name Conditional -

Complete .env.example

NODE_ENV=dev
PORT=3333
JWT_PRIVATE_KEY_BASE64="your-jwt-private-key-base64"
JWT_PUBLIC_KEY_BASE64="your-jwt-public-key-base64"
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/questify?schema=public"
DATABASE_URL_TEST="postgresql://postgres:postgres@localhost:5432/questify_test?schema=public"
REDIS_HOST="localhost"
REDIS_PORT=6379
REDIS_PASSWORD=""
REDIS_URL="redis://localhost:6379/0"
REDIS_URL_TEST="redis://localhost:6379/1"
ATTACHMENTS_STORAGE_PROVIDER="firebase"
ATTACHMENTS_MAX_FILE_SIZE_BYTES=5242880
FIREBASE_PROJECT_ID="my-project-id"
FIREBASE_CLIENT_EMAIL="firebase-adminsdk@example-project.iam.gserviceaccount.com"
FIREBASE_PRIVATE_KEY_BASE64="ZXhhbXBsZS1wcml2YXRlLWtleS1lbmNvZGVkLWluLWJhc2U2NA=="
FIREBASE_STORAGE_BUCKET="my-project-id.appspot.com"

▶️ Running the Application

Mode Command
Development npm run start
Watch mode npm run start:dev
Debug mode npm run start:debug
Build npm run build
Production npm run start:prod

📡 API Endpoints

Base URL (default): http://localhost:3333

Users

Method Endpoint Description Authentication
POST /users Create a new user account No

Sessions

Method Endpoint Description Authentication
POST /sessions Authenticate user and issue JWT access token No

Questions

Method Endpoint Description Authentication
POST /questions Create a question Yes
GET /questions?page=1&query=<text> List recent questions with optional search Yes
GET /questions/:slug Fetch a question by slug (Redis cached) Yes
PUT /questions/:id Edit question (instructor-only) Yes
DELETE /questions/:id Delete question (instructor-only) Yes

Answers

Method Endpoint Description Authentication
POST /questions/:questionId/answers Add answer to question Yes
GET /questions/:questionId/answers List answers for question Yes
PUT /answers/:id Edit answer (instructor-only) Yes
DELETE /answers/:id Delete answer (instructor-only) Yes
PATCH /answers/:answerId/choose-as-best Choose best answer (instructor-only) Yes

Comments

Method Endpoint Description Authentication
POST /questions/:questionId/comments Add comment to question Yes
GET /questions/:questionId/comments List question comments Yes
DELETE /questions/comments/:id Delete question comment (instructor-only) Yes
POST /answers/:answerId/comments Add comment to answer Yes
GET /answers/:answerId/comments List answer comments Yes
DELETE /answers/comments/:id Delete answer comment (instructor-only) Yes

Attachments

Method Endpoint Description Authentication
POST /attachments Upload attachment (multipart/form-data) for question or answer Yes

Notifications

Method Endpoint Description Authentication
PATCH /notifications/:notificationId/read Mark notification as read Yes

Notification creation is event-driven and internal (no public create endpoint).

🔒 Authentication & Authorization

Authentication

  • Strategy: JWT bearer token
  • Guard: JwtAuthGuard (Passport strategy jwt)
  • Token emission: POST /sessions
  • Token usage: Authorization: Bearer <access_token>

Authorization

  • Roles are modeled as STUDENT and INSTRUCTOR
  • Roles decorator + RolesGuard enforce role-based restrictions
  • Instructor-only operations include editing/deleting questions and answers, deleting comments, and selecting best answers

🗄️ Database

Topic Details
Main database PostgreSQL
ORM Prisma
Prisma schema prisma/schema.prisma
Migration files prisma/migrations/*
Connection DATABASE_URL
Test database DATABASE_URL_TEST (separate from development DB)

Core entities

  • User
  • Question
  • Answer
  • Comment
  • Attachment
  • Notification

Migrations and Prisma commands

npx prisma generate
npx prisma migrate deploy
npx prisma migrate dev

Seeders

TODO: Add information

🧪 Testing

This project uses Vitest with three layers:

  • Unit tests: business logic and isolated behavior
  • Integration tests: persistence/repository interactions
  • E2E tests: full HTTP flow through Nest modules

Commands

npm run test
npm run test:unit
npm run test:integration
npm run test:e2e
npm run test:cov # TODO: Add information
npm run test:watch
npm run test:coverage
npm run test:ui

Note: The script available in this repository is test:coverage (not test:cov).

📜 Available Scripts

Script Description
npm run build Compile NestJS application to dist
npm run format Format source and test files with Prettier
npm run start Start application
npm run start:dev Start application in watch mode
npm run start:debug Start application in debug + watch mode
npm run start:prod Run compiled application from dist/main
npm run lint Run ESLint with autofix
npm run test Run full test suite (unit + integration + e2e)
npm run test:unit Run unit tests
npm run test:integration Run integration tests
npm run test:e2e Run end-to-end tests
npm run test:watch Run unit tests in watch mode
npm run test:coverage Run unit tests with coverage report
npm run test:ui Run Vitest UI for unit tests

🚢 Deployment

Production flow

  1. Set production environment variables (NODE_ENV=production, database, Redis, JWT, storage credentials).
  2. Build the project:
npm ci
npm run build
  1. Apply database migrations:
npx prisma migrate deploy
  1. Start the application:
npm run start:prod

Production considerations

  • Use a dedicated PostgreSQL instance and separate test database.
  • Use a dedicated Redis instance and separate logical DB/index for non-production contexts.
  • Manage secrets using a secure secret manager.
  • Ensure TLS termination at ingress/reverse proxy.
  • Add monitoring/logging and health checks.
  • Configure CORS and rate limiting according to your deployment requirements.

🤝 Contributing

Branch naming

  • Feature: feature/<short-description>
  • Fix: fix/<short-description>
  • Chore: chore/<short-description>

Commit convention

Recommended: Conventional Commits

  • feat: add cache invalidation listener
  • fix: handle notification race condition in e2e
  • docs: update environment variable table

Pull Request flow

  1. Create a branch from the main development branch.
  2. Implement changes with tests.
  3. Run lint, build, and relevant test commands locally.
  4. Open PR with clear context, screenshots/logs if applicable, and testing evidence.
  5. Request review and address feedback before merge.

About

Scalable Q&A backend built with NestJS, Prisma, PostgreSQL, Redis, and JWT, featuring RBAC, notifications, and attachment management.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors