2쪽
Build APIs You Won’t Hate Everyone and their dog wants an API, so you should probably learn how to build them. Phil Sturgeon This book is for sale at http://leanpub.com/build-apis-you-wont-hate This version was published on 2015-03-02 This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do. ©2013 - 2015 Phil Sturgeon
3쪽
Tweet This Book! Please help Phil Sturgeon by spreading the word about this book on Twitter! The suggested tweet for this book is: I just bought @philsturgeon’s book about APIs because he said if I didn’t he would hurt me: http://apisyouwonthate.com The suggested hashtag for this book is #apisyouwonthate. Find out what other people are saying about the book by clicking on this link to search for this hashtag on Twitter: https://twitter.com/search?q=#apisyouwonthate
4쪽
To all the people who built the amazing technologies this book talks about. Without you, I would be much further away from getting my boat.
5쪽
Contents Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii 1. Useful Database Seeding . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 Introduction to Database Seeding . . . . . . . . . . . . . . . . . . . 1 1.3 Building Seeders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.4 That is about it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.5 Secondary Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.6 When to run this? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2. Planning and Creating Endpoints . . . . . . . . . . . . . . . . . . . . . 12 2.1 Functional Requirements . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2 Endpoint Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.3 Planning Endpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3. Input and Output Theory . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.2 Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.3 Responses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.4 Supporting Formats . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.5 Content Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 4. Status Codes, Errors and Messages . . . . . . . . . . . . . . . . . . . . 38 4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 4.2 HTTP Status Codes . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 4.3 Error Codes and Error Messages . . . . . . . . . . . . . . . . . . . . 40
6쪽
CONTENTS 4.4 Error or Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.5 Standards for Error Responses . . . . . . . . . . . . . . . . . . . . . 44 4.6 Common Pitfalls . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 5. Endpoint Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 5.2 Concepts & Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 5.3 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 5.4 Initialise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.5 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 5.6 Scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 5.7 Prepping Behat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 5.8 Running Behat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 6. Outputting Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 6.2 The Direct Approach . . . . . . . . . . . . . . . . . . . . . . . . . . 57 6.3 Transformations with Fractal . . . . . . . . . . . . . . . . . . . . . . 61 6.4 Hiding Schema Updates . . . . . . . . . . . . . . . . . . . . . . . . . 67 6.5 Outputting Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 6.6 Testing this Output . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 6.7 Homework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 7. Data Relationships . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 7.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 7.2 Sub-Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 7.3 Foreign Key Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 7.4 Compound Documents (a.k.a Side-Loading) . . . . . . . . . . . . . . 77 7.5 Embedded Documents (a.k.a Nesting) . . . . . . . . . . . . . . . . . 78 7.6 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 8. Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 8.2 Command-line Debugging . . . . . . . . . . . . . . . . . . . . . . . 87 8.3 Browser Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 8.4 Network Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7쪽
CONTENTS 9. Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 9.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 9.2 When is Authentication Useful? . . . . . . . . . . . . . . . . . . . . 98 9.3 Different Approaches to Authentication . . . . . . . . . . . . . . . . 99 9.4 Implementing an OAuth 2.0 Server . . . . . . . . . . . . . . . . . . . 108 9.5 Where the OAuth 2.0 Server Lives . . . . . . . . . . . . . . . . . . . 110 9.6 Understanding OAuth 2.0 Grant Types . . . . . . . . . . . . . . . . 111 10.Pagination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 10.2 Paginators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 10.3 Offsets and Cursors . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 11.Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 11.2 Types of Documentation . . . . . . . . . . . . . . . . . . . . . . . . 126 11.3 Picking a Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 11.4 Setting up API Blueprint and Aglio . . . . . . . . . . . . . . . . . . 129 11.5 Learning API Blueprint Syntax . . . . . . . . . . . . . . . . . . . . . 131 11.6 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 12.HATEOAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 12.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 12.2 Content Negotiation . . . . . . . . . . . . . . . . . . . . . . . . . . 142 12.3 Hypermedia Controls . . . . . . . . . . . . . . . . . . . . . . . . . . 146 13.API Versioning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 13.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 13.2 Different Approaches to API Versioning . . . . . . . . . . . . . . . . 156 13.3 Ask Your Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
8쪽
Introduction I’ve been building APIs for a long time now and it is becoming ever more common for server-side developers thanks to the rise of front-end JavaScript frameworks, iPhone applications and API-centric architectures. On one hand you’re just grabbing stuff from a data source and shoving it out as JSON, but surviving changes in business logic, database schema updates, new features or deprecated endpoints, etc gets super difficult. A lot of articles and tutorials talk about REST, with a varying level of accuracy. Some claim that certain things are more RESTful than others, whilst actually having very little to do with REST. The word REST has been so utterly misused for the last 5 or 6 years it actually means nothing anymore, and a large chunk of the API development community has moved to terms like “Hypermedia API” to represent what was intended by the original meaning of REST before it was utterly ruined. This book will not get too hung up on that politics. It will mostly outline the pros and cons of various approaches, only giving you the “one true way” when the other approaches are all patently awful. Like SOAP and XML-RPC in general. They can f@%k off. I found most resources out there to be horribly lacking or specifically aimed at one single framework. Many tutorials and books use apples and pears examples which are not concrete enough, or talk like listing /users and /users/1 are the only endpoints you will ever need. Between 2012 and 2014 I worked for a company called Kapture, where my primary function has been to inherit, rebuild, maintain and further develop a fairly large API with many different endpoints exposing a lot of different use-cases. The API in question was v2 when I joined the company and written in FuelPHP, utilizing a long since deprecated ORM which had been hacked to death by the original developer. Kapture was in the process of rebuilding its iPhone application to implement new functionality, so I used this as an opportunity to delete that mess and build v3 in Laravel 4, leveraging its simple (initially Symfony-based) routing, database migrations, schema, seeding, etc. Now we are doing the same for v4 but no rewrite was required this time, even though we have some different functionality the i
9쪽
Introduction ii v3 repo was forked to a new one for v4 and both are being actively developed and living side-by-side on the same “API” servers. By passing on some best practices and general good advice you can hit the ground running if you are new to API development. On the flip side, by recounting some horror stories (and how they were overcome/avoided/averted) you can hopefully avoid a lot of the pitfalls I either fell into, or nearly fell into, or saw others fall into. This book will discuss the theory of designing and building APIs in any language or framework. This theory will be applied in examples built mostly in PHP, with some Ruby and Python too. The book will not be too code-heavy regardless, as reading code is no fun. By the end of this book, you will have built an API that can create, read, update, delete things, handle searching and do everything else a good Hypermedia API needs to do.
10쪽
Sample Code Throughout this book I will refer to source code which exists on a GitHub repo, which can be downloaded in a few ways. a) You can clone it: 1 $ git clone git@github.com:philsturgeon/build-apis-you-wont-hate.git b) Browse around it: https://github.com/philsturgeon/build-apis-you-wont-hate c) Download it as a .zip file: http://bit.ly/apisyouwonthate-zip This contains a few bits and bobs that will save you from copying and pasting things out of the ebook, which would probably be horrendous. The book will assume you place the contents in ∼/apisyouwonthate, so if you put it somewhere else update the path in the examples. iii
11쪽
1. Useful Database Seeding 1.1 Introduction The first step to creating any sort of application is creating the database. Whether you are using some sort of relational platform, MongoDB, Riak, or whatever, you will need a vague idea of how your data is going to be stored. For relational databases it is very likely you will start off your planning with an entity-relationship diagram and for document based databases such as MongoDB, CouchDB or ElasticSearch you will just let your application magically build a schema, but either way you need to create a plan - even if it is on a napkin. This book will assume a traditional relational database is storing your data but the principles are easily adapted for NoSQL systems too. This chapter assumes you have already got a database designed and built. This chapter skips the “planning a database” section because there are plenty of other books on that already. 1.2 Introduction to Database Seeding With a database schema designed and implemented, the next step is to store some data. Instead of entering your real data, it is far easier to use “dummy data” to test if the schema is appropriate for your API application. This brings the added benefit of letting you ditch the database and try again without worrying about maintaining the data. The process of populating a database is known as “seeding”. This data could be: • test users 1
12쪽
Useful Database Seeding 2 • content entries with a bunch of comments • fake locations available for check-in • fake notifications to display in an iPhone app (one of each type) • credit-card payments at various stages of processing - with some complete, some half done and some super-fraudulent looking ones The process of creating seeding scripts means you can avoid wasting time creating this manually over and over again. Ultimately, the more processes you can automate during the development of your API, the more time you have to consider the intricacies of your applications which need much more consideration. Dummy data is necessary for realistic acceptance testing, getting freelancers/new hires up to speed with useful content, keeping real customer data private to those outside your company, and avoiding the temptation to copy live data over to your development environments. Why is using production data in development bad? Have you ever been writing a script that sends out emails and used some dummy copy while you’re building it? Ever used some cheeky words in that content? Ever accidentally sent that email out to 10,000 real customers email addresses? Ever been fired for losing a company over £200,000? I haven’t, but I know a guy that has. Don’t be that guy. What data should you use? Garbage! Use absolute nonsense for your development database, but nonsense of the correct data type, size, and format. That can be done with a fun little library called Faker¹ by François Zaninotto² which is a wonderful little library that can essentially bullshit for Queen and country. ¹https://github.com/fzaninotto/Faker ²https://twitter.com/francoisz/
13쪽
Useful Database Seeding 3 1.3 Building Seeders Kapture, the company I work for, uses the Laravel framework which has the joys of having Database Seeding³ baked in. This is essentially a tarted up CLI task which almost any modern PHP framework will have (or bloody well should do) so the principles are applicable to all. Break your Database Seeders down into logical groupings. This does not need to be “one seeder-per-table” but it can be. Sometimes your data needs to be built at the same time as other types of data, so for us Users are created in the same “seeder” as their settings, OAuth tokens, and friendship data is made. Putting that into multiple seeders purely to keep things tidy would complicate your seeders and slow things down a lot, so maybe consider combining them. In this chapter I will use, as an example, a check-in application. The application han- dles “users” and tracks their “check-ins” into “merchants” (or “venues”). “Merchants” also provide “campaigns” (or “opportunities”). So, this is a drastically simplified version of our user seeder all in one go, ignoring the Laravel specific structure. If you are using Laravel 4, just shove this in your run() method. Creating a user with Faker and Eloquent ORM 1 $faker = Faker\Factory::create(); 2 3 for ($i = 0; $i < Config::get('seeding.users'); $i++) { 4 5 $user = User::create([ 6 'name' => $faker->name, 7 'email' => $faker->email, 8 'active' => $i === 0 ? true : rand(0, 1), 9 'gender' => rand(0, 1) ? 'male' : 'female', 10 'timezone' => mt_rand(-10, 10), 11 'birthday' => rand(0, 1) ? $faker->dateTimeBetween('-40 years', '-1\ 12 8 years') : null, 13 'location' => rand(0, 1) ? "{$faker->city}, {$faker->state}" : null, 14 'had_feedback_email' => $faker->boolean, 15 'sync_name_bio' => $faker->boolean, ³http://laravel.com/docs/migrations#database-seeding
14쪽
Useful Database Seeding 4 16 'bio' => $faker->sentence(100), 17 'picture_url' => $this->picture_url[rand(0, 19)], 18 ]); 19 } So what do we have here? Let’s go through this section at a time: 1 $faker = Faker\Factory::create(); An instance of Faker, our bullshit artist for-hire. 3 for ($i = 0; $i < Config::get('seeding.users'); $i++) { We are going to want a certain number of users, but you should have a few less on development than you do on testing or staging to save time. 5 $user = User::create([ 6 'name' => $faker->name, 7 'email' => $faker->email, Make a random name and random email. There is no need to define the pool of random data it uses, because IT’S MAGIC! 8 'active' => $i === 0 ? true : rand(0, 1), Ok I lied, our garbage is not 100% random. We want user number 1 to be active for tests later on. 9 'gender' => $faker->randomElement(['male', 'female']), Gender equality is important.
15쪽
Useful Database Seeding 5 10 'timezone' => mt_rand(-10, 10), Our original developer decided that saving timezones as an integer was a clever thing to do. Store Timezones, Not Offsets Did you know that some timezones are not complete hours? Did you know that Nepal is UTC/GMT +05:45? Did you know that Chatham Island (New Zealand) goes from UTC/GMT +12:45 to UTC/GMT +13:45 in their local summer? Did you know that some places add 30 minutes when in daylight savings time? Don’t use integers as timestamps. PHP implements the IANA⁴ timezone database, which is an industry standard. If you store America/New_York or Asia/Khandyga for users, then the offset and daylight savings time will be automatically calculated. 11 'birthday' => rand(0, 1) ? $faker->dateTimeBetween('-40 years', '-1\ 12 8 years') : null, Users of all of our target age demographic. 13 'location' => rand(0, 1) ? "{$faker->city}, {$faker->state}" : null, Give us a city name and a state name. This works fine with foreign countries too which is cool. 14 'had_feedback_email' => $faker->boolean, 15 'sync_name_bio' => $faker->boolean, Some user flags are not as important, so set them to be true or false at random. ⁴http://www.iana.org/time-zones
16쪽
Useful Database Seeding 6 16 'bio' => $faker->sentence(100), Make a sentence with 100 characters in it. 1.4 That is about it You will end up making a lot of these files, and you will want to populate pretty much every table you have with data. You will also want to tell your Database Seeder to wipe all the tables that will be populated. Do this globally right at the start of the process. Do not wipe tables at the top of each seeder, or content in that table from other seeders will be deleted. Example of an overall system in Laravel 4 1 class DatabaseSeeder extends Seeder 2 { 3 public function run() 4 { 5 if (App::environment() === 'production') { 6 exit('I just stopped you getting fired. Love Phil'); 7 } 8 9 Eloquent::unguard(); 10 11 $tables = [ 12 'locations', 13 'merchants', 14 'opps', 15 'opps_locations', 16 'moments', 17 'rewards', 18 'users', 19 'oauth_sessions', 20 'notifications', 21 'favorites', 22 'settings', 23 'friendships', 24 'impressions', 25 ];
17쪽
Useful Database Seeding 7 26 27 foreach ($tables as $table) { 28 DB::table($table)->truncate(); 29 } 30 31 $this->call('MerchantTableSeeder'); 32 $this->call('PlaceTableSeeder'); 33 $this->call('UserTableSeeder'); 34 $this->call('OppTableSeeder'); 35 $this->call('MomentTableSeeder'); 36 } 37 } This wipes everything, then runs other seeder classes to do their thing. Foreign Keys It can be difficult to wipe a database when foreign keys constraints are enforced, so in that scenario your seeder should run DB::statement('SET FOREIGN_KEY_CHECKS=0;'); before the truncation of the tables and DB::statement('SET FOREIGN_KEY_CHECKS=1;'); afterwards to re- enable the checks. 1.5 Secondary Data As I said it is quite likely that you will need to insert data that relates to each other. To do this you work out which data will be primary (like users); in the case of a check-in system probably you will also consider “venues” or “merchants” depending on the nomenclature of your system. For this example I will show how to create “merchants”, then attach “opportunities”, which are essentially “campaigns”.
18쪽
Useful Database Seeding 8 Primary Seeder for the Merchant Table 1 <?php 2 3 class MerchantTableSeeder extends Seeder 4 { 5 /** 6 * Run the database seeds. 7 * 8 * @return void 9 */ 10 public function run() 11 { 12 $faker = Faker\Factory::create(); 13 14 // Create however many merchants 15 for ($i = 0; $i < Config::get('seeding.merchants'); $i++) { 16 Merchant::create([ 17 'name' => $faker->company, 18 'website' => $faker->url, 19 'phone' => $faker->phoneNumber, 20 'description' => $faker->text(200), 21 ]); 22 } 23 } 24 } Primary Seeder for the Opp Table 1 <?php 2 3 use Carbon\Carbon; 4 use Kapture\CategoryFinder; 5 6 class OppTableSeeder extends Seeder 7 { 8 /** 9 * Build it up 10 * 11 * @param Place 12 */
앞 18페이지만 표시됩니다. 전체를 보려면 자료를 다운로드하세요.
댓글 (0)
댓글이 없습니다. 첫 댓글을 남겨보세요!