Skip to content

lutfifadlan/issue-server

Repository files navigation

Overview

This document outlines my approach and implementation for the backend of issue management system. It involves building a REST API to manage issue entities, with several key features including CRUD operations, revision tracking, authentication, and comparison functionality.

Prerequisites

Before running the application, ensure you have the following installed:

  • Docker and Docker Compose
  • Node.js (v18 or higher)
  • npm (usually comes with Node.js)
  • Make - For running Makefile commands
    • macOS: Comes pre-installed
    • Linux: Install via package manager (e.g., apt-get install make)
    • Windows: Install via Chocolatey (choco install make) or use Git Bash which includes make
  • Husky - For Git hooks (automatically installed when you run npm install)

Running the Application Locally

The application uses a Makefile to simplify common operations. You can set up everything with a single command:

make setup

This command will:

  1. Start the Docker containers (make up)
  2. Run database migrations (make m-up)
  3. Seed the database with initial data (make db-seed)

IMPORTANT: The database seeding step is crucial as it creates client data required by the API. Many endpoints require a valid X-Client-Id header, which is obtained from these seeded client records. Make sure the seeding process completes successfully.

After completing this setup, the API will be available at http://localhost:8080.

If you need to run the database seed separately (e.g., if you skipped the setup command or need to reset the data):

make db-seed

Alternatively, you can run each step individually if needed:

# Start containers
make up

# Run migrations
make m-up

# Seed database
make db-seed

API Documentation

The API is documented using Swagger. To access the documentation:

  1. Ensure the application is running
  2. Navigate to http://localhost:8080/docs in your web browser
  3. The Swagger UI provides a complete overview of all endpoints, request/response formats, and allows you to test the API directly from the browser

Making Changes to the Application

If you need to modify the application:

  1. Code Structure: The codebase follows a layered architecture with routes, controllers, services, and models
  2. TypeScript: The project uses TypeScript for type safety and better developer experience
  3. Testing: Run tests with make test or npm test
  4. Linting: Ensure code quality with make lint or fix issues with make lint-fix
  5. Git Hooks: The project uses Husky to run pre-commit hooks that automatically check linting and run tests before each commit. The pre-commit hook enforces a minimum of 85% test coverage - commits will be rejected if coverage falls below this threshold

For a complete development workflow:

  1. Make your code changes
  2. Stage your changes with git add
  3. When you commit, Husky will automatically run linting and tests
  4. If the pre-commit checks pass, your commit will proceed
  5. If they fail, fix the issues and try committing again
  6. Restart the application: make rst
  7. Verify your changes work as expected

Setting Up Husky and lint-staged (for Contributors)

I intentionally use lint-staged with Husky as a best practice to enforce code quality standards automatically during the development workflow. This ensures consistent code style and prevents quality issues from being committed.

Husky is automatically installed when you run npm install. If you need to manually set it up:

npm install
npm run prepare

This will install Husky and set up the Git hooks. The pre-commit hook is configured to:

  1. Run lint-staged to automatically fix and format only the files you've changed (not the entire codebase)
  2. Run all tests with coverage reporting
  3. Verify that test coverage is at least 85% (the commit will fail if coverage is below this threshold)

The lint-staged configuration in package.json works as follows:

  • When you commit, it automatically runs ESLint with the --fix flag on your staged TypeScript and JavaScript files
  • It also runs Prettier with the --write flag on your staged TypeScript, JavaScript, JSON, and Markdown files
  • Both tools automatically fix and format your code, and lint-staged automatically re-stages these changes
  • You don't need to run git add again after the tools fix your code

Architecture

I designed the application using a layered architecture pattern that separates concerns and promotes maintainability:

┌─────────────────┐
│     Routes      │  API endpoints and request handling
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   Controllers   │  Request validation and response formatting
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    Services     │  Business logic and data manipulation
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│     Models      │  Data access and database interaction
└─────────────────┘

Key Components

  • Routes: Define API endpoints and apply middleware
  • Controllers: Handle request validation and response formatting
  • Services: Implement business logic and data manipulation
  • Models: Manage data access and database interactions
  • Middleware: Provide cross-cutting concerns like authentication and error handling
  • Validators: Ensure data integrity through validation rules
  • Utils: Reusable helper functions

