Sobes.tech
Middle

Where do you draw the line between front-end and back-end development?

sobes.tech AI

Answer from AI

The boundary is at the API level. The backend is responsible for:

  • Business logic
  • Data management (database, cache)
  • Authorization and authentication (if not separated into a different service)
  • Providing data and functions via API (REST, GraphQL, etc.)

The frontend is responsible for:

  • User interface (HTML, CSS)
  • User interaction (JavaScript/Ruby with Hotwire/etc.)
  • Consuming data and calling backend functions via API
  • Presenting data received from the backend

In the classic approach, the backend generates HTML pages, and the frontend only adds interactivity. In SPA (Single Page Application), the boundary is more distinct, as interaction occurs exclusively through the API.

Example:

# app/controllers/api/users_controller.rb
# Backend: Responsible for fetching the user from the database and formatting data for the frontend
class Api::UsersController < ApplicationController
  def show
    user = User.find(params[:id])
    render json: user.as_json(only: [:id, :name, :email])
  end
end
// app/javascript/packs/user_profile.js
// Frontend: Responsible for requesting data from the backend and displaying it
document.addEventListener('DOMContentLoaded', () => {
  fetch(`/api/users/${userId}`) // userId obtained on the frontend
    .then(response => response.json())
    .then(data => {
      document.getElementById('user-name').innerText = data.name;
      document.getElementById('user-email').innerText = data.email;
    });
});

In hybrid approaches like Hotwire, some rendering logic can be shifted to the backend, but interaction is still built on data or HTML fragment transfer via specific protocols (e.g., Turbo Streams).

# app/controllers/posts_controller.rb
# Backend: Renders HTML for Turbo Stream
class PostsController < ApplicationController
  def create
    @post = Post.new(post_params)
    if @post.save
      render turbo_stream: turbo_stream.append(:posts, partial: 'posts/post', locals: { post: @post })
    else
      # Error handling
    end
  end

  private

  def post_params
    params.require(:post).permit(:body)
  end
end
<!-- _post.html.erb -->
<!-- HTML fragment sent to the frontend via Turbo Stream -->
<div>
  <%= post.body %>
</div>

The main principle: the backend provides data and services, and the frontend consumes and visualizes them for the user.