Set Up a Rails API with PostgreSQL
Build a versioned JSON API with Rails, PostgreSQL, and CORS support.
Introduction
30 Second Summary
Every time you open an app on your phone or browse a website, there is an API working behind the scenes to fetch and store data. APIs are the invisible backbone of modern software.
In this project, you will build a Bookshelf API from scratch using Ruby on Rails and PostgreSQL. You will create endpoints that let any client create, read, update, and delete books through clean JSON responses.
What You'll Build
A Rails API-only application that serves JSON responses for managing a collection of books, backed by PostgreSQL and configured with CORS for cross-origin requests.
By the end of this project, you'll have:
- A Rails API-only application running locally with PostgreSQL as the database backend.
- A versioned API namespace (api/v1) with a fully functional CRUD controller serving JSON.
- All five endpoints verified with curl from the terminal, confirming correct HTTP status codes and responses.
- Secret Mission: Add model validations and a reusable error handling concern for production-ready error responses.
Are there any prerequisites?
No prior experience with Rails or PostgreSQL is needed. This guide walks you through installing every tool from scratch, including Ruby, Rails, and PostgreSQL.
Create the Rails API Application
Every API starts with a solid foundation. In this step, you'll generate a Rails application purpose-built for serving JSON.
Rails has a built-in API mode that strips away browser-oriented features like views, cookies, and session handling. Combined with PostgreSQL as the database, this gives you a lean starting point for building a production-ready backend.
In this step, get ready to:
- Generate a Rails API-only application with PostgreSQL.
- Create the development database and start the server.
- Verify the health check endpoint responds correctly.
Generate the API-only Rails application
The --api flag tells Rails to generate a slimmer application without views, assets, or browser middleware. The --database=postgresql flag configures the app to use PostgreSQL instead of the default SQLite.
- Move to your Desktop by running this command:
cd ~/Desktop
- Generate your Rails API application by running:
rails new bookshelf_api --api --database=postgresql
What does this command do?
- rails new bookshelf_api creates a new Rails project in a folder called bookshelf_api.
- --api configures the app for API-only mode. Your ApplicationController inherits from ActionController::API instead of ActionController::Base.
- --database=postgresql adds the pg gem and generates a config/database.yml pre-configured for PostgreSQL.
You'll see a series of create messages as Rails generates files, followed by bundle install to install dependencies. This may take a minute or two.
- Once the command finishes, move into the project folder by running:
cd bookshelf_api
- Confirm the project files exist by running:
ls
You should see folders like app, config, db, and files like Gemfile and Rakefile listed in the output.
Command not found or gem errors?
- If you see command not found: rails, confirm Rails is installed by running gem install rails.
- If bundle install fails with a pg gem error, make sure PostgreSQL is installed and its pg_config is in your PATH.
Help me troubleshoot my Rails API project generation
- Open the project in VS Code by running:
code .
Code command not recognized?
If your terminal doesn't recognize code, open VS Code manually. Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar, type Visual Studio Code, and press Enter.
Then click File in the menu bar, select Open Folder, and navigate to Desktop/bookshelf_api.
Create the database and start the server
Your Rails app is configured to talk to PostgreSQL, but the actual database doesn't exist yet. You need to create it before the app can store any data.
- Create the development database by running:
bin/rails db:create
You should see output confirming that the bookshelf_api_development database was created.
Database creation failed?
- If you see a connection error, PostgreSQL may not be running. On macOS with Homebrew, start it with brew services start postgresql.
- If you see a role error, create a PostgreSQL superuser matching your system username by running createuser -s $(whoami).
Help me fix my PostgreSQL database creation error
- Start the Rails development server by running:
bin/rails server
You should see output showing the server listening on http://127.0.0.1:3000. The terminal will stay occupied while the server runs.
Port already in use?
If you see an error about port 3000 being in use, another process is occupying it. Try running the server on a different port with bin/rails server -p 3001.
Help me fix my Rails server startup error
Verify the health check endpoint
Rails automatically generates a health check endpoint at /up. This endpoint returns a 200 status when the application has booted successfully.
- Open your browser and visit:
http://localhost:3000/up
You should see a page with a solid green background. This confirms the server is running and healthy.
Why green?
The health check controller renders a minimal HTML page. A green background means the app booted without exceptions (200 OK). A red background would mean something went wrong (500 error).
Use curl as an alternative
If you prefer testing from the terminal, open a new terminal tab and run:
curl -I http://localhost:3000/up
You should see HTTP/1.1 200 OK in the response headers.
Page not loading?
- Make sure the Rails server is still running in your other terminal tab. Look for the process output to confirm it hasn't crashed.
- Double-check the URL includes the port: localhost:3000/up.
Help me troubleshoot my Rails health check endpoint
Your Rails API application is up and running. Next, you'll configure CORS so that frontend applications can make requests to your API.
Configure CORS
Your Rails server is running and responding to requests. Now it's time to make sure other applications can actually talk to your API.
When a frontend app on a different domain tries to call your API, browsers block the request by default. This security feature is called CORS (Cross-Origin Resource Sharing). You'll configure the rack-cors gem to allow these cross-origin requests.
In this step, get ready to:
- Enable the rack-cors gem in your Gemfile.
- Configure CORS settings to allow all origins for development.
- Verify CORS middleware is active in the stack.
Uncomment the rack-cors gem
Rails already added a commented-out rack-cors gem to your Gemfile when you generated the API app. You just need to activate it.
- In VS Code, open the Gemfile from the file explorer sidebar.
- Scroll down and find this commented-out line:
# gem "rack-cors"
- Remove the # and the space before gem so the line now looks like this:
gem "rack-cors"
- Save Gemfile.
- Install the newly activated gem by running this command in your terminal:
bundle install
- You should see output confirming the bundle is complete and the rack-cors gem is included.
Bundle install failing?
Make sure you removed only the # character and the space. The line should read exactly gem "rack-cors" with nothing before it.
If you see a network error, check your internet connection and try running bundle install again.
Help me troubleshoot this
Configure CORS to allow all origins
Rails also generated a CORS initializer file when you created the API app. You'll replace its commented-out contents with a working configuration that permits requests from any origin during development.
- In VS Code, open config/initializers/cors.rb.
- Select all the existing content in the file and replace it with the following code:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "*"
resource "*",
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
What does this code do?
- insert_before 0 places the CORS middleware at the very top of the request pipeline, so it runs before anything else.
- origins "*" allows requests from any domain. In production, you'd replace this with your frontend's actual URL.
- resource "*" applies CORS handling to all routes in your API.
- methods lists every HTTP verb your API supports, including :options for preflight requests that browsers send automatically.
- Save config/initializers/cors.rb.
Why allow all origins in development?
Using "*" as the origin is only safe for local development. It lets you test from any frontend tool (React dev server, Postman, a browser) without CORS errors getting in the way.
Before deploying to production, you'd lock this down to only your frontend's domain.
✔️ Awesome, I've got everything!
Great. Double check you've saved the file before moving on.
ⓧ I'd like to double check the full code
Here's the complete config/initializers/cors.rb file:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "*"
resource "*",
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
Verify CORS is in the middleware stack
Rails provides a command to list every middleware in the request pipeline. You'll use it to confirm rack-cors is active and positioned at the top.
- Check the middleware stack by running this command:
bin/rails middleware
- You should see use Rack::Cors as the very first entry in the output list.
What should I see?
The output prints one middleware per line in execution order. Because you used insert_before 0, Rack::Cors appears at position zero (the very top). Every incoming request hits CORS handling before any other middleware processes it.
Don't see Rack::Cors in the output?
Make sure you saved both the Gemfile and config/initializers/cors.rb before running the command.
Confirm that bundle install completed successfully earlier. If you skipped it, run it now and try bin/rails middleware again.
Help me figure out what's wrong
Your API is now ready to accept requests from any frontend application. Next up, you'll create the Book model and database table to give your API some data to work with.
Create the Book Model
Your Rails API is running and CORS is configured. Now you need somewhere to store data.
In this step, you will create a Book model backed by PostgreSQL. The model defines what a book looks like (its fields), and a migration tells the database to create the matching table.
In this step, get ready to:
- Generate a Book model with five fields using the Rails model generator.
- Run the database migration to create the books table.
- Seed the database with sample books and verify the data in the Rails console.
Generate the Book model
Rails generators create boilerplate files for you. The model generator produces a model file and a migration file in one command.
- Generate the Book model by running this command in your terminal:
bin/rails generate model Book title:string author:string isbn:string description:text published_date:date
What does this command do?
The model generator creates two key files. app/models/book.rb defines the Book class that maps to a database table. db/migrate/XXXXXXXXXXXXXXXX_create_books.rb (where X is a timestamp) contains instructions for building the table in PostgreSQL.
Each field is defined as name:type. The description field uses text instead of string because it can hold longer content.
You should see output listing the created files, including the model and migration.
Next, you need to add database constraints. The title and author fields should be required at the database level.
- In VS Code, open the migration file at db/migrate/. It will be named something like 20260703000000_create_books.rb (the numbers will differ).
- Find the line that says t.string :title and change it to:
t.string :title, null: false
- Find the line that says t.string :author and change it to:
t.string :author, null: false
What does null: false do?
Adding null: false tells PostgreSQL to reject any row where that column is empty. This enforces data integrity at the database level. Every book must have a title and an author.
- Save the migration file.
✔️ Awesome, I've got everything!
Great. Double check you have saved the migration file before moving on.
ⓧ I'd like to double check the full code
Your migration file should look exactly like this:
class CreateBooks < ActiveRecord::Migration[8.1]
def change
create_table :books do |t|
t.string :title, null: false
t.string :author, null: false
t.string :isbn
t.text :description
t.date :published_date
t.timestamps
end
end
end
Run the migration to create the books table
The migration file is just a set of instructions. You need to run it to actually create the table in your PostgreSQL database.
- Run the migration by entering this command:
bin/rails db:migrate
- You should see output like CreateBooks: migrated confirming the table was created.
What just happened?
Rails read your migration file and executed the SQL commands to create a books table in the bookshelf_api_development database. The table has columns for id, title, author, isbn, description, published_date, created_at, and updated_at.
The t.timestamps line automatically added created_at and updated_at columns that Rails manages for you.
Seeing a migration error?
If you see a syntax error, open the migration file and check that null: false has a colon before false and a comma after the field type.
If the database connection fails, make sure PostgreSQL is running. You can check with brew services list on macOS.
Help me troubleshoot my Rails migration error.
Add seed data and verify in the Rails console
An empty database is not very useful for testing. Seed data gives you sample records to work with when you build your API endpoints later.
- In VS Code, open db/seeds.rb from the db folder in the Explorer sidebar.
- Replace the contents of the file with this seed data:
# Remove any existing books to avoid duplicates
Book.destroy_all
# Create three sample books
books = Book.create!([
{
title: "The Pragmatic Programmer",
author: "David Thomas, Andrew Hunt",
isbn: "978-0135957059",
description: "A guide to becoming a better programmer.",
published_date: "2019-09-23"
},
{
title: "Clean Code",
author: "Robert C. Martin",
isbn: "978-0132350884",
description: "A handbook of agile software craftsmanship.",
published_date: "2008-08-01"
},
{
title: "Design Patterns",
author: "Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides",
isbn: "978-0201633610",
description: "Elements of reusable object-oriented software.",
published_date: "1994-10-31"
}
])
# Print confirmation
puts "Seeded #{books.count} books."
What does this code do?
Book.destroy_all clears any existing records first. This makes the seed file safe to run multiple times without creating duplicates.
Book.create! inserts each book into the database. The bang (!) makes it raise an error if something goes wrong, so you know immediately if your data is invalid.
- Save db/seeds.rb.
- Run the seed file by entering this command:
bin/rails db:seed
- You should see Seeded 3 books. printed in the terminal.
Now verify the data landed in the database using the Rails console.
- Open the Rails console by running:
bin/rails console
- Check how many books are in the database by typing:
Book.count
- You should see => 3 returned in the console output.
- Exit the console by typing exit and pressing Enter.
Book.count not returning 3?
If you see 0, the seed command did not run successfully. Exit the console with exit and run bin/rails db:seed again. Look for error messages about missing columns or validation failures.
If you see a NameError for Book, make sure you ran bin/rails db:migrate before seeding. The model needs its table to exist.
Help me fix my Rails seed data issue.
✔️ Awesome, I've got everything!
Great. Make sure you have saved db/seeds.rb and confirmed Book.count returns 3.
ⓧ I'd like to double check the full code
Your db/seeds.rb should look exactly like this:
Book.destroy_all
books = Book.create!([
{
title: "The Pragmatic Programmer",
author: "David Thomas, Andrew Hunt",
isbn: "978-0135957059",
description: "A guide to becoming a better programmer.",
published_date: "2019-09-23"
},
{
title: "Clean Code",
author: "Robert C. Martin",
isbn: "978-0132350884",
description: "A handbook of agile software craftsmanship.",
published_date: "2008-08-01"
},
{
title: "Design Patterns",
author: "Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides",
isbn: "978-0201633610",
description: "Elements of reusable object-oriented software.",
published_date: "1994-10-31"
}
])
puts "Seeded #{books.count} books."
Your app/models/book.rb should look like this:
class Book < ApplicationRecord
end
Your Book model is ready and loaded with sample data. Next up, you will build the API routes and controller that expose this data as JSON endpoints.
Build the Versioned API Routes and Controller
Your Book model and database are ready with seed data. Now you need a way for clients to actually reach that data over HTTP.
In this step, you'll define versioned API routes and build the controller that handles each request. This gives your API a clean URL structure like /api/v1/books that can evolve without breaking existing clients.
In this step, get ready to:
- Define namespaced routes in config/routes.rb.
- Create the BooksController with full CRUD actions.
- Verify routes with bin/rails routes.
Define namespaced API routes
APIs change over time. API versioning lets you introduce breaking changes under a new version prefix (like /v2) while existing clients continue using /v1 undisturbed.
Rails makes this easy with nested namespace blocks in the routes file. Each namespace adds a URL prefix and expects the controller to live in a matching directory.
- In VS Code, open config/routes.rb from the Explorer sidebar.
- Replace the entire contents of the file with the following code:
Rails.application.routes.draw do
# Nest book routes under /api/v1/ for versioning
namespace :api do
namespace :v1 do
resources :books
end
end
get "up" => "rails/health#show", as: :rails_health_check
end
What does this code do?
The namespace :api block adds /api to every route inside it. The nested namespace :v1 adds /v1 on top of that.
Together, resources :books generates all standard RESTful routes (index, show, create, update, destroy) under the combined prefix /api/v1/books. Rails also expects the matching controller to live at app/controllers/api/v1/books_controller.rb.
Why namespaced routes instead of header-based versioning?
URL-based versioning is the most visible and beginner-friendly approach. It uses standard Rails routing conventions and produces routes that are easy to test with curl. Header-based versioning adds complexity without teaching additional Rails concepts.
- Save config/routes.rb.
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before moving on.
ⓧ I'd like to double check the full code
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :books
end
end
get "up" => "rails/health#show", as: :rails_health_check
end
Create the BooksController
The routes you just defined expect a controller at a specific path. You need to create both the directory structure and the controller file that handles each book endpoint.
- Create the nested controller directory by running this command in your terminal:
mkdir -p app/controllers/api/v1
- Confirm the directory exists by running:
ls app/controllers/api/v1
The command should return without error. The directory is empty for now.
- Create a new file at app/controllers/api/v1/books_controller.rb by right-clicking the v1 folder in the Explorer sidebar and selecting New File.
- Name the file books_controller.rb.
- Paste the following code into the new file:
module Api
module V1
class BooksController < ApplicationController
# Find the book before show, update, or destroy actions
before_action :set_book, only: [:show, :update, :destroy]
# GET /api/v1/books
def index
@books = Book.all
render json: @books
end
# GET /api/v1/books/:id
def show
render json: @book
end
# POST /api/v1/books
def create
@book = Book.new(book_params)
if @book.save
render json: @book, status: :created
else
render json: { errors: @book.errors }, status: :unprocessable_entity
end
end
# PATCH /api/v1/books/:id
def update
if @book.update(book_params)
render json: @book
else
render json: { errors: @book.errors }, status: :unprocessable_entity
end
end
# DELETE /api/v1/books/:id
def destroy
@book.destroy
head :no_content
end
private
# Look up a book by ID from the URL
def set_book
@book = Book.find(params[:id])
end
# Only allow trusted parameters through
def book_params
params.require(:book).permit(:title, :author, :isbn, :description, :published_date)
end
end
end
end
What does this code do?
The module Api; module V1 wrapper matches the directory structure Rails expects for namespaced routes. Every action renders JSON instead of HTML.
The before_action :set_book callback automatically finds the requested book by ID before show, update, and destroy run. This avoids repeating the same lookup in each action.
The book_params method uses strong parameters to whitelist only the fields a client is allowed to set. This prevents mass-assignment vulnerabilities where a client could overwrite fields you didn't intend to expose.
- Save books_controller.rb.
File not appearing in the right place?
Make sure the file lives at app/controllers/api/v1/books_controller.rb. If you created it in the wrong folder, drag it to the correct location in the Explorer sidebar.
The module nesting (Api::V1) must match the folder path exactly. A mismatch causes Rails to fail with a routing error.
Help me debug my controller file path
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before moving on.
ⓧ I'd like to double check the full code
module Api
module V1
class BooksController < ApplicationController
before_action :set_book, only: [:show, :update, :destroy]
def index
@books = Book.all
render json: @books
end
def show
render json: @book
end
def create
@book = Book.new(book_params)
if @book.save
render json: @book, status: :created
else
render json: { errors: @book.errors }, status: :unprocessable_entity
end
end
def update
if @book.update(book_params)
render json: @book
else
render json: { errors: @book.errors }, status: :unprocessable_entity
end
end
def destroy
@book.destroy
head :no_content
end
private
def set_book
@book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:title, :author, :isbn, :description, :published_date)
end
end
end
end
Verify routes are registered
Rails has a built-in command that lists every registered route. This confirms your namespaces, HTTP verbs, and controller mappings are wired up correctly.
- Check all registered routes by running this command:
bin/rails routes
You should see a table with columns for Prefix, Verb, URI Pattern, and Controller#Action. Every book route is prefixed with /api/v1/books and maps to api/v1/books#index, api/v1/books#show, api/v1/books#create, api/v1/books#update, and api/v1/books#destroy.
Don't see any book routes?
Make sure you saved config/routes.rb after editing it. The resources :books line must be nested inside both the namespace :api and namespace :v1 blocks.
If you see a syntax error, check that every do has a matching end in your routes file.
Help me debug my missing routes
Your API skeleton is fully wired. Next up, you'll start the server and test every endpoint with curl to prove your API handles real HTTP requests.
Test Your API with curl
Your routes are configured and the controller is built. Now it is time to prove everything works by sending real HTTP requests to your API.
In this step, you will use curl to test every endpoint your API exposes. This is how backend developers verify their APIs before connecting a frontend.
In this step, get ready to:
- Test reading all books and a single book with GET requests.
- Test creating a new book with a POST request.
- Test updating and deleting a book with PATCH and DELETE requests.
Start the Rails server and test the read endpoints
Your API needs a running server to respond to requests. You will start the server in one terminal and run curl commands in a second terminal.
- Switch back to the terminal where your bookshelf_api project is open.
- Start the Rails server by running this command:
bin/rails server
You should see output that includes Listening on http://127.0.0.1:3000. The server is now running and waiting for requests.
- Open a new terminal tab or window. On macOS, press Cmd+T in Terminal to open a new tab.
- Navigate back to your project folder by running:
cd ~/Desktop/bookshelf_api
Why a second terminal?
The Rails server occupies its terminal while running. You need a separate terminal to send curl requests to it.
- Test the index endpoint to fetch all books by running:
curl http://localhost:3000/api/v1/books
You should see a JSON array containing your 3 seeded books, each with fields like id, title, author, and isbn.
Seeing 'Connection refused'?
Make sure the Rails server is still running in your other terminal tab. If it crashed, switch back and run bin/rails server again.
Also confirm you are hitting port 3000. The URL must be exactly http://localhost:3000/api/v1/books.
Help me debug a connection refused error when curling my Rails API.
- Test the show endpoint to fetch a single book by running:
curl http://localhost:3000/api/v1/books/1
You should see a single JSON object for "The Pragmatic Programmer" with all its attributes.
What do these responses confirm?
A successful JSON array from the index endpoint confirms your Book.all query works and the controller renders JSON correctly.
A single book JSON from the show endpoint confirms Book.find(params[:id]) is resolving the ID from the URL. Both responses return HTTP status 200.
Test creating a new book with POST
Creating a resource requires sending JSON data in the request body. You also need to set the Content-Type header so Rails knows to parse the body as JSON.
- Create a new book by running this command:
curl -X POST http://localhost:3000/api/v1/books \
-H "Content-Type: application/json" \
-d '{"book": {"title": "Eloquent Ruby", "author": "Russ Olsen", "isbn": "978-0321584106", "description": "A guide to writing beautiful Ruby code.", "published_date": "2011-02-07"}}'
You should see a JSON object for the newly created book. The response includes an id field (likely 4) and all the attributes you sent.
What does this command do?
- -X POST sets the HTTP method to POST instead of the default GET.
- -H "Content-Type: application/json" tells Rails the request body is JSON.
- -d '{...}' sends the JSON data as the request body. The data is nested under a book key because the controller uses params.require(:book).
A successful response returns HTTP status 201 (Created), confirming the book was saved to PostgreSQL.
Seeing a 422 Unprocessable Entity error?
Double-check that your JSON is nested under a book key. Sending {"title": "..."} without the wrapper will cause params.require(:book) to raise an error.
Also verify there are no typos in the JSON. A missing quote or comma will cause a parse failure.
Help me fix a 422 error when POSTing to my Rails API.
Test updating and deleting a book
The final two operations let you modify existing data and remove records. You will use the id from the book you just created.
Check your book ID
The commands below use ID 4 for the book you just created. If your POST response showed a different id value, substitute that number in the URLs below.
- Update the book's title by running this command:
curl -X PATCH http://localhost:3000/api/v1/books/4 \
-H "Content-Type: application/json" \
-d '{"book": {"title": "Eloquent Ruby (2nd Edition)"}}'
You should see the full book JSON with the updated title. The response returns HTTP status 200, confirming the update was saved.
- Delete the book by running this command:
curl -i -X DELETE http://localhost:3000/api/v1/books/4
You should see response headers that include HTTP/1.1 204 No Content. The response body is empty because the book no longer exists.
Why use -i for the DELETE request?
A 204 response has no body, so curl shows nothing by default. The -i flag includes the response headers in the output, letting you confirm the 204 No Content status code.
- Confirm the book was deleted by running the index endpoint again:
curl http://localhost:3000/api/v1/books
You should see your original 3 seeded books. The "Eloquent Ruby" book you created and then deleted is no longer in the list.
Seeing a 404 Not Found error?
If you see a routing error or 404 on PATCH or DELETE, check that the ID in your URL matches a book that exists. Run the index endpoint (GET /api/v1/books) to see which IDs are available.
If you already deleted the book, the same ID will return a 404 on subsequent requests. That is expected behavior.
Help me troubleshoot a 404 error on my PATCH or DELETE request.
You have now tested all five CRUD operations. Your Rails API correctly handles GET (200), POST (201), PATCH (200), and DELETE (204) with proper JSON responses and status codes.
Secret mission
Add Validations and Error Handling
Your API currently accepts any data and crashes ungracefully when a record isn't found. Can you add model validations and a reusable error handling concern to make your API production-ready?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources available for future development or delete them entirely.
Resources you used:
- The bookshelf_api_development PostgreSQL database (local, free, uses only disk space).
- The bookshelf_api project directory on your Desktop.
Keep everything running
No action needed. Choose this if you're still actively building or plan to extend the API with more resources.
- Your PostgreSQL database and project files stay on your machine.
- There is no ongoing cost. PostgreSQL only uses local disk space.
- You can restart the server anytime with bin/rails server from the project directory.
Pause - I'll come back to this later
Shut down the Rails server to free up the port. Your files and database stay intact for when you return.
- Press Ctrl+C in the terminal where the Rails server is running to stop it.
- Your database and project files remain untouched.
- When you're ready to resume, open a terminal in the bookshelf_api directory and run bin/rails server.
Delete - I don't want to use this again
Remove the PostgreSQL database and project directory completely.
- Stop the Rails server by pressing Ctrl+C if it is still running.
- Drop the database by running this command from the project directory:
bin/rails db:drop
- Move back to your Desktop and delete the project folder:
cd ~/Desktop
rm -rf bookshelf_api
On Windows, use this command instead to remove the folder:
cd ~\Desktop
rmdir /s /q bookshelf_api
- Ruby, Rails, and PostgreSQL remain installed on your system. These are general development tools you may use for other projects.
Nice Work!
Nice Work!
Nice work! You just built a fully functional REST API from scratch using Ruby on Rails and PostgreSQL.
You've learned how to:
- Create a Rails API-only application with PostgreSQL, including database migrations and seed data.
- Configure CORS middleware with the rack-cors gem so frontend clients can make cross-origin requests to your API.
- Implement API versioning using namespaced routes and build a full CRUD controller that returns JSON with proper HTTP status codes.
- Secret Mission: Added model validations and a reusable ErrorHandler concern for consistent, production-ready error responses.
Ready to quiz yourself?