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

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
- PostgresSQL: https://www.postgresql.org/download/
- Docker: https://www.docker.com/products/docker-desktop/
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