word-counter-api-docs/architecture/tad.md

5.6 KiB

Tech Architecture Document

Stack

Layer Technology Version / Notes
Runtime Node.js LTS (≥ 20.x)
Language TypeScript ≥ 5.x, compiled to CommonJS or ESM for execution
Framework Express.js ≥ 4.x
Test runner Jest or Vitest Unit tests for counting logic
Build tooling tsc (TypeScript compiler) No bundler required
Package manager npm Standard npm install && npm start workflow

Components

1. HTTP Server (src/server.ts)

Bootstraps the Express application, registers middleware (JSON body-parser, error handler), mounts routes, and starts listening on process.env.PORT (default 3000).

2. Router (src/routes/count.ts, src/routes/health.ts)

Declares the two route handlers:

  • POST /count — validates the request body, calls the counting service, and returns the result.
  • GET /health — returns { "status": "ok" } with HTTP 200.

3. Counting Service (src/services/counter.ts)

Pure functions with no side-effects:

  • countWords(text: string): number — trims the input, splits on /\s+/, filters empty tokens, returns the token count.
  • countCharacters(text: string): number — returns text.length (UTF-16 code units, spaces included).

4. Validation Middleware (src/middleware/validateCount.ts)

Express middleware applied to POST /count that checks:

  1. Body is present and Content-Type: application/json was parsed successfully.
  2. text field exists.
  3. text is of type string. Returns HTTP 400 with a descriptive { "error": string } payload on any violation.

5. Error Handler (src/middleware/errorHandler.ts)

Global Express error-handling middleware (four-argument signature) that catches any unhandled errors — including JSON parse errors emitted by express.json() — and returns a 400 response with a descriptive message.

6. Unit Tests (src/__tests__/counter.test.ts)

Tests for countWords and countCharacters covering: normal input, multiple/consecutive whitespace, leading/trailing whitespace, empty string, and character count including spaces.

Data Model

No persistence layer exists. All data is transient per request.

Request — POST /count

{
  "text": "hello world"
}

Response — POST /count (HTTP 200)

{
  "words": 2,
  "characters": 11
}

Response — GET /health (HTTP 200)

{
  "status": "ok"
}

Error Response (HTTP 400)

{
  "error": "<descriptive message>"
}

Integrations

None. The service is fully self-contained with no external APIs, databases, message queues, or third-party services.

Architecture Diagram

graph TD
    Client["HTTP Client\n(curl / test suite / browser)"]

    subgraph "WCNT API (Node.js / Express)"
        Server["Express App\nsrc/server.ts"]
        HealthRoute["GET /health\nsrc/routes/health.ts"]
        CountRoute["POST /count\nsrc/routes/count.ts"]
        ValidateMW["Validation Middleware\nsrc/middleware/validateCount.ts"]
        ErrMW["Error Handler\nsrc/middleware/errorHandler.ts"]
        CounterSvc["Counting Service\nsrc/services/counter.ts\ncountWords() · countCharacters()"]
    end

    Client -->|HTTP request| Server
    Server --> HealthRoute
    Server --> ValidateMW
    ValidateMW -->|valid| CountRoute
    ValidateMW -->|invalid| ErrMW
    CountRoute --> CounterSvc
    CounterSvc -->|result| CountRoute
    CountRoute -->|200 JSON| Client
    HealthRoute -->|200 JSON| Client
    ErrMW -->|400 JSON| Client

Request / Response Data Flow

sequenceDiagram
    participant C as HTTP Client
    participant E as Express App
    participant V as Validation MW
    participant R as /count Route
    participant S as Counter Service

    C->>E: POST /count  { "text": "hello world" }
    E->>V: parse JSON body
    alt body invalid or text missing/non-string
        V-->>C: HTTP 400  { "error": "..." }
    else body valid
        V->>R: next()
        R->>S: countWords(text), countCharacters(text)
        S-->>R: { words: 2, characters: 11 }
        R-->>C: HTTP 200  { "words": 2, "characters": 11 }
    end

NFRs

ID Category How Addressed
NFR-1 Runtime Node.js LTS; TypeScript source compiled via tsc before execution.
NFR-2 Framework Express.js; express.json() middleware for body parsing.
NFR-3 Persistence No database; all state is request-scoped and discarded after the response.
NFR-4 Security No auth layer; endpoint is open by design for demo use.
NFR-5 Testing Jest/Vitest unit tests for countWords and countCharacters; executed in CI via npm test.
NFR-6 Simplicity Single repository; zero non-Node dependencies beyond Express and the test runner.
NFR-7 Portability No native add-ons or OS-specific code; runs inside any standard node:lts Docker image or bare Node environment.

Risks

ID Risk Likelihood Impact Mitigation
R-1 Unicode surrogate pairs counted as 2 characters Low Low Documented as Assumption A4; acceptable for demo scope. Upgrade to Intl.Segmenter if true grapheme count is ever required.
R-2 Malformed Content-Type header bypasses JSON parsing Low Low Global error handler catches SyntaxError from express.json() and returns 400.
R-3 Port conflict on default 3000 Medium Low PORT environment variable allows any port; documented in README / startup instructions.
R-4 Dependency vulnerabilities in Express Medium Medium Kept on latest stable Express 4.x; npm audit run in CI; minimal transitive dependency surface.