Photo by Sigmund on Unsplash

Document and test your API

Elson Akio Otake

--

Video showing the steps involved in creating API authentications

What do you think about creating API documentation where the user can test the endpoints? That’s what this article is about.

Good documentation is fundamental when creating your API. The more users understand how your API works, the greater the usage and success of your project. In the case of APIs, users are other developers. The idea is to allow these developers to understand the use of your API without the need to contact you or consult your source code.

I’ll give tips on creating your API documentation in a Rails application using the Rswag gem.

My suggestion for creating your API documentation is to use the same structure as a TDD. Write what the endpoint should do, what input data, and what output data. Provide examples to make it easy to understand. If there is a difference between the implementation and the documentation, your tests should catch them.

Let’s create a simple Rails application for APIs that stores user data such as email and description. We’ll call this application api-documentation.

rails new api-documentation --api

Let’s install the documentation gem. Include the Rswag and RSpec-rails gems in the Gemfile.

gem 'rswag'

group :development, :test do
gem 'rspec-rails'
end

Execute bundle install.

bundle install

Generate the RSpec and Rswag files.

rails generate rspec:install
rails generate rswag:install

Create a new folder called integration inside the spec folder. Create the users_spec.rb file inside the integration folder with the following content:

require 'swagger_helper'

describe 'Users' do
path '/api/v1/users' do
get 'List users' do
tags 'Users'
description 'List all users'
produces 'application/json'

response '200', 'OK' do
schema type: :object,
properties: {
id: { type: :integer },
email: { type: :string },
description: { type: :string },
created_at: { type: :string },
updated_at: { type: :string }
}
run_test!
end
end
end

path '/api/v1/users' do
post 'Create a user' do
tags 'Users'
consumes 'application/json'
produces 'application/json'

parameter name: :user,
in: :body,
description: 'Create a user',
schema: {
type: :object,
properties: {
email: { type: :string },
description: { type: :string }
},
required: %w[email]
}

response '201', 'OK' do
schema type: :object,
properties: {
id: { type: :integer },
email: { type: :string },
description: { type: :string },
created_at: { type: :string },
updated_at: { type: :string }
}
run_test!
end

response '422', 'Unprocessable entity' do
run_test!
end
end
end

path '/api/v1/users/{id}' do
get 'Show a user' do
tags 'Users'
description 'Show a user'
produces 'application/json'

parameter name: :id,
in: :path,
type: :integer,
required: true,
description: 'User identification'

response '200', 'OK' do
schema type: :object,
properties: {
id: { type: :integer },
email: { type: :string },
description: { type: :string },
created_at: { type: :string },
updated_at: { type: :string }
}
run_test!
end

response '401', 'Unauthorized' do
run_test!
end

response '404', 'Not Found' do
run_test!
end
end
end

path '/api/v1/users/{id}' do
put 'Updates a user' do
tags 'Users'
description 'Updates a user'
consumes 'application/json'
produces 'application/json'

parameter name: :id,
in: :path,
type: :integer,
required: true,
description: 'User identification'

parameter name: :user,
in: :body,
description: 'Updates a user',
schema: {
type: :object,
properties: {
email: { type: :string },
description: { type: :string }
}
}
response '200', 'OK' do
schema type: :object,
properties: {
id: { type: :integer },
email: { type: :string },
description: { type: :string },
created_at: { type: :string },
updated_at: { type: :string }
}
run_test!
end

response '404', 'Not Found' do
run_test!
end

response '422', 'Unprocessable entity' do
run_test!
end
end
end

path '/api/v1/users/{id}' do
delete 'Delete a user' do
tags 'Users'
description 'Delete a user'
produces 'application/json'

parameter name: :id,
in: :path,
type: :integer,
required: true,
description: 'User identification'

response '204', 'OK' do
run_test!
end

response '404', 'Not Found' do
run_test!
end
end
end
end

