Compare commits

..

2 Commits

Author SHA1 Message Date
bed53dbbd3 Epic 1, Story 1.4: Database Schema Definition with Prisma
- Installed Prisma and @prisma/client in backend/ directory
- Initialized Prisma with PostgreSQL provider (Prisma 7.x)
- Created prisma/schema.prisma with Book and ReadingLog models
- Configured prisma.config.ts with DATABASE_URL from environment
- Added .gitignore for Prisma-generated files

Book model includes:
- All required fields (id, title, author, totalPages, coverUrl, deadlineDate, isPrimary, status)
- Timestamps (createdAt, updatedAt)
- Indexes on deadlineDate and status for query performance
- One-to-many relationship with ReadingLog

ReadingLog model includes:
- All required fields (id, bookId, logDate, currentPage)
- Timestamps (createdAt, updatedAt)
- Foreign key relationship to Book with cascade delete
- Unique constraint on (bookId, logDate) - one entry per book per day
- Indexes on bookId and logDate for query performance

All acceptance criteria met:
 Prisma installed in backend/
 Initialized with PostgreSQL provider
 schema.prisma defines Book and ReadingLog models
 Appropriate indexes for performance
 One-to-many relationship defined
 Unique constraint ensures one log entry per book per day

Schema validated with npx prisma format

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 16:12:04 +01:00
27802f4bf3 Epic 1, Story 1.3: Backend Scaffolding with Node.js + Express
- Created Express application in backend/src/server.js
- Installed dependencies: express, cors, helmet, dotenv, express-validator
- Installed dev dependencies: nodemon, eslint, globals
- Configured middleware: CORS (frontend origin), Helmet (security), JSON parser
- Loaded environment configuration via dotenv
- Server listens on port from API_PORT env variable (default: 3001)
- Implemented error handling middleware for validation and server errors
- Created health check endpoint: GET /api/health returns status and timestamp
- Added npm scripts: dev (nodemon), start (production), lint
- Configured ESLint for Node.js/Express with CommonJS
- Created backend/.env with all required environment variables

All acceptance criteria met:
 Express application created in backend/src/server.js
 All required dependencies installed
 Middleware configured (CORS, Helmet, JSON parser)
 Environment configuration via dotenv
 Server listens on env port
 Error handling middleware implemented
 Health check endpoint working
 Package.json scripts: dev, start, lint
 ESLint configured for Node.js/Express

Tested and verified:
- ESLint passes with no errors
- Server starts successfully
- Health endpoint returns correct JSON
- Changed default port to 3001 (3000 occupied)

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 16:09:03 +01:00
7 changed files with 3279 additions and 0 deletions

5
backend/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
node_modules
# Keep environment variables out of version control
.env
/src/generated/prisma

21
backend/eslint.config.js Normal file
View File

@ -0,0 +1,21 @@
const globals = require('globals');
module.exports = [
{
files: ['**/*.js'],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'commonjs',
globals: {
...globals.node,
},
},
rules: {
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'no-console': 'off',
},
},
{
ignores: ['node_modules/**', 'dist/**'],
},
];

3116
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
backend/package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "backend",
"version": "1.0.0",
"description": "Book Reading Tracker - Backend API",
"main": "src/server.js",
"scripts": {
"dev": "nodemon src/server.js",
"start": "node src/server.js",
"lint": "eslint .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@prisma/client": "^7.1.0",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"express": "^5.2.1",
"express-validator": "^7.3.1",
"helmet": "^8.1.0",
"prisma": "^7.1.0"
},
"devDependencies": {
"eslint": "^9.39.1",
"globals": "^16.5.0",
"nodemon": "^3.1.11"
}
}

14
backend/prisma.config.ts Normal file
View File

@ -0,0 +1,14 @@
// This file was generated by Prisma and assumes you have installed the following:
// npm install --save-dev prisma dotenv
import "dotenv/config";
import { defineConfig, env } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: env("DATABASE_URL"),
},
});

View File

@ -0,0 +1,43 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
}
model Book {
id Int @id @default(autoincrement())
title String @db.VarChar(500)
author String? @db.VarChar(500)
totalPages Int
coverUrl String? @db.VarChar(1000)
deadlineDate DateTime @db.Date
isPrimary Boolean @default(false)
status String @default("reading") @db.VarChar(50)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
readingLogs ReadingLog[]
@@index([deadlineDate])
@@index([status])
}
model ReadingLog {
id Int @id @default(autoincrement())
bookId Int
logDate DateTime @db.Date
currentPage Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
@@unique([bookId, logDate])
@@index([bookId])
@@index([logDate])
}

51
backend/src/server.js Normal file
View File

@ -0,0 +1,51 @@
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const app = express();
// Middleware
app.use(helmet());
app.use(
cors({
origin: process.env.CORS_ORIGIN || 'http://localhost:5173',
credentials: true,
})
);
app.use(express.json());
// Health check endpoint
app.get('/api/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
});
});
// Error handling middleware
app.use((err, req, res, _next) => {
console.error('Error:', err);
// Validation errors (from express-validator)
if (err.errors && Array.isArray(err.errors)) {
return res.status(400).json({
error: 'Validation failed',
details: err.errors,
});
}
// Default error response
res.status(err.statusCode || 500).json({
error: err.message || 'Internal server error',
});
});
// Start server
const PORT = process.env.API_PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log(`Health check: http://localhost:${PORT}/api/health`);
});
module.exports = app;