QAtrial v3.0.0 is a full-stack quality management platform built with React 19, Hono, PostgreSQL, and Prisma ORM. This article is a technical walkthrough of the architecture for developers, architects, and technical evaluators considering QAtrial for regulated environments.

The codebase comprises 130+ TypeScript source files and 25,000+ lines of code. It supports 12 languages, 10 industry verticals, and 37 countries. Everything runs from a single repository with dual deployment modes: standalone (browser-only with localStorage) and server (full PostgreSQL backend).

Architecture Overview

Browser (React 19)  -->  Hono Server (port 3001)  -->  PostgreSQL 16
     |                        |                            |
     |-- Vite dev server      |-- 60+ API endpoints       |-- 15 Prisma models
     |-- 14 code-split chunks |-- 21 route files          |-- Append-only audit log
     |-- 20 Zustand stores    |-- JWT auth middleware      |-- ACID transactions
     |-- TanStack Table v8    |-- RBAC permission checks   |-- Referential integrity
     |-- react-i18next        |-- Webhook dispatch         |
     |-- Recharts             |-- AI proxy                 |
                              |-- SSO (OIDC)               |
                              |-- Static file serving      |

The frontend communicates with the backend exclusively through REST API calls authenticated with Bearer tokens. An apiClient.ts wrapper injects the JWT access token into every request and handles token refresh transparently.

