Skip to Content
Course content

162: Building a Simple Task Management API

Click on the "Edit" button in the top corner of the screen to edit your slide content.

How do I actually structure the routes so they feel like a real REST API?

When you're building an API, the goal is predictability. You don't want to invent names like /get_all_tasks or /update_task_now. Instead, we use the HTTP verbs to describe the action and the URL to describe the resource. For a task manager, your resource is /tasks.

I usually stick to this pattern: GET /tasks to list everything, POST /tasks to create one, and then use a dynamic ID (like /tasks/:id) for specific actions. Here is how that looks in a Sinatra setup:

require 'sinatra'
require 'json'

# In-memory store for now
tasks = {}

get '/tasks' do
  content_type :json
  tasks.values.to_json
end

post '/tasks' do
  content_type :json
  data = JSON.parse(request.body.read)
  id = tasks.keys.max.to_i + 1
  
  new_task = { id: id, title: data['title'], completed: false }
  tasks[id] = new_task
  
  status 201
  new_task.to_json
end

Do I really need a full database for a simple prototype, or can I just use a hash?

Honestly? For a prototype or a coding challenge, a hash is plenty. It keeps the setup fast and avoids the headache of managing migrations and connection strings while you're still figuring out your API logic. I used a hash in the example above because it allows for O(1) lookup time when you're trying to find a task by its ID.

Just keep in mind that the moment you restart your server, your data vanishes. That's the trade-off. Once you're happy with the interface, swapping that hash for a database call using ActiveRecord or Sequel is usually a pretty straightforward refactor because the "shape" of the data stays the same.

How do I handle JSON requests and responses without it getting messy?

Ruby doesn't automatically turn a request body into a hash, which is a common point of confusion. You have to explicitly read the request body and parse it. I always recommend setting the content_type :json at the top of your routes so the client (like Postman or a frontend app) knows exactly what it's receiving.

If you find yourself repeating JSON.parse(request.body.read) in every single POST and PUT route, just move it into a helper method. It keeps your route handlers clean and focused on the business logic rather than the plumbing.

helpers do
  def json_params
    JSON.parse(request.body.read)
  rescue
    halt 400, { error: "Invalid JSON" }.to_json
  end
end

put '/tasks/:id' do
  content_type :json
  id = params[:id].to_i
  halt 404, { error: "Task not found" }.to_json unless tasks.key?(id)
  
  tasks[id].merge!(json_params)
  tasks[id].to_json
end

What's the best way to handle cases where a task doesn't exist?

Don't let your app crash with a 500 Internal Server Error just because a user typed the wrong ID. That's a bad experience. Instead, use the halt method in Sinatra to stop execution immediately and return a 404 Not Found.

I've seen a lot of beginners return a 200 OK with an error message in the body, but that's a "REST crime." If the resource isn't there, the HTTP status code should say so. It makes your API much easier to debug for whoever is consuming it.

delete '/tasks/:id' do
  content_type :json
  id = params[:id].to_i
  
  if tasks.delete(id)
    status 204 # No Content is the standard for successful deletes
  else
    halt 404, { error: "Can't delete what doesn't exist" }.to_json
  end
end



📋 Practical Task

Implementing a Priority Filter for the Task List

Right now, the GET /tasks endpoint returns every single task in the system. In a real app, that would eventually slow everything down. Your goal is to modify the GET /tasks route to support an optional priority query parameter.

  • Update the task creation logic (POST /tasks) to allow an optional priority field (e.g., "high", "medium", "low").
  • Modify the GET /tasks route so that if a user visits /tasks?priority=high, the API only returns tasks with that specific priority.
  • If no priority parameter is provided, the API should continue to return all tasks as it does now.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.