Repository navigation
Endpoints Basic API
We can use multiple routes to differentiate between specific requests. In an API, these are typically referred to as endpoints. A user of the API uses endpoints to access specific pieces of data. Just like a normal Rails app, we can create full CRUD based controllers that only render JSON.
Rails.application.routes.draw do
get '/birds' => 'birds#index'
get '/birds/:id' => 'birds#show' # new
endclass BirdsController < ApplicationController
def index
birds = Bird.all
render json: birds
end
def show
bird = Bird.find_by(id: params[:id])
render json: bird
end
endAn API that sends user information might contain details of a user internally that it does not want to ever share externally.
{
"id": 2,
"name": "Grackle",
"species": "Quiscalus Quiscula",
"created_at": "2019-05-09T21:51:41.543Z",
"updated_at": "2019-05-09T21:51:41.543Z"
}
We probably don't need bits of data like created_at and updated_at. Rather than send this unnecessary info when rendering, we could just pick and choose what we want to send:
def show
bird = Bird.find_by(id: params[:id])
render json: {id: bird.id, name: bird.name, species: bird.species }
end.slice() simplifies it:
def show
bird = Bird.find_by(id: params[:id])
render json: bird.slice(:id, :name, :species)
endboth methods above results in:
{
"id": "3",
"name": "Common Starling",
"species": "Sturnus Vulgaris"
}
def index
birds = Bird.all
render json: birds
endonly:
def index
birds = Bird.all
render json: birds, only: [:id, :name, :species]
endexcept:
def index
birds = Bird.all
render json: birds, except: [:created_at, :updated_at]
enddef show
bird = Bird.find_by(id: params[:id])
if bird
render json: { id: bird.id, name: bird.name, species: bird.species }
else
render json: { message: 'Bird not found' }
end
endNow, if we were to send a request to an invalid endpoint like http://localhost:3000/birds/hello_birds, rather than receiving a general HTTP error, we would still receive a response from the API:
{
"message": "Bird not found"
}
In this lesson, we will look at how our Rails API will be able to convey relationships between multiple models in a single JSON object.
def show
sighting = Sighting.find_by(id: params[:id])
render json: sighting
endvisiting http://localhost:3000/sightings/1:
{
"id": 1,
"bird_id": 1,
"location_id": 2,
"created_at": "2019-05-14T11:20:37.225Z",
"updated_at": "2019-05-14T11:20:37.225Z"
}
NOTE: Notice that the object includes its own "id", as well as the related "bird_id" and "location_id". That is useful data. We could use these values to send additional requests using JavaScript to get bird and location data if needed.
def show
sighting = Sighting.find_by(id: params[:id])
render json: { id: sighting.id, bird: sighting.bird, location: sighting.location }
end{
"id": 2,
"bird": {
"id": 2,
"name": "Grackle",
"species": "Quiscalus Quiscula",
"created_at": "2019-05-14T11:20:37.177Z",
"updated_at": "2019-05-14T11:20:37.177Z"
},
"location": {
"id": 2,
"latitude": 30.26715,
"longitude": -97.74306,
"created_at": "2019-05-14T11:20:37.196Z",
"updated_at": "2019-05-14T11:20:37.196Z"
}
}An alternative is to use the include option to indicate what models you want to nest:
def show
sighting = Sighting.find_by(id: params[:id])
render json: sighting, include: [:bird, :location]
end
#also works fine when dealing with an action that renders an array
def index
sightings = Sighting.all
render json: sightings, include: [:bird, :location]
end{
"id": 2,
"bird_id": 2,
"location_id": 2,
"created_at": "2019-05-14T11:20:37.228Z",
"updated_at": "2019-05-14T11:20:37.228Z",
"bird": {
"id": 2,
"name": "Grackle",
"species": "Quiscalus Quiscula",
"created_at": "2019-05-14T11:20:37.177Z",
"updated_at": "2019-05-14T11:20:37.177Z"
},
"location": {
"id": 2,
"latitude": 30.26715,
"longitude": -97.74306,
"created_at": "2019-05-14T11:20:37.196Z",
"updated_at": "2019-05-14T11:20:37.196Z"
}
}To get more control of the data shown also in the nested Objects:
def show
sighting = Sighting.find_by(id: params[:id])
render json: sighting, include: {
bird: {only: [:name, :species]},
location: {only: [:latitude, :longitude]}
}, except: [:updated_at]
end{
"id": 2,
"bird_id": 2,
"location_id": 2,
"created_at": "2019-05-14T11:20:37.228Z",
"bird": {
"name": "Grackle",
"species": "Quiscalus Quiscula"
},
"location": {
"latitude": 30.26715,
"longitude": -97.74306
}
}
For the purposes of building applications in JavaScript and frameworks like React we specifically need Rails to act as an API that responds with JSON.
When first building a Rails application, it is possible to flag that the application should be API-only.
We can now take a single model or all the instances of that model and render it to JSON, extracting out any specific content we do or do not want to send!
Whether you are building a professional API for a company or for your own personal site, having the ability to fine tune how your data look is a critical skill that we're only just beginning to scratch the surface on.