Building a School Supply Management System

Understanding the Problem

We're building an API to help a school manage their supplies and classroom assignments. The core business question we need to answer is: "What order do I need to place for school supplies this year?" This seemingly simple question requires us to build a robust system that can:

  1. Track supplies for each classroom
  2. Manage student assignments and their handedness
  3. Calculate supply needs based on classroom enrollments
  4. Handle dynamic updates as assignments change

Database Structure

Our database consists of several interconnected tables:

Implementation Strategy

Phase 1: Basic Querying and Ordering


// Ordering students by name
router.get('/students', async (req, res) => {
    const students = await Student.findAll({
        order: [
            ['lastName', 'ASC'],
            ['firstName', 'ASC']
        ],
        attributes: ['id', 'firstName', 'lastName', 'leftHanded']
    });
    
    res.json(students);
});

// Ordering classrooms alphabetically
router.get('/classrooms', async (req, res) => {
    const classrooms = await Classroom.findAll({
        order: [['name', 'ASC']],
        attributes: ['id', 'name', 'studentLimit']
    });
    
    res.json(classrooms);
});
    

Think of this like organizing a filing cabinet - we want to make it easy to find any student or classroom quickly and consistently.

Phase 2: Implementing Pagination


router.get('/students', async (req, res) => {
    // Parse page and size with defaults
    let page = parseInt(req.query.page) || 1;
    let size = parseInt(req.query.size) || 10;
    
    // Validate parameters
    if (page < 0 || size < 0 || size > 200) {
        return res.status(400).json({
            errors: [{ message: 'Requires valid page and size params' }],
            count: 0,
            pageCount: 0
        });
    }

    // Calculate offset
    const offset = size * (page - 1);
    
    const students = await Student.findAndCountAll({
        limit: size,
        offset: offset,
        order: [['lastName', 'ASC'], ['firstName', 'ASC']]
    });

    // Calculate total pages
    const pageCount = Math.ceil(students.count / size);

    res.json({
        rows: students.rows,
        count: students.count,
        page: page,
        pageCount: pageCount
    });
});
    

This is similar to how a textbook is organized - we break the content into manageable chunks (pages) while keeping track of the total content (page count).

Phase 3: Supply Management


router.get('/supplies/scissors/calculate', async (req, res) => {
    // Find current scissors inventory
    const scissorsCount = await Supply.findAll({
        where: {
            name: 'Safety Scissors',
            handed: {
                [Op.in]: ['left', 'right']
            }
        },
        attributes: [
            'handed',
            [sequelize.fn('COUNT', sequelize.col('id')), 'count']
        ],
        group: ['handed']
    });

    // Calculate needed supplies
    const studentCounts = await Student.findAll({
        attributes: [
            'leftHanded',
            [sequelize.fn('COUNT', sequelize.col('id')), 'count']
        ],
        group: ['leftHanded']
    });

    // Calculate differences
    const result = calculateNeededSupplies(scissorsCount, studentCounts);
    
    res.json(result);
});
    

Common Patterns and Best Practices

Error Handling

Always validate input and return clear error messages:


const validatePaginationParams = (page, size) => {
    const errors = [];
    if (page < 1) errors.push('Page must be greater than 0');
    if (size < 1 || size > 200) errors.push('Size must be between 1 and 200');
    return errors;
};
    

Query Optimization

Use efficient querying techniques to minimize database load:

Testing Strategies

Important scenarios to test:

Real World Applications

This pattern of development can be applied to many business scenarios:

Performance Considerations

Key areas to monitor and optimize: