DJ-1: Creating a Rails API with Postgres and Docker

betosardinha

Beto Sardinha

Posted on January 13, 2023

DJ-1: Creating a Rails API with Postgres and Docker

Hello there to you who are reading this post for some reason!

Me showing my code

I don't know how to start a development journal properly, it's the first time I've done something like this and it's also the first time I've written a full article in English. So I apologize for future language mistakes.

But I think you're here because maybe you want to create a small Rails project and learn a little more about Docker. And that's exactly what I want too! So this journal entry is my attempts to build a foundation for projects, and a step by step for you to build one to.

So let's go!

Installing Ruby and Rails on your machine

First thing is to download and install ruby, you can either use the default installation or some version manager. In my case I'm using rbenv, and if you're using linux I recommend it too, it's simple and I like it a lot.

For this project I'm using ruby ​​3.2.0, but feel free to use the most stable/latest version available.

After installing ruby, I installed the rails gem and a few more rubocop-related ones to lint right into my code editor (I'm using vscode with the ruby ​​and ruby-rubocop extensions).

gem install rails
gem install rubocop
gem install rubocop-rails
gem install rubocop-performance
Enter fullscreen mode Exit fullscreen mode

Creating a Rails API with Postgres

Where I work and in several other projects I use PostgreSQL, I'm used to the behavior and the interface, so when creating this project I configured it to be the database. And since I'm working on an API project I set the flag for Rails to not create front-end files (I'll create a separate project to consume the API later, probably in VueJS).

I put the name as articles-api for simplicity (since I'm not good with names).

rails new articles-api --api --database=postgresql
Enter fullscreen mode Exit fullscreen mode

I also created the license file as MIT to specify anyone's right to download, use and modify the project.

Using Docker to run the project

Despite being used to Postgres, I don't want to install it on my machine, nor use rails directly. And for many other compatibility reasons with other computers (and even for you to download and run this project) I decided to use Docker containers. If you want, just follow this installation step by step.

I created my Dockerfile with the necessary settings for the Rails project, and an entrypoint.sh, which deletes the server.pid file that sometimes is not removed if the container is not shut down correctly (generating an error when running it again).

Another point to be mentioned is the BUNDLE_FROZEN configuration, which will always force a comparison between the Gemfile and the Gemfile.lock, and if the versions are different the project will not run (more a security configuration).

Dockerfile

# Put the ruby ​​version you are using
FROM ruby:3.2.0

# Install the necessary libraries
RUN apt-get update -qq && apt-get install -y postgresql-client

# BUNDLE_FROZEN setting
RUN bundle config --global frozen 1

# Set working directory
WORKDIR /articles-api

# Copy and install the project gems
COPY Gemfile /articles-api/Gemfile
COPY Gemfile.lock /articles-api/Gemfile.lock
RUN bundle install

# Run entrypoint.sh to delete server.pid
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

# Listen on this specified network port
EXPOSE 3000

# Run rails server
CMD ["rails", "server", "-b", "0.0.0.0"]
Enter fullscreen mode Exit fullscreen mode

entrypoint.sh

#!/bin/bash
set -e

rm -f /articles-api/tmp/pids/server.pid

exec "$@"
Enter fullscreen mode Exit fullscreen mode

Setting up database variables

Before building the project, I corrected the database settings so that the project could connect to the database container. I put host, username and password as environment variables, which will be passed both in the container compose file and in an .env file in case it needs to be run locally at some point. Variables are in default scope, so development and test database have the same credentials.

config/database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: <%= ENV['DBHOST'] %>
  username: <%= ENV['DBUSER'] %>
  password: <%= ENV['DBPASS'] %>

development:
  <<: *default
  database: articles_api_development

test:
  <<: *default
  database: articles_api_test

production:
  <<: *default
  database: articles_api_production
  username: articles_api
  password: <%= ENV["ARTICLES_API_DATABASE_PASSWORD"] %>
Enter fullscreen mode Exit fullscreen mode

.env

DBHOST=localhost
DBUSER=postgres
DBPASS=password
Enter fullscreen mode Exit fullscreen mode

Creating a docker-compose file

Finally, to be able to run the project, I created a docker-compose.yml file containing the following services:

  • A database using Postgres image;
  • A database administrator with the pgAdmin image (optional);
  • Rails running my project with the Dockerfile;

docker-compose.yml

version: '3.8'

services:
  articles-db:
    image: postgres
    container_name: articles-db
    volumes:
      - postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: "articles_api_development"
      POSTGRES_USER: "postgres"
      POSTGRES_PASSWORD: "password"
    ports:
      - "5432:5432"
    networks:
      - articles-api-network

  articles-pgadmin:
    image: dpage/pgadmin4
    container_name: articles-pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: "user@articles.com"
      PGADMIN_DEFAULT_PASSWORD: "password"
    ports:
      - "15432:80"
    depends_on:
      - articles-db
    networks:
      - articles-api-network

  articles-api:
    image: articles-api
    container_name: articles-api
    build: .
    environment:
      - DBHOST=articles-db
      - DBUSER=postgres
      - DBPASS=password
    volumes:
      - .:/articles-api
    ports:
      - "3000:3000"
    depends_on:
      - articles-db
    networks:
      - articles-api-network

networks:
  articles-api-network:
    driver: bridge

volumes:
  postgres:
Enter fullscreen mode Exit fullscreen mode

Some points are interesting to talk about, all services have environment variables defined, with articles-db and articles-api database access variables, and articles-pgadmin access credentials to the administrator panel. Both the API and pgAdmin services are dependent on the database, so if you upload any of them, the database service automatically uploads.

Another point is that I created a separate network for the services to communicate with, I just find it easier to find it by giving it a specific name. If you want to learn more about compose file tags I recommend the official documentation, Docker is just scary at first, with time you get used to it.

Running the project

Finally, after so many configurations, I assembled the project's containers and loaded the services:

docker compose build
docker compose up
Enter fullscreen mode Exit fullscreen mode

I entered the api service bash to perform the database migration:

docker compose exec articles-api bash
rails db:create
rails db:migrate
Enter fullscreen mode Exit fullscreen mode

And right after restarting the services, I have confirmation that everything is working.

Rails service

I also accessed the pgAdmin service to test that the database was created correctly.

pgAdmin service

Conclusion

That's it for today! The project is working and ready for the next changes, I think after that I will make a new entry showing the process of creating a CI workflow with Github Actions.

If you have any questions or tips on how I can improve the project (or even this post), feel free to drop them in! My idea is that this development diary should be periodic.

Link to the project

https://github.com/betosardinha/articles-api

💖 💪 🙅 🚩
betosardinha
Beto Sardinha

Posted on January 13, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related