Skip to content

Conversation

qwalski
Copy link
Owner

@qwalski qwalski commented Sep 10, 2025

Summary by CodeRabbit

  • New Features
    • Todo list supports filtering by search and completion status.
    • Todo creation accepts priority and tags.
    • Added a debug info endpoint exposing runtime details.
  • Refactor
    • Standardized API responses for listing, detail, and creation to include structured data, counts/metadata, and consistent error payloads.
  • Chores
    • Server port set to 3000.
    • Expanded CORS to allow all origins with credentials.
    • Increased request body size limits for JSON and form data.

Copy link

coderabbitai bot commented Sep 10, 2025

Walkthrough

This change updates database connection error logging to structured objects, enhances todo controller endpoints with filtering and structured responses, expands createTodo inputs, adjusts server middleware (CORS/body limits), fixes port to 3000, and adds a new /debug/info endpoint. No exported signatures changed; one new public route introduced.

Changes

Cohort / File(s) Summary of Changes
Database connection logging
config/database.js
Logs database connection errors as structured objects (message, code, stack). Success path and initialization unchanged; exports unaffected.
Todo controller queries and responses
controllers/todoController.js
getAllTodos adds search/status filters; returns { data, count, timestamp }. getTodoById switches to dynamic query, returns structured success/error payloads. createTodo accepts priority/tags, returns detailed result or error. Error handling standardized. Exports unchanged.
Server configuration and diagnostics
server.js
Sets fixed port 3000. Adds global CORS with origin '*' and credentials. Increases body size limits to 50mb. Introduces GET /debug/info returning runtime/env/db path. Removes previous middleware block and adds route comment marker.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Server
  participant TodoController as TodoController.getAllTodos
  participant DB

  Client->>Server: GET /todos?search=...&status=...
  Server->>TodoController: Invoke with req.query
  TodoController->>TodoController: Build dynamic WHERE clauses
  TodoController->>DB: Execute SQL (filters applied)
  alt Success
    DB-->>TodoController: rows
    TodoController-->>Server: { data, count, timestamp }
    Server-->>Client: 200 JSON
  else Error
    DB-->>TodoController: err
    TodoController-->>Server: Generic DB error payload
    Server-->>Client: 500 JSON
  end
Loading
sequenceDiagram
  autonumber
  participant Client
  participant Server
  participant TodoController as TodoController.getTodoById
  participant DB

  Client->>Server: GET /todos/:id
  Server->>TodoController: Invoke with id
  TodoController->>DB: Execute dynamic SQL for id
  alt Found
    DB-->>TodoController: row
    TodoController-->>Server: { success: true, todo, metadata }
    Server-->>Client: 200 JSON
  else Not Found
    DB-->>TodoController: empty
    TodoController-->>Server: { success: false, code: TODO_NOT_FOUND }
    Server-->>Client: 404 JSON
  end
Loading
sequenceDiagram
  autonumber
  participant Client
  participant Server
  participant OS as Runtime

  Client->>Server: GET /debug/info
  Server->>OS: Collect node_version, platform, memory, uptime, env, db_path
  OS-->>Server: Metrics
  Server-->>Client: 200 JSON with diagnostics
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks (2 passed, 1 inconclusive)

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “new changes” is too vague and does not convey any specific information about the main updates in the pull request, making it difficult for reviewers to understand the purpose or scope of the changes at a glance. Please update the title to a concise sentence that highlights the key updates, for example “Enhance database error logging and update todoController response formats,” so reviewers can immediately grasp the core improvements.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

I thump my paws at port three-oh-oh,
Logs now speak in structured glow.
Todos filter, tag, and sort—
With tidy shapes in each report.
A debug warren shows our stats—
Hippity-hop, ship it, dev-rabbits! 🥕🐇

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch new-changes-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (7)
config/database.js (1)

12-16: Avoid leaking stack traces in production logs

Great move to structured logging. Gate stack output by NODE_ENV to prevent sensitive leakage in prod.

