How to build a good API: Relationships and endpoints

Factory.hr
7 min readJan 13, 2017

--

In first blog post we wrote about how to design a good API. Now we are going to take the next step and talk about Relationships and andpoints.

Resource relationships in API

There are 3 three main types of relationships between resources:

  • One to one
  • One to many
  • Many to many

One to one

In this relationship, one resource is related to only one resource. In database architecture that would mean that one row in a table may be linked with only one row in another table and vice versa.

Examples of this relationship would be:

  • Country has one capital city
  • Player has one team
  • User has one mobile phone

Fig 1. One to one and one to many relationship

Generally speaking, tables in one to one relationship could be glued together in one big table.

One to many

Type of relationship that refers to relationship between two resources A and B in which resource A may be linked to many elements of B, but resource B is linked to only one element of A. In database terms that would mean resource A is linked to many rows in resource B, but resource B with only one row from resource A.

  • Mother has many children
  • Employer has many employees
  • Team has many players

We could say that one to many model is always parent-child (children) model, where one element from one resource “owns” many child resource elements. In one to many model it is not possible to “glue” related tables together

Many to many

Relationship in which one resource element is related to many elements (in other resource) and vice versa. Good example would be Movies and Categories relationship.

Movie can belong to many Categories, and Category can have many movies. In relation database management system, these types of relationships are implemented by the means of pivot tables.

Fig 2. Database structure

Other widely used many to many relationship is User-Role relationships. In which User can have many Roles and Roles can have many Users.

Example for our API project

As we stated in previous article, for our fakeTwitter project we will have three main resources: User, Post, Comment. Now we are going to define relationships between these resources.

Fig 3. User — Post relationship

From image it is easy to see dependencies between Users and Posts. Users would have many Posts and a single Post will always belong to only one User.

Fig 4. Post — Comments relationship

Similarly Posts resource can have many Comments while Comment can belong only to one Post.

Has many through relation

There is also one special type of relationship in which one resource is related to distant resource through third resource.

Fig 5. Users — Comments relationship

So in our case, Users resource is related to Comments resource through Posts resource. This relation enables us to get all comments written by one user, which we will use later in the series.

Now we have defined backbone of our API, since API’s are data centers it is crucial to define resources and relationships between them before starting to code.

Practical part

Now we are going to replicate this in Laravel. Resource would be a model, so we begin by creating database structure first and then creating Models folder in app directory and creating User, Post, Comment models inside created folder.

There would be three database tables: users, posts, comments. Easiest way to do this is to create migrations using artisan command:

php artisan  make:migration create_users_table

Migrations should look like this:

  • Users table migration
class CreateUsersTable extends Migration
{

public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('email')->unique();
$table->string('password');
});
}

public function down()
{
Schema::drop('users');
}
}
  • Posts table migration
class CreatePostsTable extends Migration
{

public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->text('post');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}

public function down()
{
Schema::drop('posts');
}
}
  • Comments table migration
class CreateCommentsTable extends Migration
{

public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->text('comment');
$table->integer('post_id')->unsigned();
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');;
});
}

public function down()
{
Schema::drop('comments');
}
}

We activate migrations by using the following command:

php artisan migrate

Migrations will create database structure for us, now it is time to communicate with database. Next thing to do is to create Models folder in app folder and create User, Post, Comment model inside that folder.

Fig 6. Laravel folder structure

Good thing to mention when creating custom folders is to declare a namespace that will point to that folder avoiding long and unpractical class references. Namespaces are declared in composer.json under autoload PSR-4 key in project base folder. So my composer.json now looks like this:

"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/",
"Models\\": "app/Models"
}
},

Now we are ready to create models and define relationships between them in the way I described earlier. For handling one to many relationship we are going to use Eloquent’s hasMany() method, for inverse relation (many to one) we will use belongsTo() method and finally for has many through relationship (User — Comments) we will use hasManyThrough() method. The only thing we need to do is just define in on the model and Eloquent magic will do the rest.
So your models should look like this:

  • User model
namespace Models;use Illuminate\Database\Eloquent\Model;class User extends Model
{
protected $table = 'users'; public function posts()
{
return $this->hasMany('\Models\Post','user_id','id');
}
public function comments()
{
return $this->hasManyThrough('\Models\Comment','\Models\Post','user_id','post_id');
}
}

Eloquent’s hasMany command takes 3 arguments (only first is required), first one is related model, second is foreign key on related model and third one is the local key. HasManyThrough() however is little bit more complex, it’s first argument is distant model on which we define relationship, second argument is intermediate model, third is foreign key on intermediate model (Post model) and finally fourth argument is foreign key on distant model (Comments model).

  • Post model
namespace Models;use Illuminate\Database\Eloquent\Model;class Post extends Model
{
protected $table = 'posts'; public function posts()
{
$this->hasMany('\Models\Comment','post_id','id');
}
public function user()
{
$this->belongsTo('\Models\User','user_id','id');
}
}

In this case we also have a many to one relation which is represented by belongsTo() method, it takes three parameters, first is related model, second is foreign key and third is local key of parent model (User).

  • Comment model
namespace Models;use Illuminate\Database\Eloquent\Model;class Comment extends Model
{
protected $table = 'comments'; public function post()
{
$this->belongsTo('\Models\Post','post_id','id');
}
}

Similar to previous examples. For more information about relationships in Laravel please visit Laravel documentation.

Defining API endpoints

Now that we have our models defined, we have a blueprint on which we will build rest of our API. As I stated in previous articles, we will use HTTP verb based endpoint naming method. We will use GET, POST, PUT, DELETE verbs. Using this verbs we can define “default endpoints”. Default endpoints are incredibly easy to design and they will exists in almost every project, except default endpoints we will have custom endpoints too.

User model

Last two columns are the names of methods in controller that will handle that endpoint. As you can see, naming examples are quite intuitive and have a consistent pattern.

Post model

In Post model thing are little bit different. Since Post is owned by User model some endpoints are accessible only through User model. These endpoints starts by “users” base node in URL (POST /users/{id}/post, PUT /users/{id}/post/{post_id}).

Some of the endpoints are publicly accessible such as get all posts (GET /posts) or get one post (GET /posts/{id) and furthermore some are accessible only by admin (DELETE /posts — deletes all posts). While designing developer needs to think about usability and functionality which needs to be implemented and adjust endpoints to project needs.

Comments model

Now we have defined most common endpoints that will lead request to our models which in the end will interact with database and do some data manipulation. Bear in mind that these endpoints are just for learning purposes. In a real world example you would have more, depending on your project’s needs. Feel free to have questions about API.

--

--

Factory.hr

We are Factory — a remote-first company focused on executing the most complex digital business transformations based on Pimcore technologies.