apis do jeito certo

Post on 18-Jan-2017

873 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

APIS DO JEITO CERTORAVAN SCAFI

• Ravan Scafi• Web Developer @

Leroy Merlin• Co-organizador do

Meetup do Laravel-SP• twitter.com/ravanscafi

SOBRE MIM

SOBRE A APRESENTAÇÃO

• Motivação• Boas práticas na construção de APIs• Lições Aprendidas• Laravel / Lumen ao resgate!

APIS REST EM 1 MINUTO

• Recursos como substantivos• Verbos HTTP especificam a ação

$ curl -XGET localhost/api/users/123

MOTIVAÇÃO

• Por que escrever uma API?• O que implica ter uma API?

# DOCUMENTAÇÃO

“Uma API é apenas tão boa quanto sua documentação”

apiary.io

SWAGGER / OPEN API

• Uma forma de documentar sua API• Único arquivo swagger.json• Consumível por Humanos e por Máquinas• Swagger-UI: ❤️ ❤️ ❤️ ❤️

DOCUMENTAÇÃO: DICAS

• Mantenha a documentação próxima ao código• Regras de bom código também valem para

documentação• https://github.com/zircote/swagger-php

<?php

class UserController{ /** * @SWG\Post(path="/user", * tags={"user"}, * summary="Create user", * description="This can only be done by the logged in user.", * operationId="createUser", * produces={"application/xml", "application/json"}, * @SWG\Parameter( * in="body", * name="body", * description="Created user object", * required=false, * @SWG\Schema(ref="#/definitions/User") * ), * @SWG\Response(response="default", description="successful operation") * ) */ public function createUser() { } }

Swagger-PHP: Exemplo

<?php

$basePath = __DIR__ . '/..';require_once $basePath . '/vendor/autoload.php';

// grabbing info from .env file$beforeEnv = $_ENV;(new Dotenv\Dotenv($basePath))->load();$envVars = array_diff($_ENV, $beforeEnv);

// grabbing info from composer.json file$composerFile = json_decode(file_get_contents($basePath . '/composer.json'));$composerInfo = [ 'COMPOSER_NAME' => $composerFile->name ?? null, 'COMPOSER_DESCRIPTION' => $composerFile->description ?? null, 'COMPOSER_VERSION' => $composerFile->version ?? '0.0.0', 'COMPOSER_LICENSE' => $composerFile->license ?? null,];

// defining grabbed info as constants$desiredConstants = array_filter(array_merge($envVars, $composerInfo));array_map('define', array_keys($desiredConstants), $desiredConstants);

bootstrap/swagger.php

{ ... "autoload": { "psr-4": { "App\\": "app/" } }, "scripts": { "api-docs": "swagger app -o public/docs.json -b bootstrap/swagger.php", }}

composer.json

$ composer api-docs

# DESIGN

VERSIONAMENTO

• Internamente: Semantic Versioning

• Publicamente: só mude versões cheias (v1, v2)

• Evite Breaking Changes (BC) o máximo que conseguir

O QUE CONFIGURA UMA BC?

• Remover campos: SIM• Renomear campos: SIM• Adicionar campos: NÃO• Adicionar recursos / endpoints: NÃO

PROTEJA-SE COM MUTATORS

• Apresentação e transformação dos dados• Uma “barreira” entre os recursos e a API• Typecasting, relacionamentos

<?phpuse Acme\Model\Book;use League\Fractal;

$books = Book::all();

$resource = new Fractal\Resource\Collection($books, function(Book $book) { return [ 'id' => (int) $book->id, 'title' => $book->title, 'year' => $book->yr, 'author' => [ 'name' => $book->author_name, 'email' => $book->author_email, ], 'links' => [ [ 'rel' => 'self', 'uri' => '/books/'.$book->id, ] ] ];});

Transformer: Exemplo

NEGOCIAÇÃO DE CONTEÚDO

• JSON é JavaScript, portanto use camelCase• /api/user/123.json, /api/user/123.csv• Recursos embedados: trazer dinamicamente

ou não• Metadados: URI, página atual, count total• Verbo HTTP: override com _method

RESPOSTAS

• Padronize campos como: datas, floats, moedas

• Padronize códigos HTTP de resposta• Padronize paginação, metadados, etc.

RESPOSTAS

• Faça um “wrap” da resposta em uma chave “data”

• Mostre o erro, ao invés de um código de erro• Nunca exponha exceptions para os usuários• Não reinvente a roda, seja coerente com

padrões

AUTENTICAÇÃO

• Baseados em Sessão: NÃO• APIs devem ser sem estados (stateless)• Implementação em mobile é custosa

• Baseados em Token: SIM• OAuth? OAuth2? JWT? Http-Basic?

OAUTH VS JWT

• Não são “concorrentes” diretos• É até mesmo possível utilizar os dois em

conjunto• OAuth: concebido para autorização (pseudo

autenticação)• 3-legged vs 2-legged

• JWT: concebido para claims / autenticação

Como se parece um token JWT

SEGURANÇA

• Evite expor IDs sequenciais• Limite as requisições (Throttling), porém

configurável por conta• Não utilize sessões ou logins por senhas (exceto

em aplicativos próprios)• API somente em HTTPS• Verifique sempre vulnerabilidades com OAuth /

JWT

# LARAVEL VS LUMEN

LARAVEL VS LUMEN

• Já tenho um app em Laravel: Laravel!• Já tenho um app em Lumen: Lumen!• Vou começar: Lumen!• Use e abuse de Middlewares• Agrupe rotas por versão

<?php

$app->group(['prefix' => 'api'], function () use ($app) { $app->group(['prefix' => 'v1'], function () use ($app) { $app->get('users', function () {}); $app->get('products', function () {}); });

$app->group(['prefix' => 'v2'], function () use ($app) { $app->get('users', function () {}); $app->get('products', function () {}); });});

Grupos de Rotas por Versão

PACOTES ÚTEIS

• Dingo/API• Zircote/Swagger-PHP• TymonDesigns/JWT-Auth• League - OAuth2 Server• League - Fractal• Ramsey - UUID

DINGO/API

• Negociação de Conteúdo• Múltiplos Adaptadores de Autenticação• Versionamento da API• Limitação de Requisições

DINGO/API

• Transformers and Formatters de Respostas• Handling de Exceptions e Erros• Requests Internos• Documentação Blueprint

LEMBREM-SE: EMPATIA É A CHAVE

OBRIGADO!

@ravanscafi

ESTAMOS CONTRATANDO :)

rscafi@leroymerlin.com.br

top related