QAtrial – Technical Architecture Overview
QAtrial v3.0.0 · Technical Architecture
Inside QAtrial’s
Architecture
130+ TypeScript source files. 25,000+ lines of code. Dual deployment modes: standalone (browser-only localStorage) and server (full PostgreSQL backend). A complete quality management system accessible to a single developer with docker-compose up.
React 19 Hono PostgreSQL 16 Prisma ORM v7 JWT + RBAC Docker TypeScript
130+
Source files
25K+
Lines of code
60+
API endpoints
15
Prisma models
20
Zustand stores
14
Vite chunks
Three-Layer Architecture
Browser (React 19)
Vite dev · port 5173
14 code-split chunks
20 Zustand stores
TanStack Table v8
react-i18next (12 langs)
Recharts dashboards
Lazy-loaded routes
Hono Server
Node.js · port 3001
60+ REST API endpoints
21 route files
JWT auth middleware
RBAC permission checks
Webhook dispatch
AI proxy (9 prompts)
OIDC SSO
Static file serving
PostgreSQL 16
Prisma ORM v7
15 Prisma models
Append-only audit log
ACID transactions
Referential integrity
Named Docker volume
pg_isready health check
Database Layer
15 Prisma Models — Full Quality Management Schema
Model Category Purpose Key Fields
User Auth Authentication and identity email · passwordHash · role · orgId
Organization Auth Multi-tenant isolation name · slug
Project Quality Quality project container name · country · vertical · projectType · modules[] · orgId
Requirement Quality Quality requirement seqId (REQ-NNN) · title · description · status · riskLevel · regulatoryRefs[] · tags[]
Test Quality Test case seqId (TST-NNN) · title · status · linkedRequirements[]
CAPA Quality Corrective/preventive action title · status (6-state) · rootCause · correctiveAction
Risk Quality Risk assessment severity · likelihood · detectability · riskLevel (auto-calculated)
Evidence Quality File attachment filename · path · entityType · entityId
Approval Quality Approval workflow record entityType · entityId · status (pending/approved/rejected) · reviewerId
Signature Audit Electronic signature (21 CFR Part 11) userId · meaning · reason · timestamp · passwordVerified
AuditLog Audit Append-only event log userId · action (16 types) · entityType · entityId · previousValue · newValue
Webhook Integration Webhook configuration url · secret · events[] · enabled · lastTriggered · lastStatus
Integration Integration External connector config type (jira/github) · config (JSON) · enabled · lastSyncAt
Session Auth Refresh token management userId · refreshToken · expiresAt
Setting System System configuration key · value · orgId
Authentication & Authorization
JWT Dual-Token + 5-Role RBAC Matrix
ACCESS TOKEN
24-hour expiry · Bearer in every request
Contains userId, email, role, orgId. Validated by requireAuth middleware on every protected endpoint. apiClient.ts injects token transparently and handles refresh.
REFRESH TOKEN
7-day expiry · POST /api/auth/refresh
Validated against Session table. Issues new token pair. Stored client-side. bcrypt 12 rounds for password hashing. Auth routes: register · login · refresh · me
Role canView canEdit canApprove canAdmin
admin
qa_manager
qa_engineer
reviewer
auditor
AI Subsystem
9 Prompt Templates · Multi-Provider · Server-Side Proxy · JSON Schema Validation
PROMPT 01
generateTests
IN: Requirement + country/vertical/standards/risk
OUT: 4–6 test cases with steps + expected results
PROMPT 02
riskClassification
IN: Requirement text + vertical taxonomy
OUT: Severity · likelihood · risk level + rationale
PROMPT 03
gapAnalysis
IN: Project requirements + target standard
OUT: Covered/partial/missing + remediation suggestions
PROMPT 04
capaSuggestion
IN: Failed test details + requirement context
OUT: Root cause + corrective/preventive action proposals
PROMPT 05
executiveBrief
IN: Project state (metrics, CAPAs, risks)
OUT: One-page C-level compliance summary
PROMPT 06
vsrReport
IN: Full project data
OUT: 7-section Validation Summary Report narrative
PROMPT 07
qmsrGap
IN: Requirements
OUT: QMSR/ISO 13485 gap analysis (27 clauses)
PROMPT 08
reqExtraction
IN: Source regulatory document text
OUT: Extracted requirements with metadata
PROMPT 09
qualityCheck
IN: Requirement text
OUT: Issues: vagueness · untestability · ambiguity · incompleteness
Providers: Anthropic · OpenAI · OpenRouter · Ollama · LM Studio Validation: JSON Schema on every response → auto-repair → retry Provenance: model · params · tokens · timestamp · reviewer ID
Dual Deployment Mode
Same React Codebase — Standalone or Full Server
Standalone Mode localStorage + Zustand
All data in 20 Zustand stores with Persist middleware → browser localStorage
Zero-setup demos and evaluations — clone + npm run dev
Development without running the backend
Import/export, AI panel, setup wizard all detect mode and route accordingly
CSV export generated directly from Zustand stores (no server)
Server Mode REST API + PostgreSQL
All data through REST API — Zustand for UI state, cache only
Full multi-user support with JWT auth, RBAC, sessions
Append-only audit log in PostgreSQL — 21 CFR Part 11 compliant
Webhook dispatch, OIDC SSO, external integrations
Production deployment with data sovereignty: your PostgreSQL instance
Mode detection: VITE_API_URL set + /api/status reachable → server mode. Otherwise → standalone fallback. Gradual migration: evaluate in standalone, promote to server without data restructuring.
Deployment & Operations
Docker · Build Commands · Compliance Packs
🐳 3-Stage Dockerfile
Stage 1Build: npm ci + Vite build → frontend assets
Stage 2Server: compile TypeScript + Prisma client generation
Stage 3Runtime: slim Node.js, compiled server + dist/ only. No dev deps, no source TS.
📦 docker-compose.yml
appQAtrial container · port 3001 · depends on db · env from .env
dbPostgreSQL 16 · pg_isready health check · named volume qatrial-db-data
resultEntire stack (frontend + backend + API) on single port from single process
⚙️ Build & DB Commands
server:devHono with file watching (port 3001)
db:generatePrisma client from schema
db:migrateRun Prisma migrations
db:pushPush schema directly to database
db:studioOpen Prisma Studio GUI
🎯 4 Compliance Starter Packs
fda_csvUS + Software/IT + Validation + 7 modules (Part 11, GAMP 5)
eu_mdrDE + Medical Devices + Quality System + 9 modules (ISO 13485)
fda_gmpUS + Pharma + Quality System + 10 modules (21 CFR 210/211)
iso_gdprDE + Software/IT + Compliance + 7 modules (ISO 27001/GDPR)
Codebase Statistics
v3.0.0 at a Glance
130+
Source files (TypeScript)
60+
REST API endpoints (21 route files)
20
Zustand stores (localStorage persist)
14
Vite code-split chunks
12
Languages (425+ translation keys each)
16
Audit log action types
14
Webhook event types
5
RBAC roles with permission matrix

