👋

@gsamokovarov

Evening!

WELCOME TO RUBY

Let me address this first.

Many of you have said things about our logo.

And I don't mean nice things. 🙊

Okay, so it does look a bit like shit. 💩

A bit. Okay?

A lot. Okay!

🤔

But what is the Banitsa anyway?

Isn't it what it eventually turn into?

🤔

So, we have a new logo now.

I guess...

YOU ARE WELCOME!

Anyway...

September 22-23

partial :: Conf

  • Functional conference in Sofia, Bulgaria
  • Long lectures with a half-day of trainings
  • A broader scope than a single technology
  • Interesting and fun, we hope
partial :: Conf
@partialconf

Open Call for Speakers! 📈

You can help us by spreading the word!

Follow us on twitter.

Make your friends go.

Make your non-friends go.

Make some noise online.

Make some noise inline.

That (inline) doesn't make no sense!

What does make sense is the noise though.

Every single bit of it will help.

🙇

Simple API Stack

Rails API Mode

Since 5.0 we have an API friendly mode.

Rails API Mode

This doesn't mean that Rails pre 5.0 sucks for APIs.

Rails API Mode

It does not.

Rails API Mode

It's fine.

Rails API Mode

The API mode does a few things.

Rails API Mode

Cuts the internal middleware stack.

Rails API Mode

Introduces a bit slimmer ActionController::Base.

Rails API Mode

Introduces ActionController::API.

Rails API Mode

Introduces API friendly development errors.

MORE

All of this can be backported pre Rails 5.0

5.0 acknowledges a default API stack.

🎪

🤔

What makes a good API?

What makes a good RESTful API?

Good resources.

Semantic responses.

Consistent responses.

Consistent error responses.

Responses (JSON)

Responses

We have a couple of options.

JBuilder

Renders Action View templates (views) as JSON instead of HTML.

# app/views/messages/show.json.jbuilder
json.content format_content(@message.content)
json.(@message, :created_at, :updated_at)

json.author do
  json.name @message.creator.name.familiar
  json.email_address @message.creator.email_address_with_name
  json.url url_for(@message.creator, format: :json)
end

if current_user.admin?
  json.visitors calculate_visitors(@message)
end

json.comments @message.comments, :content, :created_at

json.attachments @message.attachments do |attachment|
  json.filename attachment.filename
  json.url url_for(attachment)
end 
{
  "content": "<p>This is <i>serious</i> monkey business</p>",
  "created_at": "2011-10-29T20:45:28-05:00",
  "updated_at": "2011-10-29T20:45:28-05:00",

  "author": {
    "name": "David H.",
    "email_address": "'David Heinemeier Hansson' ",
    "url": "http://example.com/users/1-david.json"
  },

  "visitors": 15,

  "comments": [
    { "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" },
    { "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" }
  ],

  "attachments": [
    { "filename": "forecast.xls", "url": "http://example.com/downloads/forecast.xls" },
    { "filename": "presentation.pdf", "url": "http://example.com/downloads/presentation.pdf" }
  ]
} 

JBuilder

This is the default API mode option.

Too much DSL for my taste.

Even if you like the DSL, the logic feel like it's in one big bag.

Can be abstracted with partial views, but I like methods.

Active Model Serializers

Renders a canonical representation of an object as JSON.

Active Model Serializers

No templates, simple Ruby classes are used instead.

class SomeSerializer < ActiveModel::Serializer
  attribute :title, key: :name
  attributes :body

  def body
    object.strip
  end
end 
{
  "name": "Some",
  "body": "I smell something 🤔."
} 

I like the concept of the serializers.

Separates the view logic.

Testable without specialized tools.

Simple

Simple API Stack

It's kinda huge.

Brings runtime dependencies.

Supports jsonapi!

But what if you don't need jsonapi?

Requires an interface for the serializable objects.

class SomeResource < ActiveRecord::Base
  # columns: title, body
end 
class SomeResource < ActiveModelSerializers::Model
  attributes :title, :body
end 

😓

serializr

serializr

Serializr

A lightweight alternative to AMS.

Serializr

No interface restrictions for the serializable objects.

Serializr

A minimal interface on the library side.

# app/serializers/user_serializer.rb
class UserSerializer < Serializr
  attributes :id, :name, :email

  def name
    object.first_name + " " + object.last_name
  end
end 
{
  "id": 42,
  "name": "Doe John",
  "email": "john@nsa.gov"
} 

Serializr

Integration with ActionController

class UsersController < ApplicationController
  def show
    user = User.find(params[:id])

    render json: user
  end
end 
class UsersController < ApplicationController
  def show
    user = User.find(params[:id])

    render json: user, serializer: UserSerializer
  end
end 
class FriendsController < ApplicationController
  def index
    friends = User.friends_of(params[:id])

    render json: friends
  end
end 
class FriendsController < ApplicationController
  def index
    friends = User.friends_of(params[:id])

    render json: friends, serializer: UserSerializer[]
  end
end 

Serializr

A minimal interface on the library side.

Serializr

This means no has_one, has_many, etc. class macros.

Serializr

Quite easy to support.

# app/serializers/application_serializer.rb
class ApplicationSerializer < Serializr
  inlcude Rails.application.routes.url_helpers

  cattr_reader :serializer_class_cache do
    Hash.new do |hash, cls|
      hash[cls] = (cls.name + Serializer).constantize
    end
  end

  def render_one(object, serializer: nil)
    return unless object

    serializer ||= serializer_class_cache[object.class]
    serializer.new(object)
  end

  def render_many(objects, serializer: nil)
    return [] if objects.blank?

    serializer ||= serializer_class_cache[objects.first.class][]
    serializer.new(objects)
  end
end 

👆

✌️

Error Responses (JSON)

Choose a format and stick with it!

Ideas

  • No string messages
  • Field errors with error details
  • CONSISTENCY

Being an API, you have one great constraint.

You're not the one to worry about presenting the errors.

Only providing them.

Which means, we can treat all the errors the same.

👆

Let your application crash.

✌️

Catch it and render the response.

🍻

Enjoy your substance of choice, moderately!

ActionController::API.rescue_from 
ActionController::Base.rescue_from 

👓

Homework

Project you may find useful for your API.

🕶