Skip to content

Dead-WaRior/acquisition

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

27 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Acquisition API

Node.js Express PostgreSQL License Docker

A RESTful user authentication and management API built with Node.js, Express 5, and PostgreSQL (via Neon). It provides secure user registration, login, logout, and full CRUD user management with role-based access control (RBAC).

Summary

Acquisition API is a production-ready backend service that handles the complete user lifecycle β€” from account creation with hashed passwords to JWT-based authentication and role-protected endpoints. Admins can manage all users while regular users can only access and modify their own accounts. The API is secured with Helmet, CORS, Arcjet rate limiting, and structured logging via Winston. It ships with Docker support for both local development (using Neon Local ephemeral database branches) and production deployments.

Features

  • User Authentication β€” Sign up, sign in, and sign out with JWT tokens stored in HTTP-only cookies
  • Password Hashing β€” Secure password storage using bcrypt
  • Role-Based Access Control β€” admin and user roles with middleware-enforced permissions
  • Soft Delete β€” Users are soft-deleted via a deleted_at timestamp instead of being permanently removed
  • Pagination β€” Paginated user listing with configurable page and limit query parameters
  • Input Validation β€” Request validation powered by Zod schemas
  • Configurable CORS β€” Origin allowlist controlled via the CORS_ORIGIN environment variable
  • Security Hardening β€” Helmet for HTTP headers, Arcjet for bot detection, shield protection, and role-based rate limiting (admin: 20/min, user: 10/min, guest: 5/min)
  • Error Handling β€” Centralized 404 handler and global error handler with environment-aware error messages
  • Structured Logging β€” Winston logger with file and console transports
  • Database Migrations β€” Drizzle ORM with migration generation and push support
  • Docker Support β€” Multi-stage Dockerfile with development and production Compose configurations
  • CI/CD β€” GitHub Actions workflows for tests, linting, formatting, and Docker image builds

Tech Stack

Category Technology
Runtime Node.js 20
Framework Express 5
Language JavaScript (ES Modules)
Database PostgreSQL via Neon
ORM Drizzle ORM
Authentication JSON Web Tokens (jsonwebtoken) + bcrypt
Validation Zod
Security Helmet, CORS, Arcjet (rate limiting, bot detection, shield)
Logging Winston + Morgan
Testing Jest + Supertest
Code Quality ESLint + Prettier
Containerization Docker + Docker Compose

Architecture Overview

The diagram below shows how the major components of the system interact:

graph TD
    Client["🌐 Client\n(Browser / Mobile / Postman)"]

    subgraph API["Acquisition API  (Express 5)"]
        direction TB
        MW["Security Middleware\n(Helmet Β· CORS Β· Arcjet Β· Morgan)"]
        Router["Router\n/api/auth Β· /api/users Β· /api/acquisition"]
        AuthMW["Auth Middleware\n(JWT verify Β· Role check)"]
        Controllers["Controllers\n(auth Β· user)"]
        Services["Services\n(auth Β· user)"]
        Utils["Utilities\n(jwt Β· cookies Β· format)"]
    end

    DB[("🐘 PostgreSQL\n(Neon Cloud)")]
    Logger["πŸ“‹ Winston Logger\n(console + file)"]

    Client -->|HTTP Request| MW
    MW --> Router
    Router -->|protected routes| AuthMW
    AuthMW --> Controllers
    Router -->|public routes| Controllers
    Controllers --> Services
    Services --> Utils
    Services -->|Drizzle ORM| DB
    Controllers -->|response| Client
    API -->|logs| Logger
Loading

Request Lifecycle

Every HTTP request passes through a layered middleware pipeline before a response is returned:

sequenceDiagram
    participant C as Client
    participant H as Helmet / CORS
    participant A as Arcjet Security
    participant V as Zod Validation
    participant AM as Auth Middleware
    participant Ctrl as Controller
    participant Svc as Service
    participant DB as PostgreSQL

    C->>H: HTTP Request
    H->>A: Headers sanitized
    A-->>C: 429 Too Many Requests (rate-limited)
    A->>V: Request allowed
    V-->>C: 400 Bad Request (invalid body)
    V->>AM: Body validated
    AM-->>C: 401 Unauthorized (missing/invalid JWT)
    AM-->>C: 403 Forbidden (insufficient role)
    AM->>Ctrl: Token decoded β†’ req.user set
    Ctrl->>Svc: Call service method
    Svc->>DB: Query via Drizzle ORM
    DB-->>Svc: Result
    Svc-->>Ctrl: Processed data
    Ctrl-->>C: JSON Response (2xx / 4xx / 5xx)
Loading

Authentication Flow