-          console.error('Database connection failed:', {
-            message: err.message,
-            code: err.code,
-            stack: err.stack
-          });
+          const payload = process.env.NODE_ENV === 'production'
+            ? { message: err.message, code: err.code }
+            : { message: err.message, code: err.code, stack: err.stack };
+          console.error('Database connection failed:', payload);
server.js (1)

18-19: Right-size body limits (50MB is excessive) and prefer built-ins

Large limits increase DoS risk. Consider smaller defaults; using body-parser is fine, but Express has built-ins.

-app.use(bodyParser.json({ limit: '50mb' }));
-app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
+app.use(bodyParser.json({ limit: '10mb' }));
+app.use(bodyParser.urlencoded({ extended: false, limit: '10mb' }));

If you want to switch to built-ins later:

app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: false, limit: '10mb' }));
controllers/todoController.js (5)

32-36: Don’t return stack traces to clients in prod

Keep the structured error, but hide stack/code externally in production.

-      res.status(500).json({ 
-        error: 'Internal server error',
-        details: error.stack,
-        code: error.code
-      });
+      res.status(500).json(
+        process.env.NODE_ENV === 'production'
+          ? { error: 'Internal server error' }
+          : { error: 'Internal server error', details: error.stack, code: error.code }
+      );

61-68: Use ISO timestamp for metadata.retrieved_at

Ensures consistent, JSON-safe representation.

-          metadata:{
-            retrieved_at:new Date(),
-            version:"1.0"
-          }
+          metadata: {
+            retrieved_at: new Date().toISOString(),
+            version: "1.0"
+          }

78-81: Broaden input validation (priority/tags)

Validate allowed priority values and normalize tags (array vs string).

If tags can be an array, store JSON; enforce priority ∈ {'low','normal','high'} and length limits.


100-112: Normalize response fields

Return consistent defaults to match inserted values.

-            description: description,
+            description: description || '',
             priority: priority || 'normal',
-            tags: tags,
+            tags: Array.isArray(tags) ? JSON.stringify(tags) : (tags || ''),

115-116: Use error level logging

Prefer error for failures; avoid noisy console.log.

-      console.log(error);
+      console.error(error);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03b8da7 and b056d78.

⛔ Files ignored due to path filters (1)
  • utils/helpers.js is excluded by none and included by none
📒 Files selected for processing (3)
  • config/database.js (1 hunks)
  • controllers/todoController.js (3 hunks)
  • server.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
controllers/todoController.js (2)
config/database.js (1)
  • database (77-77)
server.js (1)
  • database (7-7)
server.js (1)
routes/todoRoutes.js (1)
  • express (1-1)
🔇 Additional comments (1)
controllers/todoController.js (1)

54-58: 404 payload LGTM

Clear code and message; consistent with typical API contracts.

Comment on lines +8 to +19
const { search, status } = req.query;