In standalone mode, the same React components write to Zustand stores backed by localStorage. This allows the frontend to function as a fully operational demo without any server infrastructure. The import/export system, AI panel, and setup wizard all detect the current mode and route operations accordingly.

The Road to React: The React.js 19 with Hooks in JavaScript Book (2025 Edition)

The Road to React: The React.js 19 with Hooks in JavaScript Book (2025 Edition)

As an affiliate, we earn on qualifying purchases.

As an affiliate, we earn on qualifying purchases.

Database Layer: 15 Prisma Models

The PostgreSQL schema is defined in server/prisma/schema.prisma using Prisma ORM v7. The 15 models cover every entity in the quality management domain:

ModelPurposeKey Fields
UserAuthentication and identityemail, passwordHash, role, orgId
OrganizationMulti-tenant isolationname, slug
ProjectQuality project containername, country, vertical, projectType, modules[], orgId
RequirementQuality requirementseqId (REQ-NNN), title, description, status, riskLevel, regulatoryRefs[], tags[]
TestTest caseseqId (TST-NNN), title, description, status, linkedRequirements[]
CAPACorrective/preventive actiontitle, description, status (6-state lifecycle), rootCause, correctiveAction
RiskRisk assessmentseverity, likelihood, detectability, riskLevel (auto-calculated)
EvidenceFile attachmentfilename, path, entityType, entityId
ApprovalApproval workflow recordentityType, entityId, status (pending/approved/rejected), reviewerId
SignatureElectronic signatureuserId, meaning, reason, timestamp, passwordVerified
AuditLogAppend-only event loguserId, action (16 types), entityType, entityId, previousValue, newValue, reason
WebhookWebhook configurationurl, secret, events[], enabled, lastTriggered, lastStatus
IntegrationExternal connector configtype (jira/github), config (JSON), enabled, lastSyncAt
SessionToken managementuserId, refreshToken, expiresAt
SettingSystem configurationkey, value, orgId

Sequential IDs (REQ-001, TST-001) are auto-generated per project by querying the maximum existing sequence number and incrementing. This runs inside a transaction to prevent race conditions in multi-user environments.

Learn PostgreSQL: Use, manage, and build secure and scalable databases with PostgreSQL 16

Learn PostgreSQL: Use, manage, and build secure and scalable databases with PostgreSQL 16

As an affiliate, we earn on qualifying purchases.

As an affiliate, we earn on qualifying purchases.

Authentication: JWT with Refresh Tokens

The auth system uses a dual-token approach:

  1. Access token (24-hour expiry): Included as a Bearer token in every API request. Contains the user’s ID, email, role, and organization ID. Validated by the requireAuth middleware on every protected endpoint.
  2. Refresh token (7-day expiry): Stored client-side and used to obtain a new access token when the current one expires. The POST /api/auth/refresh endpoint validates the refresh token against the Session table and issues a new token pair.

Password hashing uses bcrypt with 12 rounds. The auth routes (server/routes/auth.ts) handle:

  • POST /api/auth/register — Create user account, hash password, assign default role, return token pair.
  • POST /api/auth/login — Validate credentials, return token pair.
  • POST /api/auth/refresh — Exchange refresh token for new token pair.
  • GET /api/auth/me — Return current user profile from JWT claims.

The requirePermission(permission) middleware chains after requireAuth and checks the user’s role against the permission matrix:

admin:        canView, canEdit, canApprove, canAdmin
qa_manager:   canView, canEdit, canApprove
qa_engineer:  canView, canEdit
auditor:      canView
reviewer:     canView, canApprove

Every mutation endpoint calls requirePermission('canEdit'). Approval endpoints call requirePermission('canApprove'). Administration endpoints call requirePermission('canAdmin'). View endpoints call requirePermission('canView').

TypeScript Programming Language - Software Engineer & Coder Pullover Hoodie

TypeScript Programming Language – Software Engineer & Coder Pullover Hoodie

  • Language Type: Superset of JavaScript with static typing
  • Ideal Users: Front-end, full-stack developers, architects
  • Application Scope: Builds large-scale web applications

As an affiliate, we earn on qualifying purchases.

As an affiliate, we earn on qualifying purchases.

Template Composition System

QAtrial’s template system generates project-specific requirements and tests based on the combination of country, vertical, project type, and modules. The composition follows a layered architecture:

Country (jurisdiction)  x  Vertical (domain)  x  Project Type  x  Modules

Registry (src/templates/registry.ts): Central registry that indexes all available countries, verticals, modules, and project types.

Composer (src/templates/composer.ts): Takes the user’s selections from the setup wizard and composes the final set of requirements and tests. The composition process:

  1. Load the base requirements for the selected country (regulatory authority, submission formats, local standards).
  2. Overlay vertical-specific requirements (e.g., pharma adds ICH Q7/Q10, medical devices adds ISO 13485/ISO 14971).
  3. Add module-specific requirements for each selected quality module.
  4. Filter by project type (validation projects get different templates than quality system projects).
  5. Generate test cases linked to the composed requirements.

Lazy loading: Template data is loaded on demand. The 10 vertical definitions, 15 module definitions, and country overlays are not bundled into the initial page load. They are imported dynamically when the setup wizard reaches the relevant step.

Compliance Starter Packs (src/templates/packs/index.ts): Four pre-configured bundles that set country, vertical, project type, and modules simultaneously. These bypass the manual wizard steps for common regulatory frameworks:

  • fda_csv: US + Software/IT + Validation + 7 modules (Part 11, GAMP 5 focus)
  • eu_mdr: DE + Medical Devices + Quality System + 9 modules (ISO 13485 focus)
  • fda_gmp: US + Pharma + Quality System + 10 modules (21 CFR 210/211 focus)
  • iso_gdpr: DE + Software/IT + Compliance + 7 modules (ISO 27001/GDPR focus)
API FRESHWATER MASTER TEST KIT 800-Test Freshwater Aquarium Water Master Test Kit, White, Single, Multi-colored

API FRESHWATER MASTER TEST KIT 800-Test Freshwater Aquarium Water Master Test Kit, White, Single, Multi-colored

  • Kit Contents: Includes 800 tests, solutions, color card, and tubes
  • Water Quality Monitoring: Helps prevent harmful water issues
  • Vital Parameter Testing: Measures pH, high pH, ammonia, nitrite, nitrate

As an affiliate, we earn on qualifying purchases.

As an affiliate, we earn on qualifying purchases.

AI System: 9 Prompts, Multi-Provider, Server-Side Proxy

The AI subsystem is architected for flexibility and security:

Provider abstraction (src/ai/provider.ts): A unified interface that supports Anthropic (Claude) and OpenAI-compatible APIs. The OpenAI-compatible mode works with OpenAI directly, OpenRouter (multi-model gateway), and Ollama (local inference). Provider configuration is stored per-organization.

9 prompt templates (src/ai/prompts/):

PromptInputOutput
generateTestsRequirement + context (country, vertical, standards, risk)4-6 test cases with steps and expected results
riskClassificationRequirement text + verticalSeverity, likelihood, risk level with rationale
gapAnalysisProject requirements + target standardCovered/partial/missing clauses with remediation suggestions
capaSuggestionFailed test detailsRoot cause analysis + corrective/preventive action proposals
executiveBriefProject stateOne-page C-level compliance summary
vsrReportProject data7-section Validation Summary Report
qmsrGapRequirementsQMSR/ISO 13485 gap analysis (27 clauses)
reqExtractionSource document textExtracted requirements with metadata
qualityCheckRequirement textIssues: vagueness, untestability, ambiguity, incompleteness, duplicates, missing criteria

JSON Schema validation (src/ai/validation.ts): Every AI response is validated against an expected JSON schema. If validation fails, the system attempts automatic repair (re-parsing, extracting JSON from markdown code blocks). If repair fails, a retry is triggered with a more explicit prompt.

Provenance tracking: Every AI generation records the model used, parameters, token count, timestamp, and the ID of the human reviewer who accepted or modified the output. This creates an audit trail for AI-generated content, addressing regulatory expectations around AI output review.

Server-side proxy (server/routes/ai.ts):

  • POST /api/ai/complete — Accepts a prompt and parameters, proxies the call to the configured LLM provider. API keys are read from server environment variables (AI_PROVIDER_KEY) and never sent to the client.
  • GET /api/ai/providers — Returns provider configuration with keys redacted.
  • POST /api/ai/providers/:id/test — Tests provider connectivity.