Sign-Up

flowchart TD
    A([POST /api/auth/sign-up]) --> B{Body valid?\nZod schema}
    B -- No --> C[400 Bad Request]
    B -- Yes --> D{Email already\nregistered?}
    D -- Yes --> E[409 Conflict]
    D -- No --> F[Hash password\nbcrypt Γ— 10 rounds]
    F --> G[INSERT user row\nDrizzle ORM]
    G --> H[Sign JWT token]
    H --> I[Set HTTP-only cookie]
    I --> J[201 Created + user data]
Loading

Sign-In

flowchart TD
    A([POST /api/auth/sign-in]) --> B{Body valid?\nZod schema}
    B -- No --> C[400 Bad Request]
    B -- Yes --> D[Fetch user by email]
    D --> E{User found?}
    E -- No --> F[401 Unauthorized]
    E -- Yes --> G{Password matches?\nbcrypt.compare}
    G -- No --> F
    G -- Yes --> H[Sign JWT token]
    H --> I[Set HTTP-only cookie]
    I --> J[200 OK + user data]
Loading

Role-Based Access Control

graph LR
    subgraph Roles
        Admin["πŸ‘‘ admin"]
        User["πŸ‘€ user"]
        Guest["πŸ”“ guest\n(unauthenticated)"]
    end

    subgraph Endpoints
        SignUp["POST /api/auth/sign-up"]
        SignIn["POST /api/auth/sign-in"]
        SignOut["POST /api/auth/sign-out"]
        ListUsers["GET /api/users/"]
        GetUser["GET /api/users/:id"]
        UpdateUser["PUT /api/users/:id"]
        DeleteUser["DELETE /api/users/:id"]
    end

    Guest -->|allowed| SignUp
    Guest -->|allowed| SignIn
    Admin -->|allowed| SignOut
    User  -->|allowed| SignOut
    Admin -->|allowed| ListUsers
    Admin -->|allowed| GetUser
    User  -->|own ID only| GetUser
    Admin -->|allowed| UpdateUser
    User  -->|own ID only| UpdateUser
    Admin -->|allowed| DeleteUser
    User  -->|own ID only| DeleteUser
Loading

Database Schema

The application uses a single users table managed by Drizzle ORM:

erDiagram
    USERS {
        serial      id          PK "Auto-increment primary key"
        varchar255  name            "Full name β€” not null"
        varchar255  email           "Unique email β€” not null"
        varchar255  password        "bcrypt hash β€” not null"
        varchar50   role            "admin | user β€” default user"
        timestamp   created_at      "Row creation time"
        timestamp   updated_at      "Last update time"
        timestamp   deleted_at      "NULL = active (soft delete)"
    }
Loading
Column Type Constraints
id serial Primary key
name varchar(255) Not null
email varchar(255) Not null, unique
password varchar(255) Not null (hashed)
role varchar(50) Not null, default user
created_at timestamp Not null, default now
updated_at timestamp Not null, default now
deleted_at timestamp Nullable (soft delete)

CI/CD Pipeline

flowchart LR
    Push["git push / PR"] --> T["βœ… tests.yml\nJest + Supertest"]
    Push --> L["πŸ” lint-and-format.yml\nESLint + Prettier"]
    Push --> D["🐳 docker-build-and-push.yml\nBuild & push image"]

    T -->|pass| Merge["Merge ready"]
    L -->|pass| Merge
    D -->|pass| Merge
Loading
Workflow Trigger Description
tests.yml Push / PR Runs the test suite
lint-and-format.yml Push / PR Checks ESLint and Prettier formatting
docker-build-and-push.yml Push / PR Builds and pushes Docker images

Project Structure

