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— returnstext.length(UTF-16 code units, spaces included).
4. Validation Middleware (src/middleware/validateCount.ts)
Express middleware applied to POST /count that checks:
- Body is present and
Content-Type: application/jsonwas parsed successfully. textfield exists.textis of typestring. 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. |