Building a RESTful API

What makes and API RESTful?

  • each individual request should have no context of the requests that came before it
  • each request that modifies the database should act on one and only one row of one and only one table.Requests that only fetch information should get zero or more rows from one table.
  • The resource endpoints should return representations of the resource as data, usually XML or JSON. The returned information should be sufficient for the client to uniquely identify and manipulate the database row(s) in question.

 

Conventions:

 

  • Naming: Our API is more often used by other developers than ourselves, means its really helpfull to stick to naming conventions such as: POST for create, PUT for update and PATCH for update/insert.
  • Versioning: An existing API should never be modified except for bugfixes
  • Route Parameters: Using public, but possibly not unique, identifiers like name reduces the amount of system internals that needs to be exposed, while allowing the client to easily lookup any data needed to make a request. The downfall is longer nested routes.

 

Controllers:

To ensure statelessness, its a common approach to require reauthentication on a per-request level. A base API controller is useful to handle authentication and extract common API functionality:

class BaseApiController < ApplicationController

before_filter :parse_request, :authenticate_user_from_token!

private

def authenticate_user_from_token!

if !@json[‘api_token’] render nothing: true, status: :unauthorized

else @user = nil User.find_each do |u|

if Devise.secure_compare(u.api_token, @json[‘api_token’])

@user = u

end

end

end

end

def parse_request @json = JSON.parse(request.body.read)

 

 

The Projects Controller:

 

class ApiProjectsController < BaseApiController

before_filter :find_project, only: [:show, :update]

before_filter only: :create do

unless @json.has_key?(‘project’) && @json[‘project’].responds_to?(:[]) && @json[‘project’][‘name’]

render nothing: true, status: :bad_request

end

end

before_filter only: :update do

unless @json.has_key?(‘project’) render nothing: true, status: :bad_request

end

end

before_filter only: :create do

@project = Project.find_by_name(@json[‘project’][‘name’])

end

def index

render json: Project.where(‘owner_id = ?’, @user.id)

end

def show

render json: @project

end

def create

if @project.present?

render nothing: true, status: :conflict

else

@project = Project.new

@project.assign_attributes(@json[‘project’]

if @project.save render json: @project

else render nothing: true, status: :bad_request

end

end

end

def update @project.assign_attributes(@json[‘project’])

if @project.save render json: @project

else

render nothing: true, status: :bad_request

end

end

 

private

def find_project @project = Project.find_by_name(params[:name])

render nothing: true, status: :not_found unless @project.present? && @project.user == @user

end

end

 

 

 

Devensive Programming

 

its a principle where a piece of software continue functioning even in unforeseen circumstances. Due to exposing the API to third party developers its crucial to think about the worst and malicious things they can do with theire arbitrary input, and work around it.

 

HTTP status codes

Instead of using standart status codes like: 200 (OK), 404 (Not Found), and 500 (Internal Server Error) its a better thing to give a more precise to third party developers so that they can work with the error given.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s