acquisition/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ index.js                  # Entry point β€” starts the server
β”‚   β”œβ”€β”€ app.js                    # Express app setup (middleware, routes)
β”‚   β”œβ”€β”€ server.js                 # Alternate entry point (no dotenv)
β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”œβ”€β”€ database.js           # Drizzle + Neon database connection
β”‚   β”‚   β”œβ”€β”€ logger.js             # Winston logger configuration
β”‚   β”‚   └── arcjet.js             # Arcjet rate limiting configuration
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   └── user.model.js         # User table schema (Drizzle)
β”‚   β”œβ”€β”€ controllers/
β”‚   β”‚   β”œβ”€β”€ auth.controller.js    # Sign-up, sign-in, sign-out handlers
β”‚   β”‚   └── user.controller.js    # User CRUD handlers
β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”œβ”€β”€ Auth.routes.js        # Authentication routes
β”‚   β”‚   └── user.routes.js        # User management routes
β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”œβ”€β”€ auth.service.js       # Authentication business logic
β”‚   β”‚   └── user.service.js       # User data operations
β”‚   β”œβ”€β”€ middleware/
β”‚   β”‚   β”œβ”€β”€ auth.middleware.js    # JWT verification and role checking
β”‚   β”‚   └── security.middleware.js# Security enforcement
β”‚   β”œβ”€β”€ validations/
β”‚   β”‚   β”œβ”€β”€ auth.validation.js    # Zod schemas for auth endpoints
β”‚   β”‚   └── users.validation.js   # Zod schemas for user endpoints
β”‚   └── utils/
β”‚       β”œβ”€β”€ jwt.js                # JWT sign and verify helpers
β”‚       β”œβ”€β”€ cookies.js            # Cookie set and clear helpers
β”‚       └── format.js             # Validation error formatting
β”œβ”€β”€ test/
β”‚   └── app.test.js               # API integration tests
β”œβ”€β”€ drizzle/                      # Generated database migrations
β”œβ”€β”€ scripts/                      # Docker and deployment scripts
β”œβ”€β”€ Dockerfile                    # Multi-stage production image
β”œβ”€β”€ docker-compose.dev.yml        # Development environment (Neon Local)
β”œβ”€β”€ docker-compose.prod.yml       # Production environment (Neon Cloud)
β”œβ”€β”€ drizzle.config.js             # Drizzle Kit configuration
β”œβ”€β”€ jest.config.mjs               # Jest test configuration
β”œβ”€β”€ eslint.config.js              # ESLint rules
β”œβ”€β”€ .prettierrc                   # Prettier formatting rules
β”œβ”€β”€ DOCKER.md                     # Detailed Docker setup guide
└── package.json                  # Dependencies and npm scripts

Prerequisites

  • Node.js v20 or later
  • npm
  • A Neon PostgreSQL database (or any PostgreSQL instance)
  • Docker and Docker Compose (optional, for containerized setup)

Getting Started

1. Clone the repository

git clone https://github.com/Dead-WaRior/acquisition.git
cd acquisition

2. Install dependencies

npm install

3. Configure environment variables

Create a .env file in the project root:

PORT=3000
NODE_ENV=development
LOG_LEVEL=info

DATABASE_URL=postgresql://<user>:<password>@<host>/<database>?sslmode=require
ARCJET_KEY=<your-arcjet-key>
JWT_SECRET=<your-jwt-secret>
CORS_ORIGIN=http://localhost:3000

4. Run database migrations

npm run db:push

5. Start the development server

npm run dev

The API will be available at http://localhost:3000.

Environment Variables

Variable Description Default
PORT Port the server listens on 3000
NODE_ENV Environment (development/production) development
LOG_LEVEL Winston log level info
DATABASE_URL PostgreSQL connection string β€”
ARCJET_KEY Arcjet API key for rate limiting β€”
JWT_SECRET Secret key for signing JWT tokens β€”
CORS_ORIGIN Comma-separated list of allowed origins β€” (allow all when empty)

For Docker-specific environment variables, see DOCKER.md.

API Endpoints

Health & Status

Method Endpoint Description
GET / Welcome message
GET /health Health check with uptime
GET /api API status

Authentication

Method Endpoint Description
POST /api/auth/sign-up Register a new user
POST /api/auth/sign-in Log in and receive a JWT
POST /api/auth/sign-out Log out and clear the token

User Management (Protected)

Method Endpoint Access Description
GET /api/users/ Admin only Fetch all users (supports ?page=1&limit=10)
GET /api/users/:id Admin or self Get user by ID
PUT /api/users/:id Admin or self Update user
DELETE /api/users/:id Admin or self Soft-delete user

Protected endpoints require a valid JWT token sent via an HTTP-only cookie or an Authorization: Bearer <token> header.

Scripts

Command Description
npm run dev Start the server in watch mode
npm start Start the server
npm test Run tests with coverage
npm run lint Check code with ESLint
npm run lint:fix Auto-fix ESLint issues
npm run format Format code with Prettier
npm run format:check Check code formatting
npm run db:generate Generate database migrations
npm run db:push Apply database migrations
npm run db:studio Open Drizzle Studio (database GUI)
npm run dev:docker Start development Docker environment
npm run prod:docker Start production Docker environment

Docker

The project ships with Docker support for both development and production. See DOCKER.md for the full guide.

Quick start (development):

docker compose -f docker-compose.dev.yml up --build

Quick start (production):

docker compose -f docker-compose.prod.yml up --build -d

Testing

Tests are written with Jest and Supertest:

npm test

This runs the test suite and generates a coverage report.

License

This project is licensed under the ISC License.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors