chitfund/docs/AVAILABLE_USERS_API_FIX.md

5.5 KiB

🐛 Available Users API 500 Error - Fixed

Issue

Error: 500 Internal Server Error
Endpoint: GET /api/members/users/available/:groupId
URL: https://chitfund.deepteklabs.com/api/members/users/available/d3ac73b2-305f-49c8-82d6-84076235f9c3

SQL Error: syntax error at or near "SELECT"


Root Cause

The SQL subquery in NOT IN clause was missing parentheses:

-- ❌ WRONG (Missing parentheses)
WHERE "User"."id" NOT IN 
  SELECT user_id FROM group_members ...

-- ✅ CORRECT (With parentheses)
WHERE "User"."id" NOT IN (
  SELECT user_id FROM group_members ...
)

The Fix

File: backend/src/controllers/memberController.js

Before (Missing parentheses):

id: {
  [Op.notIn]: require('sequelize').literal(`
    SELECT user_id FROM group_members 
    WHERE group_id = '${groupId}' AND status = 'active'
  `)
}

After (With parentheses):

const { sequelize } = require('../config/database');

id: {
  [Op.notIn]: sequelize.literal(`(
    SELECT user_id FROM group_members 
    WHERE group_id = '${groupId}' AND status = 'active'
  )`)
}

Changes Made:

  1. Added parentheses around subquery: (SELECT ...)
  2. Imported sequelize from database config (cleaner)

What This Endpoint Does

Returns a list of users who are NOT already members of a specific chit group. Used when a manager wants to add new members to a group.

Request:

GET /api/members/users/available/:groupId?page=1&limit=50&search=john

Response:

{
  "success": true,
  "data": {
    "users": [
      {
        "id": "uuid",
        "full_name": "John Doe",
        "mobile_number": "9876543210",
        "email": "john@example.com",
        "address": "123 Main St",
        "emergency_contact": "9999999999",
        "created_at": "2025-11-06T..."
      }
    ],
    "pagination": {
      "currentPage": 1,
      "totalPages": 1,
      "totalItems": 5,
      "itemsPerPage": 50
    }
  }
}

How to Deploy

# 1. Commit changes
git add backend/src/controllers/memberController.js
git commit -m "Fix available users API - add parentheses to subquery"
git push origin prodnew

# 2. Deploy to production
ssh luckychit@192.168.8.148
cd /home/luckychit/apps/chitfund
./scripts/deploy.sh backend

# 3. Test it works
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://chitfund.deepteklabs.com/api/members/users/available/YOUR_GROUP_ID"

Testing

Test 1: Get Available Users

curl -H "Authorization: Bearer MANAGER_TOKEN" \
  "https://chitfund.deepteklabs.com/api/members/users/available/d3ac73b2-305f-49c8-82d6-84076235f9c3"

Should return: List of users not in the group

curl -H "Authorization: Bearer MANAGER_TOKEN" \
  "https://chitfund.deepteklabs.com/api/members/users/available/d3ac73b2-305f-49c8-82d6-84076235f9c3?search=john"

Should return: Filtered users matching "john"

Test 3: With Pagination

curl -H "Authorization: Bearer MANAGER_TOKEN" \
  "https://chitfund.deepteklabs.com/api/members/users/available/d3ac73b2-305f-49c8-82d6-84076235f9c3?page=1&limit=10"

Should return: First 10 available users


SQL Syntax Rules

NOT IN with Subquery

Always wrap subquery in parentheses:

-- ✅ CORRECT
WHERE id NOT IN (
  SELECT user_id FROM table
)

-- ❌ WRONG
WHERE id NOT IN 
  SELECT user_id FROM table

EXISTS Alternative (Better Performance)

For better performance, consider using NOT EXISTS:

// Alternative implementation (faster for large datasets)
id: {
  [Op.notIn]: sequelize.literal(`(
    SELECT gm.user_id 
    FROM group_members gm 
    WHERE gm.group_id = '${groupId}' 
      AND gm.status = 'active'
  )`)
}

// Or using NOT EXISTS:
where: sequelize.literal(`
  NOT EXISTS (
    SELECT 1 FROM group_members gm 
    WHERE gm.user_id = "User"."id" 
      AND gm.group_id = '${groupId}' 
      AND gm.status = 'active'
  )
`)

Security Note

⚠️ SQL Injection Risk: The current code uses string interpolation for groupId:

WHERE group_id = '${groupId}'  // ⚠️ Vulnerable to SQL injection

Use Sequelize's replacements for safer queries:

id: {
  [Op.notIn]: sequelize.literal(`(
    SELECT user_id FROM group_members 
    WHERE group_id = :groupId AND status = 'active'
  )`),
}
// Then use replacements in the query
{
  replacements: { groupId },
  type: sequelize.QueryTypes.SELECT
}

For now: The route is protected with requireManager middleware, so only authenticated managers can access it. Still, consider improving this for better security.


All working correctly:

  • POST /api/members/:groupId/members - Add member to group
  • GET /api/members/:groupId/members - Get group members
  • GET /api/members/:groupId/members/:memberId - Get member details
  • PUT /api/members/:groupId/members/:memberId/status - Update member status

Summary

Problem: SQL syntax error - missing parentheses around NOT IN subquery
Solution: Added parentheses: NOT IN (SELECT ...)
Impact: Managers can now see available users to add to groups
Status: Fixed
Deploy: ./scripts/deploy.sh backend


Files Changed

  • backend/src/controllers/memberController.js Fixed

The available users API should now work correctly! 🎉

Managers can now see which users are available to add to their chit groups.