Today nothing special. I created second CRUD based on first one. Components - our bikes have parts, so there would be bike_id. This is optional because we could have components not mounted to any bikes, but we still want to monitor their situation. So all code looks very similar but this time index should allow to filter by bike_id.

  def index(request)
    bike_id = request.params['bike_id']
    components = read_database

    if bike_id
      filtered_components = components.select { |component| component['bike_id'].to_i == bike_id.to_i }
      [200, { "content-type" => "application/json" }, [filtered_components.to_json]]
    else
      [200, { "content-type" => "application/json" }, [components.to_json]]
    end
  end

On component screen we probably wants to see everything, but on bike screen, would be nice to see, only components related to one bike with REST API it would be nice to have url like /bikes/:id/components but as I mentioned, there is an option to have components without bikes, and I don’t think duplicate endpoints its good practice

For the frontend, I still have one file with every case. I feel I have to rewrite it soon.

<!DOCTYPE html>
<html>
<head>
  <title>Bikes Frontend</title>
</head>
<body>
  <h1>Bikes Frontend</h1>
  <div id="bikesList"></div>
  <hr>
  <button onclick="viewAllComponents()">View All Components</button>
  <button onclick="createNewBike()">Add new bike</button>

  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const bikesList = document.getElementById('bikesList');

      // Fetch and display all bikes
      const fetchBikes = () => {
        fetch('/bikes')
          .then(response => response.json())
          .then(data => {
            bikesList.innerHTML = '';
            data.forEach(bike => {
              const bikeItem = document.createElement('div');
              bikeItem.innerHTML = `
                <p>ID: ${bike.id}</p>
                <p>Name: ${bike.name}</p>
                <button onclick="viewComponents(${bike.id})">View Components</button>
                <button onclick="updateBike(${bike.id})">Update</button>
                <button onclick="deleteBike(${bike.id})">Delete</button>
              `;
              bikesList.appendChild(bikeItem);
            });
          })
          .catch(error => {
            console.error('Error:', error);
            bikesList.textContent = 'Error retrieving bikes.';
          });
      };

      // Update a bike
      window.updateBike = bikeId => {
        const newBikeName = prompt('Enter the new name for the bike:');
        if (newBikeName) {
          fetch(`/bikes/${bikeId}`, {
            method: 'PUT',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({ name: newBikeName })
          })
            .then(response => {
              if (response.ok) {
                fetchBikes();
              } else {
                throw new Error('Failed to update bike.');
              }
            })
            .catch(error => {
              console.error('Error:', error);
              bikesList.textContent = 'Error updating bike.';
            });
        }
      };

      // Delete a bike
      window.deleteBike = bikeId => {
        if (confirm('Are you sure you want to delete this bike?')) {
          fetch(`/bikes/${bikeId}`, {
            method: 'DELETE'
          })
            .then(response => {
              if (response.ok) {
                fetchBikes();
              } else {
                throw new Error('Failed to delete bike.');
              }
            })
            .catch(error => {
              console.error('Error:', error);
              bikesList.textContent = 'Error deleting bike.';
            });
        }
      };

      window.viewComponents = bikeId => {
        fetch(`/components?bike_id=${bikeId}`)
          .then(response => response.json())
          .then(data => {
            bikesList.innerHTML = '';
            const backButton = document.createElement('button');
            const createButton = document.createElement('button');

            backButton.textContent = 'Back to Bikes';
            backButton.onclick = fetchBikes;

            createButton.textContent = 'Create Component';
            createButton.onclick = () => createComponent(bikeId);

            bikesList.appendChild(backButton);
            bikesList.appendChild(createButton);

            data.forEach(component => {
              const componentItem = document.createElement('div');
              componentItem.innerHTML = `
                <p>ID: ${component.id}</p>
                <p>Name: ${component.name}</p>
                <p>Description: ${component.description}</p>
                <button onclick="updateComponent(${component.id})">Update</button>
                <button onclick="deleteComponent(${component.id})">Delete</button>
              `;
              bikesList.appendChild(componentItem);
            });
          })
          .catch(error => {
            console.error('Error:', error);
            bikesList.textContent = 'Error retrieving components.';
          });
      };

      // Create a new component for a bike
      window.createComponent = bikeId => {
        const componentName = prompt('Enter the name for the new component:');
        const componentDescription = prompt('Enter the description for the new component:');
        if (componentName && componentDescription) {
          fetch(`/components`, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({ bike_id: bikeId, name: componentName, description: componentDescription })
          })
            .then(response => {
              if (response.ok) {
                viewComponents(bikeId);
              } else {
                throw new Error('Failed to create component.');
              }
            })
            .catch(error => {
              console.error('Error:', error);
              bikesList.textContent = 'Error creating component.';
            });
        }
      };

      // View all components
      window.viewAllComponents = () => {
        fetch('/components')
          .then(response => response.json())
          .then(data => {
            bikesList.innerHTML = '';
            const backButton = document.createElement('button');
            backButton.textContent = 'Back to Bikes';
            backButton.onclick = fetchBikes;

            bikesList.appendChild(backButton);

            data.forEach(component => {
              const componentItem = document.createElement('div');
              componentItem.innerHTML = `
                <p>ID: ${component.id}</p>
                <p>Name: ${component.name}</p>
                <p>Bike id: ${component.bike_id}</p>
                <p>Description: ${component.description}</p>
              `;
              bikesList.appendChild(componentItem);
            });
          })
          .catch(error => {
            console.error('Error:', error);
            bikesList.textContent = 'Error retrieving components.';
          });
      };

      // Create a new bike
      window.createNewBike = () => {
        const bikeName = prompt('Enter the name for the new bike:');
        if (bikeName) {
          fetch('/bikes', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({ name: bikeName })
          })
            .then(response => {
              if (response.ok) {
                fetchBikes();
              } else {
                throw new Error('Failed to create bike.');
              }
            })
            .catch(error => {
              console.error('Error:', error);
              bikesList.textContent = 'Error creating bike.';
            });
        }
      };

      // Fetch and display bikes when the page loads
      fetchBikes();
    });
  </script>
</body>
</html>

I have two separate Rack apps that are not aware of each other. Each app has its own database, and there is no connection or communication between them. This setup works well for my current needs since I am building a small application. However, it’s important to note that this approach may not be suitable in all scenarios. It’s essential to understand that instead of relying on ready-made solutions, it’s more beneficial to understand the underlying questions and principles involved. By copying the questions and gaining a deep understanding, I can make informed decisions and choose the most appropriate approach for each unique situation.

The code for this is available on GitHub