Building a REST API with Ruby on Rails and PostgreSQL: A Marketplace Example

Ryan Lindbeck
4 min readMay 26, 2024

--

Creating a REST API using Ruby on Rails is a powerful way to build robust backend services. In this guide, we’ll walk through the process of setting up a Rails application with a PostgreSQL database, focusing on a marketplace example where users can buy and sell products. By the end of this tutorial, you’ll have a functional API that can handle CRUD (Create, Read, Update, Delete) operations for users and products.

Prerequisites

Set up

Create a project folder on your local machine and navigate into it from your Terminal.

mkdir rails_rest_api_example
cd rails_rest_api_example

Next, let’s create a local dev container that we can use to set up our Ruby on Rails environment. Create a file in our project root called Dockerfile.dev and add the following:

FROM ruby:2.7

RUN apt-get update && apt-get install -y curl bash sqlite3

WORKDIR /project

RUN gem install nokogiri -v 1.15.6
RUN gem install rails

CMD ["bash"]

Build your dev container:

docker buildx build --platform linux/amd64 -t rails-dev -f Dockerfile.dev .

After the build is complete, let’s shell into it and create a volume that maps to our current working project director into the /project directory of the dev container:

docker run -it -p 3000:3000 -v $(pwd):/project rails-dev /bin/bash

Once you are inside of your dev container, let’s create a new rails project:

rails new rails_rest_api_example --database=postgresql

Install dependencies:

bundle install

Configuring the Database

Open the config/database.yml file and update the default settings to match your PostgreSQL configuration. It should look something like this:

default: &default
adapter: postgresql
encoding: unicode
pool: 5
username: your_postgres_username
password: your_postgres_password
host: host.docker.internal

development:
<<: *default
database: marketplace_api_development

test:
<<: *default
database: marketplace_api_test

production:
<<: *default
database: marketplace_api_production
username: marketplace_api
password: <%= ENV['MARKETPLACE_API_DATABASE_PASSWORD'] %>

After configuring the database, create the databases by running:

rails db:create

Generating Models and Migrations

For our marketplace, we’ll need models for User and Product. Let's generate these models along with the necessary migrations.

Generate the User model:

rails generate model User name:string email:string

Generate the Product model:

rails generate model Product title:string description:text price:decimal user:references

This will create two migration files. Run the migrations to create the tables in the database:

rails db:migrate

Setting Up Model Associations

Open the app/models/user.rb file and add the following code to set up the association between users and products:

class User < ApplicationRecord
has_many :products, dependent: :destroy

validates :name, presence: true
validates :email, presence: true, uniqueness: true
end

Next, open the app/models/product.rb file and set up the association with users:

class Product < ApplicationRecord
belongs_to :user

validates :title, presence: true
validates :description, presence: true
validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 }
end

Creating the Controllers

Generate controllers for users and products:

rails generate controller Users
rails generate controller Products

Open the app/controllers/users_controller.rb file and add the following code to define the RESTful actions:

class UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]

def index
@users = User.all
render json: @users
end

def show
render json: @user
end

def create
@user = User.new(user_params)
if @user.save
render json: @user, status: :created
else
render json: @user.errors, status: :unprocessable_entity
end
end

def update
if @user.update(user_params)
render json: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end

def destroy
@user.destroy
head :no_content
end

private

def set_user
@user = User.find(params[:id])
end

def user_params
params.require(:user).permit(:name, :email)
end
end

Next, open the app/controllers/products_controller.rb file and add the following code:

class ProductsController < ApplicationController
before_action :set_product, only: [:show, :update, :destroy]
before_action :set_user, only: [:index, :create, :show]

def index
@products = @user.products
if @products
render json: @products
else
render json: { error: 'Products not found' }, status: :not_found
end
end

def show
@product = @user.products.find_by(id: params[:id])
if @product
render json: @product
else
render json: { error: 'Product not found or does not belong to the user' }, status: :not_found
end
end

def create
@product = @user.products.new(product_params)
if @product.save
render json: @product, status: :created
else
render json: @product.errors, status: :unprocessable_entity
end
end

def update
if @product.update(product_params)
render json: @product
else
render json: @product.errors, status: :unprocessable_entity
end
end

def destroy
@product.destroy
head :no_content
end

private

def set_product
@product = Product.find(params[:id])
end

def set_user
@user = User.find(params[:user_id])
end

def product_params
params.require(:product).permit(:title, :description, :price, :user_id)
end
end

Configuring Routes

Open the config/routes.rb file and define the routes for users and products:

Rails.application.routes.draw do
resources :users do
resources :products
end
end

Testing the API

Start the Rails server:

rails server -b 0.0.0.0

Get all users:

GET http://localhost:3000/users

Create new user:

POST http://localhost:3000/users
{
"user": {
"name": "John Doe",
"email": "john@example.com"
}
}

Get a specific user:

GET http://localhost:3000/users/1

Update a user:

PATCH http://localhost:3000/users/1
{
"user": {
"name": "John Smith"
}
}

Delete a user:

DELETE http://localhost:3000/users/1

Get all products for a user:

GET http://localhost:3000/users/1/products

Create new product:

POST http://localhost:3000/users/1/products
{
"product": {
"title": "Sample Product",
"description": "This is a sample product description.",
"price": 29.99
}
}

Get a specific product:

GET http://localhost:3000/users/1/products/1

Update a product:

PATCH http://localhost:3000/users/1/products/1
{
"product": {
"price": 49.99
}
}

Delete a product:

DELETE http://localhost:3000/users/1/products/1

Conclusion

In this guide, we’ve built a simple REST API using Ruby on Rails and PostgreSQL for a marketplace application. We’ve covered creating models, setting up associations, building controllers, and configuring routes. This foundation can be extended with more features such as authentication, authorization, and advanced querying to build a fully functional marketplace. Rails’ convention-over-configuration philosophy makes it an excellent choice for rapidly developing robust and maintainable APIs.

See full code on Github: https://github.com/rycharlind/rails_rest_api_example

--

--

Ryan Lindbeck

Strategic Visionary Leader in Healthcare Analytics | Software & Data Engineer