GithubHelp home page GithubHelp logo

rails-api's Introduction

General Assembly Logo

Rails API Overview

Objectives

By the end of this lesson, developers should be able to:

  • Explain why an API is necessary.
  • List some of the responsibilities of a typical API, and identify which components within MVC map to those responsibilities.
  • Diagram the process flow from Client through Database and back
  • Map MVC roles to specific components of Rails.
  • Indicate where different types of files can be found within a Rails application.
  • Recall how to use rails generate for starting models, controllers, and serializers.

Preparation

  • Run gem install rails

Prerequisites

Who Needs An API

By now, you've built a client application: it had a user interface (UI) and behavioral logic, allowing users to play Tic-Tac-Toe in the browser. Isn't that enough? Why might we want an API?

One possible reason is that when two different people are served up a web page, they each have a separate copy of everything on the page. As a result, if we have two users interacting with our app, they can never interact with each other, or even be aware of each other.

Another possible reason is that we might want want to store the state of the game over time. What if we wanted to be able to 'save' our game, close the browser, walk away, and pick it up again the next day? Having an API where data is stored means that we can do just that. This is known as persistence (things being saved over time).

For your client, we provided an API that could do all that. But how did it work?

Lab - Design Your Own API

In your squads, take ten minutes (and a patch of whiteboard), and write out all the different things that you think that the API for your Tic-Tac-Toe clients needed to do in order to work correctly.

Then, once you've done this, take a second look at what you've written down. Assuming you were to try to build a Ruby program to handle all of these different tasks, what abstractions would you need? How would the different responsibilities be broken up? Make a list. It doesn't matter how "correct" it is. For this part, recall our strategies in modeling. What are the relevant nouns and verbs? Big ideas and details?

Once all groups are finished, we'll discuss our results.

One Possible Solution - Model-View-Controller

Suppose we wanted to build an API app that records a user's height and weight. It might work as follows:

  1. A user interacts with a front-end application, triggering a POST, and this POST request contains data - specifically, the latest measurements of height and weight.
  2. When the POST request is received by the server, the API app parses the request and extracts the relevant information.
  3. The data from the POST request gets added to our records.
  4. As confirmation, a JSON with a unique identifier (so we can refer to this specific measurement in the future) gets sent to the front end.

Our app can also retrieve records, like so:

  1. Something on the front-end (probably driven by user interaction) triggers a GET request that asks for a specific record.
  2. When the GET request is received by the server, the API app parses the request and identifies which measurement record is being requested.
  3. The data for the desired measurement gets retrieved from our set of records.
  4. Finally, the desired data gets put into a JSON and sent back to the front end.

If we were to try to generalize and abstract away the differences between these two steps, we might say that our web application needs to:

  • receive incoming requests from a front-end
  • execute specific behaviors in response to those requests
  • create, read, update, or destroy data records through some kind of data storage system
  • share information back to the browser

The quartet of 'create', 'read', 'update', and 'destroy' is commonly known as 'CRUD'; each refers to a specific type of action that can be performed on our data storage system. Each of these types might have more than one specific action associated with it ('read one' vs 'read all', for instance).

One common way of dividing up these four responsibilities is the Model-View-Controller (MVC) architecture pattern. This pattern involves making three core types of components, each responsible for a different part of the API's functionality.

A Model directly manages the data in our application, and provides a representation of that data for the rest of the application to use.

A View is like it sounds - it's data that gets sent back to the client for the user to consume.

A Controller responds to user requests as they come in, and utilizes both the model and the view components to perform the desired behavior and produce a response.

In addition to these three types of components, however, there is a fourth piece that it's important to consider with web development particularly: routing. Routes indicate to the server which controllers should be triggered (and how) by which kinds of requests. It's a critical piece of the puzzle, and one we'll be looking at later today in more detail.

What which part(s) of an HTTP request does the router use to determine which code to run?

MVC architecture is very common in web applications, and Rails gives us the tools to spin up applications that are roughly in line with the idea of MVC.

Lab - Act Out an MVC API

We're going to act out the various parts of an MVC application. Link up with another squad so that you're in a group of eight, and assign each member of your 'super-group' one of the roles below. These roles are:

  • Client

  • Server

  • Controllers

  • People Controller

  • Places Controller

  • Things Controller

  • Models

  • People Model

  • Places Model

  • Things Model