This architecture means the browser never holds an AI API key. For pharma and device companies with strict data governance policies, this is a prerequisite for AI adoption.

Webhook Dispatch

Webhook delivery is implemented as a fire-and-forget pattern in server/services/webhook.service.ts:

  1. A mutation occurs (e.g., requirement updated).
  2. The route handler calls the webhook service with the event type and payload.
  3. The service queries all enabled webhooks that subscribe to this event type.
  4. For each matching webhook, the service:
    a. Serializes the payload as JSON.
    b. Computes an HMAC-SHA256 signature using the webhook’s secret.
    c. Sends an HTTP POST with the payload and signature header.
    d. Records the delivery status (lastTriggered, lastStatus) in the Webhook model.
  5. Delivery failures are logged but do not block the original operation.

The fire-and-forget pattern ensures that webhook delivery issues (slow receivers, network problems) do not impact the user’s experience or create data consistency issues.

SSO: OIDC Discovery + Authorization Code Flow

The SSO implementation (server/routes/sso.ts) follows the standard OIDC authorization code flow:

  1. Discovery: On startup (or first SSO request), QAtrial fetches the IdP’s .well-known/openid-configuration to discover the authorization endpoint, token endpoint, and JWKS URI.
  2. Authorization redirect: When a user clicks “Sign in with SSO,” the browser is redirected to the IdP’s authorization endpoint with the configured client ID and callback URL.
  3. Callback: The IdP redirects back to /api/auth/sso/callback with an authorization code.
  4. Token exchange: QAtrial exchanges the code for an ID token and access token using the client secret.
  5. Auto-provisioning: If no QAtrial user exists for the IdP’s email claim, a new user is created with the configured default role (SSO_DEFAULT_ROLE).
  6. JWT issuance: QAtrial issues its own JWT access/refresh token pair, and the user is logged in.

Supported IdPs: Okta, Azure AD/Entra ID, Auth0, Keycloak, Google Workspace. Any OIDC-compliant IdP should work with the standard configuration.

Docker: Multi-Stage Build

The Dockerfile uses a three-stage build:

Stage 1 — Frontend build: Node.js image, npm install, npm run build. Produces the dist/ directory with the compiled React application, code-split into 14 Vite chunks.

Stage 2 — Server build: Compiles the Hono server TypeScript. Generates the Prisma client.

Stage 3 — Runtime: Slim Node.js image. Copies only the compiled server, generated Prisma client, and built frontend assets. No dev dependencies, no source TypeScript.

The production server serves the dist/ directory as static files. API routes are served under /api/. This means the entire application — frontend, backend, and API — runs from a single process on a single port.

docker-compose.yml defines two services:

  • app: The QAtrial container, port 3001, depends on db, environment variables from .env.
  • db: PostgreSQL 16, health check with pg_isready, named volume qatrial-db-data.

Health checks ensure the database is fully ready before the application attempts to connect.

Audit Trail: Append-Only PostgreSQL

The audit service (server/services/audit.service.ts) writes to the AuditLog table on every state-changing operation. The service is called from every route handler that creates, updates, deletes, approves, signs, or otherwise modifies data.

Each audit log entry captures:

FieldContent
userIdThe authenticated user’s ID (from JWT)
actionOne of 16 action types: create, update, delete, approve, reject, sign, status_change, import, export, login, logout, link, unlink, upload, download, config_change
entityTyperequirement, test, capa, risk, evidence, approval, signature, webhook, integration, user, project
entityIdThe specific record’s ID
previousValueJSON snapshot of the record before the change (null for creates)
newValueJSON snapshot of the record after the change (null for deletes)
reasonOptional reason provided by the user (required for certain operations)
timestampServer-generated UTC timestamp
ipAddressRequest origin IP

The table is append-only by design. There is no update or delete endpoint for audit log entries. The GET /api/audit/:projectId endpoint provides read access with filtering and pagination. GET /api/audit/:projectId/export produces a CSV file for offline review.

Import/Export

