A pipeline for a web API

The smallest API

The last project is the one from lesson 47, later in the course, with one addition that makes it testable from a pipeline: a health route that answers without touching the data.

require "json" require "webrick" TODOS = [] server = WEBrick::HTTPServer.new(Port: 8000) server.mount_proc("/health") { |_req, res| res.body = "ok" } server.mount_proc("/todos") do |req, res| res["Content-Type"] = "application/json" res.body = JSON.generate(TODOS) end server.start

Two routes are enough for the pipeline: one that says the process is up and one that returns data a request test can assert on.

Run it and check it

Start the server in one terminal, then use the health check from lesson 67 and a plain request for the list:

ruby app.rb

ruby health_check.rb

200 ok

ruby -e 'require "net/http"; puts Net::HTTP.get(URI("http://localhost:8000/todos"))'

[]

An empty list is the correct answer for a fresh process, and it is what a request test asserts before it posts an item. The health check exits zero on the 200; a server that did not start gives a refused connection and a nonzero status.

The pipeline file

The API job adds two things to the pattern from the previous lesson: a service container for the database, and an image that is built after the tests pass:

test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: "3.4" bundler-cache: true - run: bundle exec rake test image: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: docker build -t ci-demo . - uses: actions/upload-artifact@v4 with: name: image-tag path: VERSION

A job that needs a real database declares it as a service, and the tests wait for the port before they connect. In GitLab the same job uses services: with the same image, and the pipeline waits with a health check of its own.

The image build and the registry push are the two steps that need a Docker daemon; on a runner they are ordinary steps, on a laptop they are the reason the last section of this course is read rather than typed.

What to watch for

A service container is not ready when the job starts. Poll the port or the health route with a timeout, and fail with a message that says which service did not come up; a test that fails on a connection refused sends the reader to the wrong file.

Tag the image with the commit, not only with latest. A deploy that pulls latest cannot say which code is running, and a rollback has nothing to roll back to.

Next: The same program in Ruby and Python

Article author: Arthur Isaev

Banner image