Once you've divvied up the roles, take two minutes to read through the directions for your role, found ./roles. These directions explain what your responsibilities are and how you should carry them out. However, you may only communicate with the teammates listed in your role card.

We'll work through one example request together, and then each group will work independently to answer all remaining requests. Once all groups have finished, we'll have a retro-style discussion about how everything went.

MVC with Rails

Rails is a web framework - a tool that helps us quickly and easily build web applications - written in Ruby, and designed and created by David Heinemeier Hannson (also known as 'DHH'). Although Rails applications don't match up perfectly with the abstract idea of MVC, their architecture is fairly similar.

Let's take a look at an actual Rails app and see how it lines up. Fork and clone this repo. Recognize it? It's the Tic-Tac-Toe API!

Open it up in Atom. Have a look at the file structure. Let's take a step back and just look at directories at the top level of the app.

.
├── app
├── bin
├── config
├── db
├── lib
├── log
├── public
└── vendor

For now, you only need to think about app, config, and db - you probably won't be touching any code outside of those three directories. How is that possible? Because Rails actually builds out most of these files and folders for you, every time you use it to create a new application. That's why it's called a 'framework' - it gives you the skeleton for a brand new app, which you can then customize.

  • The app directory holds the code for our application itself. We'll be writing a lot of code here.
  • bin holds the binary code for Rails itself, as well as some of the programs that it depends on - better not touch this directory.
  • config holds configuration settings for our app and for the things that plug into it. This includes things like environmental variable settings and secret keys, but also things like the routing configuration for our server (which is not strictly part of our Rails app, but which our app uses); the routes.rb file, in particular, defines all of the routing for our app.
  • db holds files related to the structure of the application's database. The database, like the server, is separate from Rails and is not strictly part of the app, so it makes sense to keep this outside of the app directory.
  • log is a place where Rails keeps its log files - records of things that have happened when the app was running. Looking through here can sometimes be helpful if you've hit a bug.
  • public holds some static HTML pages that anyone can see, regardless of whether or not they're logged into our app. These are typically pages that represent different errors our app might have hit, such as 404.
  • vendor holds non-gem software that our app will use. Don't worry too much about this one for now.

Let's dive into the app directory. This app in particular has more going on than yours probably will, but it still has all the basic components.

./app
├── channels
├── controllers
├── jobs
├── mailers
├── models
├── serializers
└── views

Three of these directories should jump out at you: controllers, models, and views. Each holds the different Ruby files that Rails uses to handle the respective responsibilities of MVC.

Don't worry about assets, serializers, mailers, or helpers for now. In API-style applications, serializers replace all the views that we care about for this program, so we can safely skip the views folder as well.

Demo: Building our first Rails App

Now that we've conceptually wrapped our heads around what goes into a backend lets make one ourselves.

WARNING: Do not ask "why?". None of this should "make sense", yet. Instead, focus on the commands I'm running and watch how Rails automatically organizes our code. The rails generate commands are particularly interesting.

Creating a Blog API

  • Use the quite-excellent Rails API Template, already included in this repository for our convenience.

  • Scaffold our Posts and Comments (Users come with the template!)

    • bin/rails generate scaffold post title:string body:text user:references
    • bin/rails generate scaffold comment body:text user:references post:references
  • Now lets create and migrate our database by typing in:

    • bin/rake db:create
    • bin/rake db:migrate
  • Start the server! bin/rails server

  • Now navigate to localhost:4741/users (empty brackets is a good sign)

  • Let's actually see some data, by adding it to our db/examples.rb file.

    %w(alice bob charlie dana).each do |name|
      email = "#{name}@#{name}.com"
      next if User.exists? email: email
      User.create!(email: email, password: 'abc123', password_confirmation: nil)
    end
    
    %w(alice bob dana).each_with_index do |name, i|
      title = "Dear Diary Number #{i}"
      email = "#{name}@#{name}.com"
      user = User.find_by(email: email)
      next if Post.exists? title: title, user_id: user.id
      Post.create!(title: title, body: "Another beautiful day!", user_id: user.id)
    end
    
    %w(charlie).each do |name|
      body = "Great post!"
      email = "#{name}@#{name}.com"
      user = User.find_by(email: email)
      post = User.find_by(email: "[email protected]").posts.first
      next if Comment.exists? body: body, user_id: user.id
      Comment.create!(body: body, user: user, post_id: post.id)
    end
  • Now we have to update the models with relationships. Put the following in the user model (model/user.rb):

    class User < ActiveRecord::Base
      has_many :posts
      has_many :comments
    end
  • And add this to the post model (model/post.rb):

    class Post < ActiveRecord::Base
      has_many :comments
    end
  • Now fill the database with examples by running bin/rake db:examples

  • If we need to reset entirely, bin/rake db:drop db:create db:migrate db:seed db:examples

  • Try navigating to localhost:4741/users or /posts or /comments. You should see the JSON you seeded. Try making a curl request to send JSON to your API:

    curl http://localhost:4741/posts \
    --include \
    --request POST \
    --header "Content-Type: application/json" \
    --data '{
        "post": {
          "title": "a sample title",
          "body": "a sample body"
        }
      }'
  • Navigate to localhost:4741/users and see what we have. In the serializer files try adding more attributes as keys, and see how this changes. (check the db/schema file for some ideas.)

  • After adding attribute fields to the user, post and comment serializers add a has_many relationship to user serializer to match the relationship added to the user model.

Tasks

Developers should run these often!

  • bin/rake routes lists the endpoints available in your API.
  • bin/rake test runs automated tests.
  • bin/rails console opens a REPL that pre-loads the API.
  • bin/rails db opens your database client and loads the correct database.
  • bin/rails server starts the API.
  • scripts/*.sh run various curl commands to test the API. See below.

Additional Resources

  1. All content is licensed under a CC­BY­NC­SA 4.0 license.
  2. All software code is licensed under GNU GPLv3. For commercial use or alternative licensing, please contact [email protected].

rails-api's People

Contributors

berziiii avatar dependabot[bot] avatar ga-meb avatar jcornmanhomonoff avatar jrhorn424 avatar micfin avatar payne-chris-r avatar raq929 avatar realweeks avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rails-api's Issues

Issues with Creating a Blog Step

1 - Step 1 isn't fully wrapped in code backticks (gem install rails-api)
2 - cd into blog_app before Scaffold our User, Posts and Comments steps

timing

estimate: 3
actual: 3

Remove TODOs at bottom of README

- <!-- TODO -   `rake nag` checks your code style. -->
- <!-- TODO -   `rake lint` checks your code for syntax errors. -->

These are out-of-date have been covered by the steps listed above them.

Clarity needed between lesson and diagnostic associated with lesson

To my understanding, the focus of the rails-api lesson is to discuss Rails at a very high level and to execute the correct commands to create a new rails-api, with little to no explanation of what the commands are actually doing.

Due to this, the diagnostic has questions that are more specific than what is covered during the lesson itself. Therefore attention should be given to either providing more explanation in the lesson itself of the commands, actions, scaffolding, serializers, etc. or several questions on the rails-api diagnostic should be adjusted to be at a higher level of check for understanding to match what the lesson provides.

serializer update

Include in Application Controller

Use enhanced JSON serialization

include ActionController::Serialization

Failed curl request in README

The curl requested listed in the README fails as a result of missing a parameter (user_id). Using this as a teaching moment to show how to reference the files in the back end to find the proper parameters to pass through for a successful POST.

Correct script:

curl http://localhost:4741/posts \
--include \
--request POST \
--header "Content-Type: application/json" \
--data '{
    "post": {
      "title": "a sample title",
      "body": "a sample body",
      "user_id": 1
    }
  }'

Add secret key to README?

Wondering if it's worthwhile to write in the README that you need to add the test & development secret keys to the .env file....or if that's a given since it's based off of the rails-api-template and it says to do so in there. Thoughts welcome!

Rails app accidentally committed?

It looks like previous versions of this repo (I'm looking at 95d10a9) did not have the full rails app, which makes sense, since we are supposed to make one.

Was committing the rails app an accident, or is there something I'm missing here?
As it is, I can't run the commands specified in the repo because the app already exists.

Can we cover RESTful routes here?

I think it's something our developers miss. They don't understand the importance of following convention, or just don't understand that convention exists. It would be a good idea to explain a) that it exists b) why it's important, and mayyybe c) what are good examples of breaking it (i.e. sign-up, change-password, etc). I'm reviewing project 2s and so many of them break RESTful convention for reasons I cannot understand.

MVC lab uses views

In the expected response sections of roles/client, the client is told to expect HTML, when we now expect a JSON response. We should update the expected response appropriately.

Lint code

Run the linter and fix linter errors

Change g to generate

I've talked to @gaand some about always using the longform commands in git, and I'm wondering if we should do that here, too?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.