Update the contents of the spec/swagger_helper.rb file. Enter the URL http://localhost:3000/ in the server data. Also, change the title and include the description with information about your project. For deployed projects, enter the deployed address in the URL. Set the two environments separated by a comma if you want to access the documentation in each one. Cross-use requires CORS configuration not covered in this article.

 config.swagger_docs = {
'v1/swagger.yaml' => {
openapi: '3.0.1',
info: {
title: 'Documentation API V1',
description: 'Sample app created for Medium article',
version: 'v1'
},
paths: {},
servers: [{
url: 'http://localhost:3000/',
variables: {
defaultHost: {
default: 'localhost:3000'
}
}
}]
}
}

Now just run the following command in the terminal and your documentation will be generated.

rake rswag

Start the server.

rails server

In your browser, open http://localhost:3000/api-docs. This is the documentation page for your endpoints.

http://localhost:3000/api-docs

Now we need to create the API.

Generate the model that has information about the email and description of the users.

rails generate model User email description

Let's stipulate that the email is a required field in the app/models/user.rb.

class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
end

Migrate the user model.

rails db:migrate

Create our user API controller.

rails generate scaffold_controller api/v1/users --api --no-jbuilder --model-name="User" 

Include body params data access for POST and PUT in app/controllers/api/v1/users_controller.rb.

class Api::V1::UsersController < ApplicationController
before_action :set_user, only: %i[ show update destroy ]

# GET /api/v1/users
def index
@users = User.all

render json: @users
end

# GET /api/v1/users/1
def show
render json: @user
end

# POST /api/v1/users
def create
@user = User.new(json_params)

if @user.save
render json: @user, status: :created
else
render json: @user.errors, status: :unprocessable_entity
end
end

# PATCH/PUT /api/v1/users/1
def update
if @user.update(json_params)
render json: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end

# DELETE /api/v1/users/1
def destroy
@user.destroy
end

private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end

# Only allow a list of trusted JSON parameters through.
def json_params
allowed_data = %(email description).freeze
json_payload.select { |allow| allowed_data.include?(allow) }
end
end

Create the json_payload method in the app/controllers/application_controller.rb.

def json_payload
return [] if request.raw_post.empty?

HashWithIndifferentAccess.new(JSON.parse(request.raw_post))
end

Restart the server as we create new routes.

CTRL+C
rails server

To test your endpoints, open them and click on the “Try it out” button. Provide the required data and click the “Execute” button.

Select "GET api/v1/users List users" and click the “Try it out” button. No additional information is required. Then click on the “Execute” button. An empty list of users is displayed with the response code 200 (OK).

GET api/v1/users List users (edited)

Let’s create a user. Select "POST api/v1/users Create a user" and click the “Try it out” button. Provide the user information in the provided body field. Let’s include in our example, only the email of the user.

{
"email": "documentation@api.com"
}

And click “Execute”.

The response code is 201 and the created user data is returned.

POST api/v1/users Create a user (edited)

Consult this created user whose id number is 1. Select “GET api/v1/users/{id} Show a user” and click the “Try it out” button. Then enter the id number in the "User identification" path field and click “Execute”. The response brings the user data and the code 200 (OK).

GET api/v1/users/{id} Show a user (edited)

Now let’s change the description of the user. Select “PUT api/v1/users/{id} Update a user” and click on the “Try it out” button. Enter the user id and now provide, for example, only a new description.

{
"description": "User responsible for updating the article"
}

Click “Execute”. The response brings the user data and the code 200 (OK).

PUT api/v1/users/{id} Update a user (edited)

To complete your examples you can delete the created user. Select "DELETE api/v1/users/{id} Delete a user”. Just inform the id and click “Execute”. The response code is 204 (OK).

DELETE api/v1/users/{id} Delete a user (edited)

The source data for this example is available in my documentation repository.

In my next article, I’ll show you how to add authentication to your Rails API application. And in the following article, I will show how to document this authentication.

--

--