Implementation Details

Database Schema

The core data model consists of two main entities:

┌────────────────┐       ┌────────────────────┐
│     Issue      │       │   IssueRevision    │
├────────────────┤       ├────────────────────┤
│ id             │       │ id                 │
│ title          │       │ issue_id           │
│ description    │       │ issue_data         │
│ created_by     │◄──────┤ changes            │
│ updated_by     │       │ created_by         │
│ created_at     │       │ created_at         │
│ updated_at     │       └────────────────────┘
└────────────────┘

Authentication Flow

The authentication system uses JWT tokens and client identification:

┌─────────┐                  ┌─────────────┐                 ┌─────────────┐
│ Client  │                  │    API      │                 │  Database   │
└────┬────┘                  └──────┬──────┘                 └──────┬──────┘
     │                              │                               │
     │ POST /auth/signup            │                               │
     │ (email, password, client_id) │                               │
     ├─────────────────────────────►│                               │
     │                              │                               │
     │ POST /auth/login             │                               │
     │ (email, password)            │                               │
     ├─────────────────────────────►│                               │
     │                              │                               │
     │                              │ Validate credentials          │
     │                              ├──────────────────────────────►│
     │                              │                               │
     │                              │ User data                     │
     │                              │◄──────────────────────────────┤
     │                              │                               │
     │ JWT Token                    │                               │
     │◄─────────────────────────────┤                               │
     │                              │                               │
     │ Request with:                │                               │
     │ - Authorization: Bearer JWT  │                               │
     │ - X-Client-ID: client_id     │                               │
     ├─────────────────────────────►│                               │
     │                              │                               │
     │                              │ Verify token & client ID      │
     │                              ├───────────┐                   │
     │                              │           │                   │
     │                              │◄───────────┘                  │
     │                              │                               │
     │                              │ Process request               │
     │                              ├───────────┐                   │
     │                              │           │                   │
     │                              │◄───────────┘                  │
     │                              │                               │
     │ Response                     │                               │
     │◄─────────────────────────────┤                               │
     │                              │                               │

Note: The client_id parameter is optional during signup but is required as an X-Client-Id header for most API endpoints. The database seed creates client records that provide valid client IDs for testing.

Revision Tracking

The revision system tracks all changes to issues:

  1. When an issue is created, an initial revision is stored
  2. Each update generates a new revision with:
    • Complete issue state after the update
    • Only the fields that were changed
    • Timestamp and user information

Revision Comparison

The comparison endpoint provides a detailed diff between two revisions:

┌─────────────────────────────────────────────────────────────┐
│ Revision Comparison                                         │
├─────────────────────────────────────────────────────────────┤
│ ┌───────────────┐        ┌───────────────┐                  │
│ │    Before     │        │     After     │                  │
│ │ (Revision A)  │        │  (Revision B) │                  │
│ └───────┬───────┘        └───────┬───────┘                  │
│         │                        │                          │
│         └──────────┬─────────────┘                          │
│                    ▼                                        │
│ ┌───────────────────────────────────┐                       │
│ │           Changes                 │                       │
│ │ - Field X: "old" → "new"          │                       │
│ │ - Field Y: "removed"              │                       │
│ │ - Field Z: null → "added"         │                       │
│ └───────────────────────────────────┘                       │
│                                                             │
│ ┌───────────────────────────────────┐                       │
│ │      Revision Trail               │                       │
│ │ [Rev A] → [Rev C] → [Rev D] → [Rev B]                     │
│ └───────────────────────────────────┘                       │
└─────────────────────────────────────────────────────────────┘

API Endpoints

Core Endpoints