CSV Import (server/routes/import.ts):

  • POST /api/import/preview: Accepts a CSV file upload. Auto-detects the delimiter by sampling the first rows (supports comma, semicolon, tab). Returns: detected columns, sample data rows, and suggested column mapping based on header name similarity to QAtrial fields.
  • POST /api/import/execute: Creates requirements or tests from the mapped data. Supports duplicate handling strategies: skip (by title match), overwrite (update existing), or create new. Each imported record is audit-logged.

CSV Export (server/routes/export.ts):

  • GET /api/export/:projectId/csv?type=requirements|tests|all: Returns a UTF-8 BOM-encoded CSV file. The BOM ensures correct character rendering when the file is opened in Excel on Windows (a common pain point with CSV exports).

In standalone mode, the frontend generates CSV directly from Zustand stores without server involvement.

Code Splitting

Vite is configured with manual chunks in vite.config.ts to optimize the initial load:

14 chunks split the application by feature area:

  • Core framework (React, React DOM)
  • UI libraries (TanStack Table, Recharts, Lucide)
  • State management (Zustand stores)
  • Internationalization (react-i18next + locale data)
  • AI system (prompts, provider, validation)
  • Templates (verticals, modules, regions, packs)
  • Dashboard components
  • Wizard components
  • Import/export
  • Settings
  • Audit components
  • Report generation
  • Connectors
  • Shared utilities

Lazy-loaded routes ensure that a user who opens only the requirements table does not download the AI prompt templates, report generators, or settings components until they navigate to those features.

Dual Mode: Standalone + Server

The same React codebase supports two operational modes:

Standalone mode: All data stored in browser localStorage via Zustand stores with persistence middleware. The 20 Zustand stores cover: projects, requirements, tests, CAPAs, risks, evidence, approvals, signatures, audit trail, settings, AI configuration, import state, wizard state, dashboard state, notifications, theme, language, auth (mock), connectors, and webhooks.

Server mode: All data operations go through the REST API. The apiClient.ts wrapper handles authentication, token refresh, and error handling. Zustand stores are still used for client-side state (current selection, UI state, cached data) but are not the source of truth.

Mode detection is automatic. If the frontend detects a configured VITE_API_URL and can reach the /api/status endpoint, it operates in server mode. Otherwise, it falls back to standalone mode.

This dual-mode architecture enables:

  • Zero-setup demos and evaluations (standalone)
  • Production deployment with full persistence, multi-user support, and audit compliance (server)
  • Development without running the backend (standalone)
  • Gradual migration from evaluation to production without data restructuring

Build and Development

CommandDescription
npm run devVite dev server (frontend only, port 5173)
npm run buildProduction build (frontend)
npm run server:devHono server with file watching (port 3001)
npm run serverHono server without watching
npm run db:generateGenerate Prisma client from schema
npm run db:migrateRun Prisma migrations
npm run db:pushPush schema directly to database
npm run db:studioOpen Prisma Studio GUI
docker-compose upStart full stack (app + PostgreSQL)

Codebase Statistics

MetricValue
Source files130+
Lines of code25,000+
Languages (i18n)12 (425+ translation keys each)
Industry verticals10
Countries37
Database models15
API endpoints60+
Route files21
AI prompt templates9
Zustand stores20
Vite chunks14
Quality modules15
Compliance packs4
Validation documents5
Webhook events14
Audit action types16
RBAC roles5

Conclusion

QAtrial v3.0.0 is a complete quality management system, not a prototype or proof of concept. The architecture — React 19 frontend, Hono backend, PostgreSQL database, Prisma ORM, JWT authentication, five-role RBAC, append-only audit trail, OIDC SSO, webhook dispatch, and Docker deployment — addresses the technical requirements of regulated environments while remaining accessible to a single developer with docker-compose up.

The source code is available under AGPL-3.0 at https://github.com/MeyerThorsten/QAtrial.

You May Also Like

Connecting Quality Events to Your Workflow: QAtrial’s Webhook System

Quality events do not happen in isolation. A failed test needs to…

Apple Iphone Upgrade Program

Apple introduces a new iPhone upgrade program allowing customers to upgrade annually, starting this fall, confirmed by official sources.

Role-Based Access Control in Regulated Quality Systems: How QAtrial Gets It Right

Separation of duties is not a best practice in regulated industries. It…

From Excel to QAtrial: A Practical Migration Guide for Quality Teams

Excel is the most widely used quality management tool in regulated industries.…