A production-ready, well-structured TypeScript service built with Next.js that exposes a typed API for managing user access tokens. The implementation features a modular architecture, comprehensive testing, and a polished frontend interface.
- Next.js 16 (App Router) - React framework with API routes
- TypeScript 5 - Full type safety across the codebase
- SQLite (better-sqlite3) - Lightweight database for token storage
- Jest - Unit testing framework
- Tailwind CSS - Minimal styling for the frontend
- Node.js 20+ (or use Docker)
- npm or yarn
-
Install dependencies:
npm install
-
Run the development server:
npm run dev
-
Access the application:
- Frontend: http://localhost:3000
- API: http://localhost:3000/api/tokens
-
Run tests:
npm test
-
Build the image:
docker build -t token-api . -
Run the container:
docker run -p 3000:3000 -e API_KEY=your-secret-key token-api
npm run build
npm startCreates a new token for a user.
Request Headers:
X-API-KEY: API key for authentication (default:dev-secretin development)
Request Body (JSON):
{
"userId": "123",
"scopes": ["read", "write"],
"expiresInMinutes": 60
}Validation:
userIdmust be a non-empty stringscopesmust be a non-empty array of stringsexpiresInMinutesmust be a positive integer
Response (201 Created):
{
"id": "token_abc123",
"userId": "123",
"scopes": ["read", "write"],
"createdAt": "2025-01-01T10:00:00.000Z",
"expiresAt": "2025-01-01T11:00:00.000Z",
"token": "9f0c2d6a3b..."
}Error Responses:
400 Bad Request- Invalid input validation401 Unauthorized- Missing or invalid API key500 Internal Server Error- Server error
Lists all non-expired tokens for a specific user.
Request Headers:
X-API-KEY: API key for authentication
Query Parameters:
userId(required) - The user ID to query tokens for
Response (200 OK):
[
{
"id": "token_abc123",
"userId": "123",
"scopes": ["read", "write"],
"createdAt": "2025-01-01T10:00:00.000Z",
"expiresAt": "2025-01-01T11:00:00.000Z",
"token": "9f0c2d6a3b..."
}
]Empty Response (200 OK): If no active tokens are found for the user, an empty array is returned:
[]Error Responses:
400 Bad Request- Missing userId parameter or invalid userId401 Unauthorized- Missing or invalid API key500 Internal Server Error- Server error
The project follows a module-based architecture where each API endpoint is self-contained with its own types, validation, service layer, and responses. This makes it easy to add new endpoints and maintain the codebase.
src/
├── app/
│ ├── api/
│ │ └── tokens/ # Tokens API Module (self-contained)
│ │ ├── route.ts # API route handlers (GET/POST)
│ │ ├── service.ts # Database operations & business logic
│ │ ├── validation.ts # Input validation functions
│ │ ├── responses.ts # Module-specific response helpers
│ │ └── types.ts # Token-specific type definitions
│ ├── page.tsx # Frontend UI (two-section layout)
│ ├── layout.tsx # Root layout
│ └── globals.css # Global styles
├── lib/
│ ├── api/
│ │ └── shared/ # Shared API utilities
│ │ ├── middleware.ts # Authentication middleware
│ │ └── responses.ts # Generic response helpers
│ ├── auth.ts # Authentication utilities
│ ├── dbConnection.ts # Database connection management
│ ├── migrate.ts # Database schema migrations
│ ├── tokenUtils.ts # Pure utility functions
│ └── types.ts # Shared type definitions
└── types/
└── better-sqlite3.d.ts # Type definitions
__tests__/
├── setup.ts # Test setup & configuration
├── tokenUtils.test.ts # Token utility tests (comprehensive)
├── db.test.ts # Service/database integration tests
├── validation.test.ts # Input validation tests
└── middleware.test.ts # Authentication middleware tests
- Module-Based Design: Each API endpoint (
/api/tokens) is a complete module with all related code - Separation of Concerns: Clear boundaries between routes, validation, service, and types
- Shared Utilities: Common functionality (auth, responses) in
lib/api/shared/ - Type Safety: Full TypeScript coverage with explicit types throughout
- Scalable: Easy to add new endpoints by following the same module pattern
- Type Safety: All modules use explicit TypeScript types and interfaces. No
anytypes in core logic. - Module Organization: Each API endpoint is self-contained with its own types, validation, service, and responses
- Separation of Concerns:
- API routes handle HTTP concerns (validation, status codes)
- Service layer handles business logic and database operations
- Validation layer handles input validation
- Utility functions are pure and testable
- Input Validation: Comprehensive validation with clear, user-friendly error messages
- Error Handling: Appropriate HTTP status codes and consistent error response format
- Empty Response Handling: APIs always return consistent structures (empty arrays for lists)
- Testability: Core logic is separated into pure functions with comprehensive unit and integration tests
- Shared Middleware: Authentication and common logic shared across all endpoints
-
Authentication: Simple header-based API key authentication (
X-API-KEY). Default key isdev-secretwhenAPI_KEYenvironment variable is not set. In production, setAPI_KEYenvironment variable. -
Database: SQLite database stored at
.data/tokens.dbin the project root. The database directory is created automatically on first run. -
Token Generation: Tokens are generated using cryptographically secure random bytes (24 bytes hex-encoded). Token IDs use the format
token_<hex>. -
Expiry Handling: Expired tokens are filtered out at query time. No automatic cleanup job is implemented.
-
Frontend: Modern, responsive UI with two distinct sections:
- Create Token Section: Form to generate new tokens with validation
- List Tokens Section: Search and display tokens with empty state handling
- Features: Success/error messages, loading states, form validation, auto-refresh
-
Error Messages: Error messages are user-friendly but don't expose internal implementation details.
-
Database Migrations: Schema is created automatically on first run. No versioned migration system for this minimal implementation.
Comprehensive test suite with 50+ test cases covering:
-
tokenUtils.test.ts- Token utility functions- ID generation (uniqueness, format)
- Token string generation (length, format)
- Expiry calculations (edge cases, defaults)
- Expiry detection (boundary conditions)
-
db.test.ts- Service layer & database operations- Token creation and persistence
- Token listing with filtering
- Expiry filtering
- User filtering
- Empty response handling
- Multiple tokens per user
- Ordering and sorting
-
validation.test.ts- Input validation- POST request validation (20+ test cases)
- GET query parameter validation
- Missing fields, invalid types, edge cases
- Valid input acceptance
-
middleware.test.ts- Authentication middleware- Valid API key handling
- Invalid API key handling
- Missing API key handling
# Run all tests
npm test
# Run with coverage report
npm test -- --coverage
# Run specific test file
npm test -- validation.test.ts
# Run in watch mode
npm test -- --watch- Token Utils: ~100% coverage
- Validation: ~100% coverage
- Service/DB: High coverage with integration tests
- Middleware: High coverage with mocked dependencies
All tests are isolated, use fresh database instances, and include proper cleanup.
API_KEY- API key for authentication (defaults todev-secretin development)NODE_ENV- Set toproductionfor production buildsPORT- Server port (defaults to 3000)
The frontend provides a modern, user-friendly interface with:
-
Two-Section Layout:
- Create Token: Form with validation for generating new tokens
- List Tokens: Search interface for viewing user tokens
-
User Experience:
- Real-time validation feedback
- Success/error message display
- Loading states with spinners
- Empty state handling with helpful messages
- Auto-refresh after token creation
- Responsive design (mobile-friendly)
-
Styling:
- Modern gradient backgrounds
- Card-based layout with shadows
- Color-coded messages (green for success, red for errors)
- Token cards with badges for scopes
- Expiry date color coding
Each API module follows this pattern:
app/api/{module}/
├── route.ts # HTTP handlers (GET, POST, etc.)
├── service.ts # Business logic & database operations
├── validation.ts # Input validation
├── responses.ts # Response helpers
└── types.ts # Type definitions
Common functionality in lib/api/shared/:
- middleware.ts: Authentication middleware (
withAuth()) - responses.ts: Generic response helpers (error, success, etc.)
- Easy to Extend: Add new endpoints by creating new modules
- Consistent: All endpoints follow the same structure
- Maintainable: Related code is co-located
- Testable: Each module can be tested independently
Potential improvements for a production system:
- Token revocation endpoint (DELETE /api/tokens/:id)
- Automatic cleanup of expired tokens (scheduled job)
- Rate limiting per API key
- More robust authentication (JWT, OAuth)
- Database connection pooling
- API documentation (OpenAPI/Swagger)
- Logging and monitoring (structured logging)
- Environment-specific configuration management
- Token refresh mechanism
- Audit logging for token operations