SlideShare a Scribd company logo
REST API 
Development and 
Testing 101 
By Samantha Geitz
A bit about me 
• 3 years of experience in web development 
• Now work for Packback 
• Background mostly in WordPress, a little bit of 
Rails, now use Laravel full-time 
• About 1 year experience building APIs
What we’re going to talk 
about tonight 
• “Typical” Laravel application / MVC 
• What is an API, and why should you care? 
• Shitty way to structure an API 
• Better way to structure an API 
• Demonstration of interacting with API
Our Application 
• Packback 
• Digital textbook rentals for 
college students 
• Resources: Users, Books 
• Users need to be able to “rent” 
books 
• https://github.com/samanthamic 
hele7/packback-rest-api-101 
• Two branches: master and 
api_with_fractal
Anatomy of the“Typical” 
Laravel Application 
• Model (Eloquent) + database 
• Controller + routes 
• View (Blade)
Problems! 
• What happens if we want to build an iOS/Android 
application with our data? 
• What happens if we want to use AngularJS or 
EmberJS? 
• What happens when we want to rebuild the front-end 
in a few years? 
• What happens if we want to let other companies work 
with our data? (Twitter API, Facebook API, etc.)
Solution: 
Let’s build an API! 
(Application Programming Interface)
What the hell does that even mean? 
• APIs only care about data - not what things look like 
• Data in, data out 
• JSON is what most of the cool kids are using 
• Keeps data types intact 
• You can also use XML if you like typing a lot 
• Turns everything into a string
<book> 
<id>1</id> 
<title>The Lord of the Rings</title> 
<author>J. R. R. Tolkien</author> 
</book> 
XML Example 
{ 
"book": { 
"id" : 1, 
"title": "The Lord of the Rings", 
"author": "J. R. R. Tolkien" 
} 
} 
JSON Example
Laravel makes it really easy 
• The client can access routes (which are basically just URLs) 
• Controllers handle logic (or call other classes to handle it for 
them) 
• Get data from or store data in database (via models) 
• ????? 
• PROFIT!!! 
• Return data as JSON
Stuff that we need to do 
• /createUser 
• /fetchUser 
• /setUserPassword 
• /updatePaymentInfo 
• /addBook 
• /getBook 
• /deleteBook 
• /addBooktoUser 
• /removeBook
Okay, this is getting 
kind of confusing
REST to the Rescue! 
• Representational State Transfer 
• Hopefully not what you’re doing if I’m boring you
It does CRUD 
• C - Create (POST) 
• R - Read (GET) 
• U - Update (PATCH/PUT) 
• D - Destroy (DELETE)
Stuff that we need to do 
(the RESTful way) 
• POST /users - Create a new user 
• GET /users - Get all users 
• GET /users/{id} - Get one user by ID 
• PATCH /users/{id} - Update a user by ID 
• DELETE /users/{id} - Delete a user by ID 
• POST /users/{id}/books/{id} - Add a book (or books) to a user 
• GET /users/{id}/books/ - Get a list of books for a specific user 
• etc.
Cool story, bro, but how 
do I actually build an API?
Step 1: Create Databases 
• Run database migrations (the same way as in a regular 
Laravel application) 
• Books, users, pivot 
php artisan migrate
app/database/migrations/2014_11_18_024437_create_users_table.<?php 
use IlluminateDatabaseSchemaBlueprint; 
use IlluminateDatabaseMigrationsMigration; 
class CreateUsersTable extends Migration { 
/** 
* Run the migrations. 
* 
* @return void 
*/ 
public function up() 
{ 
Schema::create('users', function(Blueprint $table) { 
$table->increments('id'); 
$table->string('email'); 
$table->string('name'); 
$table->string('password'); 
$table->timestamps(); 
}); 
} 
/** 
* Reverse the migrations. 
* 
* @return void 
*/ 
public function down() 
{ 
Schema::drop('users'); 
} 
}
app/database/migrations/2014_11_18_024939_create_books_table.<?php 
use IlluminateDatabaseSchemaBlueprint; 
use IlluminateDatabaseMigrationsMigration; 
class CreateBooksTable extends Migration { 
/** 
* Run the migrations. 
* 
* @return void 
*/ 
public function up() 
{ 
Schema::create('books', function(Blueprint $table) { 
$table->increments('id'); 
$table->string('isbn13'); 
$table->string('title'); 
$table->string('author'); 
$table->float('price'); 
$table->timestamps(); 
}); 
} 
/** 
* Reverse the migrations. 
* 
* @return void 
*/ 
public function down() 
{ 
Schema::drop('books'); 
} 
}
database/migrations/2014_11_18_025038_create_books_users_<?php 
use IlluminateDatabaseSchemaBlueprint; 
use IlluminateDatabaseMigrationsMigration; 
class CreateBooksUsersTable extends Migration { 
/** 
* Run the migrations. 
* 
* @return void 
*/ 
public function up() 
{ 
Schema::create('books_users', function(Blueprint $table) { 
$table->increments('id'); 
$table->integer('user_id')->unsigned(); 
$table->foreign('user_id')->references('id')->on('users'); 
$table->integer('book_id')->unsigned(); 
$table->foreign('book_id')->references('id')->on('books'); 
$table->timestamps(); 
}); 
} 
/** 
* Reverse the migrations. 
* 
* @return void 
*/ 
public function down() 
{ 
Schema::table('books_users', function(Blueprint $table) { 
$table->dropForeign('books_users_user_id_foreign'); 
$table->dropForeign('books_users_book_id_foreign'); 
}); 
Schema::drop('books_users'); 
}
Step 2: Seed data 
• Your life will be much easier if you fill your database with fake 
data 
• Faker is easy to use and has realistic fake data: 
https://github.com/fzaninotto/Faker 
• I generally do one seed file per table 
• Hook in database/seeds/DatabaseSeeder.php 
• Make sure you truncate every time the seeder is run or you will 
end up with a ton of data 
php artisan db:seed
Seed Users 
app/database/seeds/UserTableSeeder.php 
<?php 
use CarbonCarbon; 
use FakerFactory as Faker; 
class UserTableSeeder extends Seeder 
{ 
public function run() 
{ 
$faker = Faker::create(); 
DB::table('users')->truncate(); 
for ($i = 0; $i < 50; $i++) { 
DB::table('users')->insert([ 
'email' => $faker->email, 
'name' => $faker->name, 
'password' => Hash::make($faker->word), 
'created_at' => Carbon::now(), 
'updated_at' => Carbon::now() 
]); 
} 
} 
}
Seed Books 
app/database/seeds/BookTableSeeder.php 
<?php 
use CarbonCarbon; 
use FakerFactory as Faker; 
class BookTableSeeder extends Seeder 
{ 
public function run() 
{ 
$faker = Faker::create(); 
DB::table('books')->truncate(); 
for ($i = 0; $i < 50; $i++) { 
DB::table('books')->insert([ 
'isbn13' => $faker->ean13(), 
'title' => $faker->sentence, 
'author' => $faker->name, 
'price' => $faker->randomNumber(2) . '.' . $faker->randomNumber(2), 
'created_at' => Carbon::now(), 
'updated_at' => Carbon::now(), 
]); 
} 
} 
}
Seed User Books 
app/database/seeds/UserBookTableSeeder.php 
<?php 
use CarbonCarbon; 
class UserBookTableSeeder extends Seeder 
{ 
public function run() 
{ 
DB::table('books_users')->truncate(); 
for ($i = 1; $i < 51; $i++) { 
DB::table('books_users')->insert([ 
[ 
'user_id' => $i, 
'book_id' => $i, 
'created_at' => Carbon::now(), 
'updated_at' => Carbon::now() 
], 
[ 
'user_id' => $i, 
'book_id' => 51 - $i, 
'created_at' => Carbon::now(), 
'updated_at' => Carbon::now() 
] 
]); 
} 
} 
}
Step 3: Models 
• Very little difference compared to a more traditional 
Laravel app 
• Define a ManyToMany relationship between users and 
books
app/models/User.php 
class User extends Eloquent implements UserInterface, RemindableInterface { 
use UserTrait, RemindableTrait; 
protected $table = 'users'; 
protected $fillable = ['email', 'name', 'password']; 
protected $hidden = array('password', 'remember_token'); 
public function books() 
{ 
return $this->belongsToMany('Book', 'books_users'); 
} 
public function setPasswordAttribute($password) 
{ 
$this->attributes['password'] = Hash::make($password); 
} 
}
app/models/Book.php 
class Book extends Eloquent { 
protected $table = 'books'; 
protected $fillable = ['isbn13', 'title', 'author', 'price']; 
public function users() 
{ 
return $this->belongsToMany('User', 'books_users'); 
} 
}
Step 4: Routes 
• Should you use Laravel magic? (Route::resource() or 
Route::controller()) 
• Pros: Less code 
• Cons: Less code 
• It is generally clearer (to me) to explicitly define your 
routes (so you have a blueprint) 
• However, some people would disagree, so we’ll look 
at both
RESTful routes 
Option 1 (Less code) 
Route::group(['prefix' => 'api'], function() { 
Route::resource('users', 'UserController'); 
Route::resource('books', 'BookController'); 
}); 
This will automatically look for create, edit, 
index, show, store, update, and destroy 
methods in your controller.
RESTful Routes 
Option 2 (Explicit code) 
Route::group(['prefix' => 'api'], function() { 
Route::group(['prefix' => 'books'], function(){ 
Route::get('', array('uses' => 'BookController@index')); 
Route::get('{book_id}', array('uses' => 'BookController@show')); 
Route::post('', array('uses' => 'BookController@store')); 
Route::patch('{book_id}', array('uses' => 'BookController@update')); 
Route::delete('{book_id}', array('uses' => 'BookController@destroy')); 
}); 
Route::group(['prefix' => 'users'], function(){ 
Route::get('', array('uses' => 'UserController@index')); 
Route::get('{user_id}', array('uses' => 'UserController@show')); 
Route::get('{user_id}/books', array('uses' => 'UserController@showBooks')); 
Route::post('', array('uses' => 'UserController@store')); 
Route::post('{user_id}/books/{book_id}', array('uses' => 'UserController@storeBooks')); 
Route::patch('{user_id}', array('uses' => 'UserController@update')); 
Route::delete('{user_id}', array('uses' => 'UserController@destroy')); 
Route::delete('{user_id}/books/{book_id}', array('uses' => 'UserController@destroyBooks')); 
}); 
});
Let’s talk about status codes 
• Your API needs to send back a HTTP status code 
so that the client knows if the succeeded or failed 
(and if it failed, why) 
• 2xx - GREAT SUCCESS 
• 3xx - Redirect somewhere else 
• 4xx - Client errors 
• 5xx - Service errors
Some common status codes 
• 200 - generic OK 
• 201 - Created OK 
• 301 - Moved permanently and redirect to new location 
• 400 - Generic bad request (often used for validation on models) 
• 401 - Unauthorized (please sign in) 
• 403 - Unauthorized (you are signed in but shouldn’t be accessing this) 
• 404 - Does not exist 
• 500 - API dun goofed 
• 503 - API is not available for some reason 
• Plus lots more!
Step 5: Controllers 
• You will need (at least) 5 methods: index (get all), show (get 
one), store, update, destroy 
• What about create() and edit() (if you use 
Route::resource())? 
• You don’t need them if you’re building a pure data-driven 
API! 
• Use Postman (http://www.getpostman.com/) to interact 
with your API instead of Blade templates
Controllers / Index 
/** 
* Get all books 
* 
* @return Response 
*/ 
public function index() 
{ 
$books = Book::all(); 
return Response::json([ 
'data' => $books 
]); 
} 
/** 
* Get all users 
* 
* @return Response 
*/ 
public function index() 
{ 
$users = User::all(); 
return Response::json([ 
'data' => $users 
]); 
}
Controllers / Show 
/** 
* Get a single user 
* 
* @param $user_id 
* @return Response 
*/ 
public function show($user_id) 
{ 
$user = User::findOrFail($user_id); 
return Response::json([ 
'data' => $user 
]); 
} 
/** 
* Get a single book 
* 
* @param $book_id 
* @return Response 
*/ 
public function show($book_id) 
{ 
$book = Book::findOrFail($book_id); 
return Response::json([ 
'data' => $book 
]); 
}
Controllers / Store 
/** 
* Store a book 
* 
* @return Response 
*/ 
public function store() 
{ 
$input = Input::only('isbn13', 'title', 'author', 
'price'); 
$book = Book::create($input); 
return Response::json([ 
'data' => $book 
]); 
} 
/** 
* Store a user 
* 
* @return Response 
*/ 
public function store() 
{ 
$input = Input::only('email', 'name', 
'password'); 
$user = User::create($input); 
return Response::json([ 
'data' => $user 
]); 
}
Controllers / Update 
/** 
* Update a book 
* 
* @param $book_id 
* @return Response 
*/ 
public function update($book_id) 
{ 
$input = Input::only('isbn13', 'title', 
'author', 'price'); 
$book = Book::find($book_id); 
$book->update($input); 
return Response::json([ 
'data' => $book 
]); 
} 
/** 
* Update a user 
* 
* @param $user_id 
* @return Response 
*/ 
public function update($user_id) 
{ 
$input = Input::only('email', 'name', 
'password'); 
$user = User::findOrFail($user_id); 
$user->update($input); 
return Response::json([ 
'data' => $user 
]); 
}
Controllers / Destroy 
/** 
* Delete a book 
* 
* @param $book_id 
* @return Response 
*/ 
public function destroy($book_id) 
{ 
$book = User::findOrFail($book_id); 
$book->users()->sync([]); 
$book->delete(); 
return Response::json([ 
'success' => true 
]); 
} 
/** 
* Delete a user 
* 
* @param $user_id 
* @return Response 
*/ 
public function destroy($user_id) 
{ 
$user = User::findOrFail($user_id); 
$user->books()->sync([]); 
$user->delete(); 
return Response::json([ 
'success' => true 
]); 
}
Postman 
Demonstration
Let’s Review! 
• Request is sent through client (we used Postman, but could 
be AngularJS app, iPhone app, etc.) 
• Route interprets where it needs to go, sends it to 
appropriate controller + method 
• Controller takes the input and figures out what to do with it 
• Model (Eloquent) interacts with the database 
• Controller returns the data as JSON 
• Look, ma, no views!
A few problems… 
• We’re relying on the Laravel “hidden” attribute to avoid 
showing sensitive information but otherwise have no 
control over what is actually output. This is dangerous. 
• What happens if our database schema changes? 
• For example, we need to add a daily vs semester 
rental price and rename the “price” database column 
• How can we easily show a user + associated books with 
one API call?
Use transformers!
Transformers 
(Not like the robots) 
• “Transform” data per resource so that you have a lot more 
control over what you’re returning and its data type 
• Easy to build your own, or you can use Fractal for more 
advanced features: http://fractal.thephpleague.com/ 
• Serialize, or structure, your transformed data in a more 
specific way 
• Uses items (one object) and collections (group of objects) 
• Easily embed related resources within each other
Book Transformer 
app/Packback/Transformers/BookTransformer.php 
/** 
* Turn book object into generic array 
* 
* @param Book $book 
* @return array 
*/ 
public function transform(Book $book) 
{ 
return [ 
'id' => (int) $book->id, 
'isbn13' => $book->isbn13, 
'title' => $book->title, 
'author' => $book->author, 
// If we needed to rename the 'price' field to 'msrp' 
'msrp' => '$' . money_format('%i', $book->price) 
]; 
}
User Transformer 
app/Packback/Transformers/UserTransformer.php 
/** 
* Turn user object into generic array 
* 
* @param User $user 
* @return array 
*/ 
public function transform(User $user) 
{ 
return [ 
'id' => (int) $user->id, 
'name' => $user->name, 
'email' => $user->email 
]; 
}
/** 
* List of resources possible to include 
* 
* @var array 
*/ 
protected $availableIncludes = [ 
'books' 
]; 
/** 
* Include books in user 
* 
* @param User $user 
* @return LeagueFractalItemResource 
*/ 
public function includeBooks(User $user) 
{ 
$books = $user->books; 
return $this->collection($books, new BookTransformer); 
}
API Controller 
Extend the ApiController in 
UserController and BookController 
/** 
* Wrapper for Laravel's Response::json() method 
* 
* @param array $array 
* @param array $headers 
* @return mixed 
*/ 
protected function respondWithArray(array $array, array $headers = []) 
{ 
return Response::json($array, $this->statusCode, $headers); 
} 
class UserController extends ApiController 
class BookController extends ApiController 
app/controllers/ApiController.php
/** 
* Respond with Fractal Item 
* 
* @param $item 
* @param $callback 
* @return mixed 
*/ 
protected function respondWithItem($item, $callback) 
{ 
$resource = new Item($item, $callback); 
$rootScope = $this->fractal->createData($resource); 
return $this->respondWithArray($rootScope->toArray()); 
} 
/** 
* Respond with Fractal Collection 
* 
* @param $collection 
* @param $callback 
* @return mixed 
*/ 
protected function respondWithCollection($collection, $callback) 
{ 
$resource = new Collection($collection, $callback); 
$rootScope = $this->fractal->createData($resource); 
return $this->respondWithArray($rootScope->toArray()); 
}
Controller with Fractal 
public function index() 
{ 
$books = Book::all(); 
return $this->respondWithCollection($books, new BookTransformer); 
} 
public function show($book_id) 
{ 
$book = Book::findOrFail($book_id); 
return $this->respondWithItem($book, new BookTransformer); 
}
Improved Postman API Calls 
with Fractal
A Disclaimer 
• This app is an over-simplified example 
• Feel free to ignore everything I’ve told you tonight 
• Different conventions/opinions 
• Strict REST doesn’t make sense for every scenario 
• BUT the more you scale, the harder it will be to keep 
your code organized
REST APIs with Laravel 102 
AKA 
Things we didn’t have time to cover tonight 
• Testing :( 
• Pagination - return lots of records, a little bit at a time 
• Validation 
• Better error handling 
• Authentication 
• OOP best practices + design patterns
Questions? 
Twitter: @samanthageitz 
Email: samanthamichele7@gmail.com

More Related Content

PPTX
REST-API introduction for developers
PDF
introduction to Vue.js 3
PPT
Understanding REST
PPTX
Understanding REST APIs in 5 Simple Steps
PDF
OpenAPI 3.0, And What It Means for the Future of Swagger
ODP
Basics of VueJS
PDF
Laravel 101
PPTX
REST API Design & Development
REST-API introduction for developers
introduction to Vue.js 3
Understanding REST
Understanding REST APIs in 5 Simple Steps
OpenAPI 3.0, And What It Means for the Future of Swagger
Basics of VueJS
Laravel 101
REST API Design & Development

What's hot (20)

PPTX
Web development presentation.pptx
PPTX
PPTX
Full Stack Web Development
PPTX
.Net Core
PDF
Modern Web Development
PPTX
web development.pptx
PPT
ASP.NET MVC Presentation
PPTX
Rest & RESTful WebServices
PDF
Web Designing Presentation
ODP
Joomla REST API
PPT
API 101 - Understanding APIs
PDF
Restful Web Services
PPTX
An Introduction To REST API
PPTX
Laravel introduction
PDF
1. web technology basics
PPTX
PPTX
REST API
PDF
Java persistence api 2.1
PDF
Chapter 1 semantic web
PPTX
OAuth2 + API Security
Web development presentation.pptx
Full Stack Web Development
.Net Core
Modern Web Development
web development.pptx
ASP.NET MVC Presentation
Rest & RESTful WebServices
Web Designing Presentation
Joomla REST API
API 101 - Understanding APIs
Restful Web Services
An Introduction To REST API
Laravel introduction
1. web technology basics
REST API
Java persistence api 2.1
Chapter 1 semantic web
OAuth2 + API Security
Ad

Viewers also liked (20)

PDF
Laravel 5 Annotations: RESTful API routing
PDF
Web services with laravel
PDF
RESTful API development in Laravel 4 - Christopher Pecoraro
PDF
Bootstrat REST APIs with Laravel 5
ODP
REST API Laravel
PDF
Your rest api using laravel
PDF
QA Lab: тестирование ПО. Станислав Шмидт: "Self-testing REST APIs with API Fi...
PDF
Laravel 5.2 Gates, AuthServiceProvider and Policies
ODP
Javascript laravel's friend
PPTX
Super powered API testing
PDF
jQuery Keynote - Fall 2010
PDF
Service-Oriented Architecture
PDF
Developing and Testing a MongoDB and Node.js REST API
PDF
Laravel Restful API and AngularJS
PPT
SOAP-UI The Web service Testing
PDF
Api testing
PDF
4 Major Advantages of API Testing
PDF
OpenStack Tempest and REST API testing
PDF
Auto encoding-variational-bayes
PPTX
software testing on whatsapp
Laravel 5 Annotations: RESTful API routing
Web services with laravel
RESTful API development in Laravel 4 - Christopher Pecoraro
Bootstrat REST APIs with Laravel 5
REST API Laravel
Your rest api using laravel
QA Lab: тестирование ПО. Станислав Шмидт: "Self-testing REST APIs with API Fi...
Laravel 5.2 Gates, AuthServiceProvider and Policies
Javascript laravel's friend
Super powered API testing
jQuery Keynote - Fall 2010
Service-Oriented Architecture
Developing and Testing a MongoDB and Node.js REST API
Laravel Restful API and AngularJS
SOAP-UI The Web service Testing
Api testing
4 Major Advantages of API Testing
OpenStack Tempest and REST API testing
Auto encoding-variational-bayes
software testing on whatsapp
Ad

Similar to REST APIs in Laravel 101 (20)

DOCX
Laravel
PDF
Getting to know Laravel 5
PDF
What's New In Laravel 5
PDF
SDPHP Lightning Talk - Let's Talk Laravel
PDF
MidwestPHP 2016 - Adventures in Laravel 5
PDF
Laravel intake 37 all days
PPTX
CRUD presentation of laravel application.pptx
PDF
How to Create REST API Using Laravel Framework
PDF
Building RESTful APIs with Laravel A Complete Guide.pdf
PPTX
Introduction to Laravel Framework (5.2)
PPTX
Introduction to Monsoon PHP framework
PDF
Object Oriented Programming with Laravel - Session 6
PDF
Building Lithium Apps
PPTX
Introduction to laravel framework
PPTX
What-is-Laravel-23-August-2017.pptx
PPTX
Laravel Crud Tutorial Basic Step by Stepy S
PDF
Laravel and Django and Rails, Oh My!
PDF
Adventures in Laravel 5 SunshinePHP 2016 Tutorial
PDF
Doctrine For Beginners
PDF
Building Scalable Applications with Laravel
Laravel
Getting to know Laravel 5
What's New In Laravel 5
SDPHP Lightning Talk - Let's Talk Laravel
MidwestPHP 2016 - Adventures in Laravel 5
Laravel intake 37 all days
CRUD presentation of laravel application.pptx
How to Create REST API Using Laravel Framework
Building RESTful APIs with Laravel A Complete Guide.pdf
Introduction to Laravel Framework (5.2)
Introduction to Monsoon PHP framework
Object Oriented Programming with Laravel - Session 6
Building Lithium Apps
Introduction to laravel framework
What-is-Laravel-23-August-2017.pptx
Laravel Crud Tutorial Basic Step by Stepy S
Laravel and Django and Rails, Oh My!
Adventures in Laravel 5 SunshinePHP 2016 Tutorial
Doctrine For Beginners
Building Scalable Applications with Laravel

Recently uploaded (20)

PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Electronic commerce courselecture one. Pdf
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PPTX
sap open course for s4hana steps from ECC to s4
PDF
Encapsulation theory and applications.pdf
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PPTX
Big Data Technologies - Introduction.pptx
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
NewMind AI Weekly Chronicles - August'25-Week II
PDF
Approach and Philosophy of On baking technology
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPT
Teaching material agriculture food technology
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Chapter 3 Spatial Domain Image Processing.pdf
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Network Security Unit 5.pdf for BCA BBA.
Electronic commerce courselecture one. Pdf
Review of recent advances in non-invasive hemoglobin estimation
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
sap open course for s4hana steps from ECC to s4
Encapsulation theory and applications.pdf
MIND Revenue Release Quarter 2 2025 Press Release
Building Integrated photovoltaic BIPV_UPV.pdf
Digital-Transformation-Roadmap-for-Companies.pptx
Big Data Technologies - Introduction.pptx
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
NewMind AI Weekly Chronicles - August'25-Week II
Approach and Philosophy of On baking technology
Programs and apps: productivity, graphics, security and other tools
Reach Out and Touch Someone: Haptics and Empathic Computing
Teaching material agriculture food technology
Per capita expenditure prediction using model stacking based on satellite ima...
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...

REST APIs in Laravel 101

  • 1. REST API Development and Testing 101 By Samantha Geitz
  • 2. A bit about me • 3 years of experience in web development • Now work for Packback • Background mostly in WordPress, a little bit of Rails, now use Laravel full-time • About 1 year experience building APIs
  • 3. What we’re going to talk about tonight • “Typical” Laravel application / MVC • What is an API, and why should you care? • Shitty way to structure an API • Better way to structure an API • Demonstration of interacting with API
  • 4. Our Application • Packback • Digital textbook rentals for college students • Resources: Users, Books • Users need to be able to “rent” books • https://github.com/samanthamic hele7/packback-rest-api-101 • Two branches: master and api_with_fractal
  • 5. Anatomy of the“Typical” Laravel Application • Model (Eloquent) + database • Controller + routes • View (Blade)
  • 6. Problems! • What happens if we want to build an iOS/Android application with our data? • What happens if we want to use AngularJS or EmberJS? • What happens when we want to rebuild the front-end in a few years? • What happens if we want to let other companies work with our data? (Twitter API, Facebook API, etc.)
  • 7. Solution: Let’s build an API! (Application Programming Interface)
  • 8. What the hell does that even mean? • APIs only care about data - not what things look like • Data in, data out • JSON is what most of the cool kids are using • Keeps data types intact • You can also use XML if you like typing a lot • Turns everything into a string
  • 9. <book> <id>1</id> <title>The Lord of the Rings</title> <author>J. R. R. Tolkien</author> </book> XML Example { "book": { "id" : 1, "title": "The Lord of the Rings", "author": "J. R. R. Tolkien" } } JSON Example
  • 10. Laravel makes it really easy • The client can access routes (which are basically just URLs) • Controllers handle logic (or call other classes to handle it for them) • Get data from or store data in database (via models) • ????? • PROFIT!!! • Return data as JSON
  • 11. Stuff that we need to do • /createUser • /fetchUser • /setUserPassword • /updatePaymentInfo • /addBook • /getBook • /deleteBook • /addBooktoUser • /removeBook
  • 12. Okay, this is getting kind of confusing
  • 13. REST to the Rescue! • Representational State Transfer • Hopefully not what you’re doing if I’m boring you
  • 14. It does CRUD • C - Create (POST) • R - Read (GET) • U - Update (PATCH/PUT) • D - Destroy (DELETE)
  • 15. Stuff that we need to do (the RESTful way) • POST /users - Create a new user • GET /users - Get all users • GET /users/{id} - Get one user by ID • PATCH /users/{id} - Update a user by ID • DELETE /users/{id} - Delete a user by ID • POST /users/{id}/books/{id} - Add a book (or books) to a user • GET /users/{id}/books/ - Get a list of books for a specific user • etc.
  • 16. Cool story, bro, but how do I actually build an API?
  • 17. Step 1: Create Databases • Run database migrations (the same way as in a regular Laravel application) • Books, users, pivot php artisan migrate
  • 18. app/database/migrations/2014_11_18_024437_create_users_table.<?php use IlluminateDatabaseSchemaBlueprint; use IlluminateDatabaseMigrationsMigration; class CreateUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('users', function(Blueprint $table) { $table->increments('id'); $table->string('email'); $table->string('name'); $table->string('password'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('users'); } }
  • 19. app/database/migrations/2014_11_18_024939_create_books_table.<?php use IlluminateDatabaseSchemaBlueprint; use IlluminateDatabaseMigrationsMigration; class CreateBooksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('books', function(Blueprint $table) { $table->increments('id'); $table->string('isbn13'); $table->string('title'); $table->string('author'); $table->float('price'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('books'); } }
  • 20. database/migrations/2014_11_18_025038_create_books_users_<?php use IlluminateDatabaseSchemaBlueprint; use IlluminateDatabaseMigrationsMigration; class CreateBooksUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('books_users', function(Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned(); $table->foreign('user_id')->references('id')->on('users'); $table->integer('book_id')->unsigned(); $table->foreign('book_id')->references('id')->on('books'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('books_users', function(Blueprint $table) { $table->dropForeign('books_users_user_id_foreign'); $table->dropForeign('books_users_book_id_foreign'); }); Schema::drop('books_users'); }
  • 21. Step 2: Seed data • Your life will be much easier if you fill your database with fake data • Faker is easy to use and has realistic fake data: https://github.com/fzaninotto/Faker • I generally do one seed file per table • Hook in database/seeds/DatabaseSeeder.php • Make sure you truncate every time the seeder is run or you will end up with a ton of data php artisan db:seed
  • 22. Seed Users app/database/seeds/UserTableSeeder.php <?php use CarbonCarbon; use FakerFactory as Faker; class UserTableSeeder extends Seeder { public function run() { $faker = Faker::create(); DB::table('users')->truncate(); for ($i = 0; $i < 50; $i++) { DB::table('users')->insert([ 'email' => $faker->email, 'name' => $faker->name, 'password' => Hash::make($faker->word), 'created_at' => Carbon::now(), 'updated_at' => Carbon::now() ]); } } }
  • 23. Seed Books app/database/seeds/BookTableSeeder.php <?php use CarbonCarbon; use FakerFactory as Faker; class BookTableSeeder extends Seeder { public function run() { $faker = Faker::create(); DB::table('books')->truncate(); for ($i = 0; $i < 50; $i++) { DB::table('books')->insert([ 'isbn13' => $faker->ean13(), 'title' => $faker->sentence, 'author' => $faker->name, 'price' => $faker->randomNumber(2) . '.' . $faker->randomNumber(2), 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); } } }
  • 24. Seed User Books app/database/seeds/UserBookTableSeeder.php <?php use CarbonCarbon; class UserBookTableSeeder extends Seeder { public function run() { DB::table('books_users')->truncate(); for ($i = 1; $i < 51; $i++) { DB::table('books_users')->insert([ [ 'user_id' => $i, 'book_id' => $i, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now() ], [ 'user_id' => $i, 'book_id' => 51 - $i, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now() ] ]); } } }
  • 25. Step 3: Models • Very little difference compared to a more traditional Laravel app • Define a ManyToMany relationship between users and books
  • 26. app/models/User.php class User extends Eloquent implements UserInterface, RemindableInterface { use UserTrait, RemindableTrait; protected $table = 'users'; protected $fillable = ['email', 'name', 'password']; protected $hidden = array('password', 'remember_token'); public function books() { return $this->belongsToMany('Book', 'books_users'); } public function setPasswordAttribute($password) { $this->attributes['password'] = Hash::make($password); } }
  • 27. app/models/Book.php class Book extends Eloquent { protected $table = 'books'; protected $fillable = ['isbn13', 'title', 'author', 'price']; public function users() { return $this->belongsToMany('User', 'books_users'); } }
  • 28. Step 4: Routes • Should you use Laravel magic? (Route::resource() or Route::controller()) • Pros: Less code • Cons: Less code • It is generally clearer (to me) to explicitly define your routes (so you have a blueprint) • However, some people would disagree, so we’ll look at both
  • 29. RESTful routes Option 1 (Less code) Route::group(['prefix' => 'api'], function() { Route::resource('users', 'UserController'); Route::resource('books', 'BookController'); }); This will automatically look for create, edit, index, show, store, update, and destroy methods in your controller.
  • 30. RESTful Routes Option 2 (Explicit code) Route::group(['prefix' => 'api'], function() { Route::group(['prefix' => 'books'], function(){ Route::get('', array('uses' => 'BookController@index')); Route::get('{book_id}', array('uses' => 'BookController@show')); Route::post('', array('uses' => 'BookController@store')); Route::patch('{book_id}', array('uses' => 'BookController@update')); Route::delete('{book_id}', array('uses' => 'BookController@destroy')); }); Route::group(['prefix' => 'users'], function(){ Route::get('', array('uses' => 'UserController@index')); Route::get('{user_id}', array('uses' => 'UserController@show')); Route::get('{user_id}/books', array('uses' => 'UserController@showBooks')); Route::post('', array('uses' => 'UserController@store')); Route::post('{user_id}/books/{book_id}', array('uses' => 'UserController@storeBooks')); Route::patch('{user_id}', array('uses' => 'UserController@update')); Route::delete('{user_id}', array('uses' => 'UserController@destroy')); Route::delete('{user_id}/books/{book_id}', array('uses' => 'UserController@destroyBooks')); }); });
  • 31. Let’s talk about status codes • Your API needs to send back a HTTP status code so that the client knows if the succeeded or failed (and if it failed, why) • 2xx - GREAT SUCCESS • 3xx - Redirect somewhere else • 4xx - Client errors • 5xx - Service errors
  • 32. Some common status codes • 200 - generic OK • 201 - Created OK • 301 - Moved permanently and redirect to new location • 400 - Generic bad request (often used for validation on models) • 401 - Unauthorized (please sign in) • 403 - Unauthorized (you are signed in but shouldn’t be accessing this) • 404 - Does not exist • 500 - API dun goofed • 503 - API is not available for some reason • Plus lots more!
  • 33. Step 5: Controllers • You will need (at least) 5 methods: index (get all), show (get one), store, update, destroy • What about create() and edit() (if you use Route::resource())? • You don’t need them if you’re building a pure data-driven API! • Use Postman (http://www.getpostman.com/) to interact with your API instead of Blade templates
  • 34. Controllers / Index /** * Get all books * * @return Response */ public function index() { $books = Book::all(); return Response::json([ 'data' => $books ]); } /** * Get all users * * @return Response */ public function index() { $users = User::all(); return Response::json([ 'data' => $users ]); }
  • 35. Controllers / Show /** * Get a single user * * @param $user_id * @return Response */ public function show($user_id) { $user = User::findOrFail($user_id); return Response::json([ 'data' => $user ]); } /** * Get a single book * * @param $book_id * @return Response */ public function show($book_id) { $book = Book::findOrFail($book_id); return Response::json([ 'data' => $book ]); }
  • 36. Controllers / Store /** * Store a book * * @return Response */ public function store() { $input = Input::only('isbn13', 'title', 'author', 'price'); $book = Book::create($input); return Response::json([ 'data' => $book ]); } /** * Store a user * * @return Response */ public function store() { $input = Input::only('email', 'name', 'password'); $user = User::create($input); return Response::json([ 'data' => $user ]); }
  • 37. Controllers / Update /** * Update a book * * @param $book_id * @return Response */ public function update($book_id) { $input = Input::only('isbn13', 'title', 'author', 'price'); $book = Book::find($book_id); $book->update($input); return Response::json([ 'data' => $book ]); } /** * Update a user * * @param $user_id * @return Response */ public function update($user_id) { $input = Input::only('email', 'name', 'password'); $user = User::findOrFail($user_id); $user->update($input); return Response::json([ 'data' => $user ]); }
  • 38. Controllers / Destroy /** * Delete a book * * @param $book_id * @return Response */ public function destroy($book_id) { $book = User::findOrFail($book_id); $book->users()->sync([]); $book->delete(); return Response::json([ 'success' => true ]); } /** * Delete a user * * @param $user_id * @return Response */ public function destroy($user_id) { $user = User::findOrFail($user_id); $user->books()->sync([]); $user->delete(); return Response::json([ 'success' => true ]); }
  • 40. Let’s Review! • Request is sent through client (we used Postman, but could be AngularJS app, iPhone app, etc.) • Route interprets where it needs to go, sends it to appropriate controller + method • Controller takes the input and figures out what to do with it • Model (Eloquent) interacts with the database • Controller returns the data as JSON • Look, ma, no views!
  • 41. A few problems… • We’re relying on the Laravel “hidden” attribute to avoid showing sensitive information but otherwise have no control over what is actually output. This is dangerous. • What happens if our database schema changes? • For example, we need to add a daily vs semester rental price and rename the “price” database column • How can we easily show a user + associated books with one API call?
  • 43. Transformers (Not like the robots) • “Transform” data per resource so that you have a lot more control over what you’re returning and its data type • Easy to build your own, or you can use Fractal for more advanced features: http://fractal.thephpleague.com/ • Serialize, or structure, your transformed data in a more specific way • Uses items (one object) and collections (group of objects) • Easily embed related resources within each other
  • 44. Book Transformer app/Packback/Transformers/BookTransformer.php /** * Turn book object into generic array * * @param Book $book * @return array */ public function transform(Book $book) { return [ 'id' => (int) $book->id, 'isbn13' => $book->isbn13, 'title' => $book->title, 'author' => $book->author, // If we needed to rename the 'price' field to 'msrp' 'msrp' => '$' . money_format('%i', $book->price) ]; }
  • 45. User Transformer app/Packback/Transformers/UserTransformer.php /** * Turn user object into generic array * * @param User $user * @return array */ public function transform(User $user) { return [ 'id' => (int) $user->id, 'name' => $user->name, 'email' => $user->email ]; }
  • 46. /** * List of resources possible to include * * @var array */ protected $availableIncludes = [ 'books' ]; /** * Include books in user * * @param User $user * @return LeagueFractalItemResource */ public function includeBooks(User $user) { $books = $user->books; return $this->collection($books, new BookTransformer); }
  • 47. API Controller Extend the ApiController in UserController and BookController /** * Wrapper for Laravel's Response::json() method * * @param array $array * @param array $headers * @return mixed */ protected function respondWithArray(array $array, array $headers = []) { return Response::json($array, $this->statusCode, $headers); } class UserController extends ApiController class BookController extends ApiController app/controllers/ApiController.php
  • 48. /** * Respond with Fractal Item * * @param $item * @param $callback * @return mixed */ protected function respondWithItem($item, $callback) { $resource = new Item($item, $callback); $rootScope = $this->fractal->createData($resource); return $this->respondWithArray($rootScope->toArray()); } /** * Respond with Fractal Collection * * @param $collection * @param $callback * @return mixed */ protected function respondWithCollection($collection, $callback) { $resource = new Collection($collection, $callback); $rootScope = $this->fractal->createData($resource); return $this->respondWithArray($rootScope->toArray()); }
  • 49. Controller with Fractal public function index() { $books = Book::all(); return $this->respondWithCollection($books, new BookTransformer); } public function show($book_id) { $book = Book::findOrFail($book_id); return $this->respondWithItem($book, new BookTransformer); }
  • 50. Improved Postman API Calls with Fractal
  • 51. A Disclaimer • This app is an over-simplified example • Feel free to ignore everything I’ve told you tonight • Different conventions/opinions • Strict REST doesn’t make sense for every scenario • BUT the more you scale, the harder it will be to keep your code organized
  • 52. REST APIs with Laravel 102 AKA Things we didn’t have time to cover tonight • Testing :( • Pagination - return lots of records, a little bit at a time • Validation • Better error handling • Authentication • OOP best practices + design patterns