fit-api

Lightweight framework for building JSON API's

Introduction

fit-api is a library based on Rack and inspired by Rails & Sinatra.
The goal of this gem is to provide simplicity when developing an API with ruby.

Installation

Install the latest version from RubyGems

$ gem install fit_api

Table of Contents

Usage

This is a basic example showing how it works... you can check the demo app from this repository: fit-api-demo

api.rb

require 'fit_api'

FitApi::Router.define do
  get '/:name', to: 'app#show'

  root to: 'app#index'
end

class AppController < FitApi::Controller
  def index
    json({ message: 'Hello world' })
  end

  def show
    json({ message: "Welcome #{params.name}" })
  end
end

# You can setup any Rack Middleware

FitApi.use Rack::CommonLogger, Logger.new('log/app.log')

Rack::Handler::WEBrick.run FitApi.app
ruby api.rb

Router

It recognizes URLs and invoke the controller's action... the DSL is pretty similar to Rails (obviously not so powerful):

HTTP methods:

get '/test/:name',  to: 'app#test_show'
post '/test',       to: 'app#test_post'
put '/test',        to: 'app#test_put'
delete '/test/:id', to: 'app#test_delete'

Resources

Nested:

resources :users do
  resource :avatar do
    get :comments
    post :add_comment
  end
end

Endpoints for users:

Method Path Controller & action
GET /users users#index
GET /users/:id users#show
POST /users users#create
PATCH /users/:id users#update
DELETE /users/:id users#destroy

Endpoints for avatar:

Method Path Controller & action
GET /users/:user_id/avatar avatar#show
POST /users/:user_id/avatar avatar#create
PATCH /users/:user_id/avatar avatar#update
DELETE /users/:user_id/avatar avatar#destroy
GET /users/:user_id/avatar/comments avatar#comments
POST /users/:user_id/avatar/add_comment avatar#add_comment

Member & Collection:

resources :contacts, only: %i(index) do
  member do
    post :add_activity
  end

  collection do
    get :search
  end
end
Method Path Controller & action
GET /contacts contacts#index
GET /contacts/search contacts#search
POST /contacts/:id/add_activity contacts#add_activity

Namespace

Only for paths

namespace :test do
  get :hello_world 
  post :hello_world, action: :post_hello_world 
end
Method Path Controller & action
GET /test/hello_world test#hello_world
POST /test/hello_world test#post_hello_world

namespace '/hello/world', controller: :test do
  get :test
end
Method Path Controller & action
GET /test/world/test test#test

Controller

controller :app do
  get :another_action
  get '/welcome', action: 'hello_world'
end
Method Path Controller & action
GET /another_action app#another_action
GET /welcome app#hello_world

Root

root to: 'app#index'
Method Path Controller & action
GET / app#index

Customize error 404 message

not_found to: 'app#error_404'

Controllers

The library provides one class FitApi::Controller which should be inherited from your controllers.
One limitation is the class name of your controller must end with "Controller", i.e: AppController, UsersController...

class AppController < FitApi::Controller
  def index
    json 'hello world'
  end

  def process_post
    json params 
  end
end 

You have the method #json available, which basically sets the response body.


Request

You can access the Request object like this:

request


Halt

You can exit the current action throwing an exception... the default status code is 400

halt
halt 500
halt 404, 'Not found'
halt 'Error message'

Params

GET /users

curl -i http://localhost:1337/users/:id?name=Berna&age=28&height=180
params.id         # 1
params.name       # "Berna"
params[:age]      # 28
params['height']  # 180

POST with params:

curl -i -X POST  -d 'user[name]=Berna&user[age]=28' http://localhost:1337/users

POST with JSON:

curl -i -X POST -H "Content-Type: application/json" -d '{ "user": { "name": "Berna", "age": 28 } }' http://localhost:1337/users

Result:

params.user.name     # "Berna"
params[:user][:age]  # "28"

#permit

params.user.permit(:name, :age)

#except

params.user.except(:height)

Request Headers

request.headers['Authorization']

Response Headers

headers['Header-Name'] = 'Header Value'

Callbacks

before_action *actions
after_action *actions, only: %i(index show)

TODO:

  • [ ] Implement tests
  • [ ] Allow websockets -> FitApi::Controller#stream

Any contribution would be appreciated =)