Method Endpoint Description
POST /v1/auth/login Authenticate and receive JWT token
POST /v1/auth/signup Register a new user
POST /v1/auth/refresh-token Refresh JWT token
POST /v1/issues Create a new issue
GET /v1/issues List all issues (with optional filters)
GET /v1/issues/:id Get a specific issue by ID
PUT /v1/issues/:id Update an issue
DELETE /v1/issues/:id Delete an issue
GET /v1/issues/:id/revisions Get all revisions for an issue
GET /v1/issues/:id/compare Compare two revisions of an issue
GET /v1/users/me Get current user
PUT /v1/users/me Update current user
DELETE /v1/users/me Delete current user
GET /v1/clients List all clients
GET /v1/clients/:id Get a specific client by ID
POST /v1/clients Create a new client
PUT /v1/clients/:id Update a client
DELETE /v1/clients/:id Delete a client

Utility Endpoints

I intentionally added the following utility endpoints to enhance the API's usability and monitoring capabilities:

Method Endpoint Description
GET / Root endpoint that returns basic API information
GET /health Health check endpoint for monitoring API status
GET /docs Swagger documentation endpoint for interactive API exploration

Technical Decisions and Trade-offs

TypeScript

I chose to implement the solution in TypeScript to provide:

  • Type safety and better IDE support
  • Self-documenting code through interfaces and types
  • Easier maintenance and refactoring

API Versioning

I intentionally implemented API versioning (e.g., /v1/issues, /v1/auth) as a best practice to:

  • Ensure backward compatibility when introducing breaking changes
  • Allow for the evolution of the API without disrupting existing clients
  • Provide a clear migration path for clients when new versions are released
  • Support multiple API versions simultaneously during transition periods

Authentication

For authentication, I implemented:

  • JWT-based authentication for stateless operation
  • Client ID validation through the X-Client-ID header
  • User tracking for all database operations

Error Handling

I implemented a centralized error handling approach:

  • Consistent error response format
  • Detailed error codes and messages
  • Proper HTTP status codes
  • Error logging for debugging

Testing

The codebase includes:

  • Unit tests for individual components
  • Integration tests for API endpoints
  • Mock services for isolated testing
  • End-to-end (e2e) tests intentionally added using supertest for comprehensive API validation

Pagination and Filtering

The GET /issues endpoint supports:

  • Filtering by creator and updater
  • Date range filtering for creation and update times
  • Pagination for large result sets with the following parameters:
    • page: Page number (default: 1)
    • limit: Number of items per page (default: 10)
    • Response includes pagination metadata (total count, current page, page size, total pages)

Challenges and Solutions

Revision Storage

Challenge: Efficiently storing and retrieving issue revisions.

Solution: I used a separate table for revisions with a JSON column for storing the complete issue state and changes. This approach provides flexibility while maintaining good query performance.

Revision Comparison Implementation

Challenge: Generating meaningful comparisons between arbitrary revisions.

Solution: I implemented a diff algorithm that:

  1. Identifies added, removed, and modified fields
  2. Handles both forward and backward comparisons
  3. Generates a complete revision trail between the compared revisions

Authentication Security

Challenge: Implementing secure authentication without compromising usability.

Solution:

  • JWT tokens with appropriate expiration
  • Client ID validation for additional security
  • Secure password storage with bcrypt
  • Environment-based configuration for secrets

Deployment and CI/CD

API Deployment

The API has been deployed and is accessible at:

The deployment is fully automated with CI/CD:

  • The API is automatically deployed to a VPS when changes are pushed to the solution/mochamad-lutfi-fadlan branch
  • The CI/CD pipeline handles building, testing, and deploying the application

Frontend Application

A frontend application has been built to interact with the API:

  • Technology Stack: React, TypeScript, Next.js, Tailwind CSS
  • Production URL: https://issue-dashboard.lutfifadlan.com
  • CI/CD: Automatically deployed to Vercel when changes are pushed to the main branch

The frontend provides a user-friendly interface for interacting with all the API features, including issue management, revision tracking, and comparison functionality.

Future Improvements

Given more time, I would enhance the solution with:

  1. Caching: Implement Redis caching for frequently accessed data
  2. Rate Limiting: Add more sophisticated rate limiting based on user and client
  3. Metrics: Add Prometheus metrics for monitoring API usage and performance
  4. Webhooks: Implement webhooks for issue events to enable integrations
  5. Soft Delete: Add soft delete functionality to preserve issue history
  6. Advanced Filtering: Implement full-text search and more complex filtering options

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors