Slide 1

Slide 1 text

Building a RESTful API with Laravel Saad EL HAJJAJI saadel.me - @_saadel - github.com/saadel

Slide 2

Slide 2 text

Application Programming Interface Building a RESTful API

Slide 3

Slide 3 text

REpresentational State Transfer Building a RESTful API

Slide 4

Slide 4 text

Resource-based Building a RESTful API Things vs. Actions Nouns vs. Verbs

Slide 5

Slide 5 text

Representations Building a RESTful API Transferred between client and server Typically JSON or XML

Slide 6

Slide 6 text

Constraints Building a RESTful API • Uniform Interface • Stateless HTTP Verbs / URIs / HTTP Responses • Client-Server • Cacheable • Layered System Server Responses • Code On Demand

Slide 7

Slide 7 text

Building a RESTful API with Laravel

Slide 8

Slide 8 text

Building a RESTful API with Laravel github.com/saadel/mphpa-blog-api Code available on GitHub

Slide 9

Slide 9 text

1. Planning 2. Building 3. Tweaking

Slide 10

Slide 10 text

1. Planning

Slide 11

Slide 11 text

1 1 1 1..* 1..* 1..*

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Action Plan

Slide 14

Slide 14 text

Action Plan Users - Create - Read - Update - Delete - List - Posts - Comments Posts - Create - Read - Update - Delete - List - Comments Comments - Create - Read - Update - Delete - List

Slide 15

Slide 15 text

Endpoints

Slide 16

Slide 16 text

Endpoints /{resource}/{id} /{resource}/{id}/{sub-resource}/{id}

Slide 17

Slide 17 text

Endpoints /users/1 /users/1/posts/3

Slide 18

Slide 18 text

Endpoints /posts/2 /posts/2/comments/4

Slide 19

Slide 19 text

Endpoints /posts /posts/2 /posts/2/comments /posts/2/comments/4

Slide 20

Slide 20 text

2. Building

Slide 21

Slide 21 text

Status Codes 200: Success! 201: Resource created 204: Success, but no content to return 400: Request not fulfilled 401: Not authenticated 403: Refusal to respond 404: Not found 500: Other error

Slide 22

Slide 22 text

Displaying Data

Slide 23

Slide 23 text

The Direct Approach Post::find($id)->toArray(), ]); } public function index() { return json_encode([ 'data' => Post::all()->toArray(), ]); } }

Slide 24

Slide 24 text

Formatting Data [ 'id' => (int) $post->id, 'title' => $post->title, 'content' => $post->content, 'created_at' => (string) $post->created_at, ], ]); } }

Slide 25

Slide 25 text

Fractal

Slide 26

Slide 26 text

Fractal Output complex, flexible, AJAX/RESTful data structures.

Slide 27

Slide 27 text

Fractal Output complex, flexible, AJAX/RESTful data structures. $ composer require league/fractal

Slide 28

Slide 28 text

Fractal Output complex, flexible, AJAX/RESTful data structures. $ composer require league/fractal 1. Create Transformers

Slide 29

Slide 29 text

Fractal Output complex, flexible, AJAX/RESTful data structures. $ composer require league/fractal 1. Create Transformers 2. Create ApiController

Slide 30

Slide 30 text

Fractal Output complex, flexible, AJAX/RESTful data structures. $ composer require league/fractal 1. Create Transformers 2. Create ApiController 3. Update Controllers

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

(int) $post->id, 'content' => $post->content, 'created_at' => (String) $post->created_at, ]; } }

Slide 33

Slide 33 text

ApiController class ApiController extends Controller { protected $statusCode = 200; public function __construct(Manager $fractal) { $this->fractal = $fractal; } protected function respondWithItem($item, $callback) { //Create item then return it with respondWithArray() } protected function respondWithCollection($collection, $callback){ //Create Collection then return it with respondWithArray() } protected function respondWithArray(array $array, array $headers = []) { $response = response()->json($array, $this->statusCode, $headers); return $response; }

Slide 34

Slide 34 text

ApiController protected function respondWithError($message, $errorCode) { return $this->respondWithArray([ 'error' => [ 'code' => $errorCode, 'http_code' => $this->statusCode, 'message' => $message, ] ]); } }

Slide 35

Slide 35 text

Update Controllers class PostController extends ApiController { public function index() { $posts = Post::take(10)->get(); return $this->respondWithCollection($posts, new PostTransformer); } public function show($id) { $post = Post::find($id); return $this->respondWithItem($post, new PostTransformer); } }

Slide 36

Slide 36 text

Routes Route::get('/users', 'UserController@index'); Route::get('/users/{id}', 'UserController@show'); Route::get('/posts', 'PostController@index'); Route::get('/posts/{id}', 'PostController@show'); Route::get('/comments', 'CommentController@index'); Route::get('/comments/{id}', 'CommentController@show');

Slide 37

Slide 37 text

Resource Groups Action Create Read Update Delete List Endpoint POST /posts GET /posts/X PUT /posts/X DELETE /posts/X GET /posts

Slide 38

Slide 38 text

POST /posts public function store(Request $request) { $post = new Post(); $post->content = $request->content; $post->user = User::find($request->user_id); $post->save(); return response()->json([ "msg" => "Success", "id" => $post->id() ], 200 ); }

Slide 39

Slide 39 text

PUT /posts/X public function update(Request $request, $id) { $post = Post::find($id); $post->content = $request->content; $post->user = User::find($request->user_id); $post->save(); return response()->json([ "msg" => "Success", ], 200 ); }

Slide 40

Slide 40 text

DELETE /posts/X public function destroy($id) { $post = Post::find($id); $post->delete(); return response()->json([ "msg" => "Success", ], 200 ); }

Slide 41

Slide 41 text

Errors

Slide 42

Slide 42 text

Error { “error”: { “code”: "GEN-NOTFOUND", “http_code”: 404, “message”: "A post with this ID doesn't exist." } }

Slide 43

Slide 43 text

class ApiController extends Controller { const CODE_WRONG_ARGS = 'GEN-1WT45PO'; const CODE_NOT_FOUND = 'GEN-LPF2FV6'; const CODE_INTERNAL_ERROR = 'GEN-A9AR4H'; const CODE_UNAUTHORIZED = 'GEN-M36UI'; const CODE_FORBIDDEN = ‘GEN-23T7'; protected function respondWithError($message, $errorCode) { return $this->respondWithArray([ 'error' => [ 'code' => $errorCode, 'http_code' => $this->statusCode, 'message' => $message, ] ]); } public function errorNotFound($message = 'Resource Not Found') { return $this->setStatusCode(404)->respondWithError($message, self::CODE_NOT_FOUND); } public function errorUnauthorized($message = 'Unauthorized') { return $this->setStatusCode(401)->respondWithError($message, self::CODE_UNAUTHORIZED); } //errorForbidden, errorInternalError, …

Slide 44

Slide 44 text

Data Relationships

Slide 45

Slide 45 text

Sub-Resources /posts/1/comments

Slide 46

Slide 46 text

Foreign Key Arrays { "post": { "id": 1 "title": "Progressive Enhancement is Dead", "comments": ["1", "2"], "_links": { "user": "/people/saadel" } } }

Slide 47

Slide 47 text

Embedded Documents comments; return $this->collection($comments, new CommentTransformer); } // embedUser… }

Slide 48

Slide 48 text

3. Tweaking

Slide 49

Slide 49 text

Versioning

Slide 50

Slide 50 text

v1/users/1 v1/posts/1?embed=comments

Slide 51

Slide 51 text

// app/Http/routes.php Route::group(array('prefix' => 'v1', function() { // Routes here... });

Slide 52

Slide 52 text

Documentation

Slide 53

Slide 53 text

Documentation Swagger apiblueprint

Slide 54

Slide 54 text

Debugging

Slide 55

Slide 55 text

Debugging Command-line debugging curl -X POST http://localhost/users/gf325/posts --data
 @payload.json

Slide 56

Slide 56 text

Debugging Browser debugging HTTP Clients or REST Clients Clockwork

Slide 57

Slide 57 text

POSTMAN

Slide 58

Slide 58 text

Clockwork

Slide 59

Slide 59 text

Debugging Network debugging

Slide 60

Slide 60 text

Resources Build APIs you won’t hate by Phil Sturgeon REST API Design Rulebook by Mark Masse REST API concepts and examples

Slide 61

Slide 61 text

Closing Thoughts

Slide 62

Slide 62 text

thanks ! Saad EL HAJJAJI saadel.me - @_saadel - github.com/saadel