db.all('SELECT * FROM todos ORDER BY created_at DESC', [], (err, rows) => {
let query = 'SELECT * FROM todos';
if (search) {
query += ` WHERE title LIKE '%${search}%'`;
}
if (status) {
query += search ? ' AND' : ' WHERE';
query += ` completed = ${status === 'true' ? 1 : 0}`;
}
query += ' ORDER BY created_at DESC';

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Eliminate SQL injection in filtering (use placeholders, not string concat)

Current LIKE/status concatenation is injectable and breaks on quotes. Use parameterized queries.

-      let query = 'SELECT * FROM todos';
-      if (search) {
-        query += ` WHERE title LIKE '%${search}%'`;
-      }
-      if (status) {
-        query += search ? ' AND' : ' WHERE';
-        query += ` completed = ${status === 'true' ? 1 : 0}`;
-      }
-      query += ' ORDER BY created_at DESC';
-
-      db.all(query, [], (err, rows) => {
+      const base = 'SELECT * FROM todos';
+      const where = [];
+      const params = [];
+      if (search) {
+        where.push('title LIKE ?');
+        params.push(`%${search}%`);
+      }
+      if (typeof status === 'string') {
+        where.push('completed = ?');
+        params.push(status === 'true' ? 1 : 0);
+      }
+      const query = `${base}${where.length ? ' WHERE ' + where.join(' AND ') : ''} ORDER BY created_at DESC`;
+
+      db.all(query, params, (err, rows) => {

Also applies to: 20-30

Comment on lines +46 to 49
const query = `SELECT * FROM todos WHERE id = ${id}`;

db.get(query, [], (err, row) => {
if (err) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Parameterize getTodoById (current code is injectable)

Interpolate-free query with numeric coercion.

-      const query = `SELECT * FROM todos WHERE id = ${id}`;
-      
-      db.get(query, [], (err, row) => {
+      const query = 'SELECT * FROM todos WHERE id = ?';
+      db.get(query, [Number(id)], (err, row) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const query = `SELECT * FROM todos WHERE id = ${id}`;
db.get(query, [], (err, row) => {
if (err) {
const query = 'SELECT * FROM todos WHERE id = ?';
db.get(query, [Number(id)], (err, row) => {
if (err) {
🤖 Prompt for AI Agents
In controllers/todoController.js around lines 46 to 49, the SQL query currently
interpolates id directly which is vulnerable to injection; change to a
parameterized query and coerce id to a number before using it. Replace the
string interpolation with a placeholder (e.g., "SELECT * FROM todos WHERE id =
?"), call db.get using the id as a bound parameter array, and coerce/validate
the id (parseInt or Number and check for NaN) before passing it; handle invalid
id by returning an appropriate error response.

Comment on lines +87 to 98
const insertQuery = `INSERT INTO todos (title, description, priority, tags) VALUES ('${title}', '${description || ''}', '${priority || 'normal'}', '${tags || ''}')`;

db.run(insertQuery, [], function(err) {
if (err) {
res.status(500).json({
error: 'Database error',
details: err.message,
code: err.code,
sql: insertQuery
});
return;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix INSERT: SQL injection + schema mismatch (priority,tags don’t exist yet)

  • Uses string interpolation → injectable and breaks on quotes.
  • Table schema lacks priority/tags, so this fails at runtime.
-      const insertQuery = `INSERT INTO todos (title, description, priority, tags) VALUES ('${title}', '${description || ''}', '${priority || 'normal'}', '${tags || ''}')`;
-      
-      db.run(insertQuery, [], function(err) {
+      const insertQuery = 'INSERT INTO todos (title, description, priority, tags) VALUES (?, ?, ?, ?)';
+      const values = [
+        title.trim(),
+        (description || '').trim(),
+        (priority || 'normal'),
+        Array.isArray(tags) ? JSON.stringify(tags) : (tags || '')
+      ];
+      
+      db.run(insertQuery, values, function(err) {
-        if (err) {
-          res.status(500).json({ 
-            error: 'Database error',
-            details: err.message,
-            code: err.code,
-            sql: insertQuery
-          });
+        if (err) {
+          res.status(500).json({
+            error: 'Database error',
+            details: err.message,
+            code: err.code
+          });
           return;
         }

Additionally update the DB schema and migrate existing DBs. Example (in config/database.js initializeTables), after CREATE TABLE:

// Ensure new columns exist (idempotent)
this.db.all("PRAGMA table_info(todos)", [], (e, cols) => {
  if (e) return reject(e);
  const names = new Set(cols.map(c => c.name));
  const alters = [];
  if (!names.has('priority')) alters.push("ALTER TABLE todos ADD COLUMN priority TEXT DEFAULT 'normal'");
  if (!names.has('tags')) alters.push("ALTER TABLE todos ADD COLUMN tags TEXT DEFAULT ''");
  (function runNext() {
    if (!alters.length) return resolve();
    const sql = alters.shift();
    // Ignore 'duplicate column name' errors for safety
    this.db.run(sql, (err2) => runNext());
  }).call(this);
});
🤖 Prompt for AI Agents
In controllers/todoController.js around lines 87-98, the INSERT uses string
interpolation causing SQL injection and will break on quotes, and it expects
columns (priority, tags) that may not exist; fix by converting the INSERT to a
parameterized prepared statement (use placeholders and pass values as params)
and only include priority/tags when present or set safe defaults, and update the
DB initialization/migration (config/database.js initializeTables) to
idempotently add priority and tags columns if missing (run PRAGMA table_info,
build ALTER TABLE statements for missing columns with defaults, and execute them
while safely ignoring duplicate column errors) so existing DBs are migrated.

Comment on lines +11 to +16
const PORT = 3000;

app.use(cors({
origin: '*',
credentials: true
}));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid CORS config ('*' + credentials) and restore env-driven PORT

Browsers reject Access-Control-Allow-Origin: * when Access-Control-Allow-Credentials: true. Also, hardcoding PORT breaks PaaS.

-const PORT = 3000;
-
-app.use(cors({
-  origin: '*',
-  credentials: true
-}));
+const PORT = process.env.PORT || 3000;
+
+const corsOptions = {
+  origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
+  credentials: true
+};
+app.use(cors(corsOptions));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const PORT = 3000;
app.use(cors({
origin: '*',
credentials: true
}));
const PORT = process.env.PORT || 3000;
const corsOptions = {
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
credentials: true
};
app.use(cors(corsOptions));
🤖 Prompt for AI Agents
In server.js around lines 11–16, change the hardcoded PORT to use an environment
variable (e.g., process.env.PORT || 3000) so PaaS can control the port, and
replace the invalid CORS config (origin: '*', credentials: true) by configuring
a concrete origin or dynamic origin resolution—e.g., read allowed origin(s) from
an env var (CORS_ORIGIN) or implement a function that echoes/validates the
request Origin against an allowlist—while keeping credentials: true.

Comment on lines +25 to +34
app.get('/debug/info', (req, res) => {
res.json({
node_version: process.version,
platform: process.platform,
memory_usage: process.memoryUsage(),
uptime: process.uptime(),
environment: process.env.NODE_ENV,
database_path: './todos.db'
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Protect /debug/info (information disclosure)

This exposes environment and memory details. Gate it by environment (or auth).

-app.get('/debug/info', (req, res) => {
-  res.json({
-    node_version: process.version,
-    platform: process.platform,
-    memory_usage: process.memoryUsage(),
-    uptime: process.uptime(),
-    environment: process.env.NODE_ENV,
-    database_path: './todos.db'
-  });
-});
+if (process.env.NODE_ENV !== 'production') {
+  app.get('/debug/info', (req, res) => {
+    res.json({
+      node_version: process.version,
+      platform: process.platform,
+      memory_usage: process.memoryUsage(),
+      uptime: process.uptime(),
+      environment: process.env.NODE_ENV,
+      database_path: './todos.db'
+    });
+  });
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.get('/debug/info', (req, res) => {
res.json({
node_version: process.version,
platform: process.platform,
memory_usage: process.memoryUsage(),
uptime: process.uptime(),
environment: process.env.NODE_ENV,
database_path: './todos.db'
});
});
if (process.env.NODE_ENV !== 'production') {
app.get('/debug/info', (req, res) => {
res.json({
node_version: process.version,
platform: process.platform,
memory_usage: process.memoryUsage(),
uptime: process.uptime(),
environment: process.env.NODE_ENV,
database_path: './todos.db'
});
});
}
🤖 Prompt for AI Agents
In server.js around lines 25 to 34 the /debug/info route currently returns
sensitive process and environment details; gate this endpoint so it is not
exposed in production by checking process.env.NODE_ENV and only registering or
responding when NODE_ENV !== 'production' (or require a simple auth header/api
key when NODE_ENV === 'production'); if access is not allowed return a 403 or
404 and do not include any sensitive fields. Ensure the route either is
conditionally mounted or performs the environment/auth check at the top and
avoids sending process.env or full memory dumps in production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant