Ton API plus rapide qu'Usain Bolt
👆Ton API après ce talk
Qu'est-ce qu'une
API ?
Application Programming Interface
ensemble normalisé de classes, de méthodes, de fonctions et de constantes qui sert de façade par laquelle un logiciel offre des services à d'autres logiciels
API c'est aussi le sigle de allocation parent isolé
Les avantages d'une API
Spécialisée dans un domaine et sur un use case particulier
Pas de couplage avec le code source
Détachement de la consommation de la donnée
Ouverture de fonctionnalités à des tiers
Les différents types d'API
-
REST
-
RPC
-
GRAPHQL
Remote Procedure Call
POST /sayHello HTTP/1.1
HOST: api.example.com
Content-Type: application/json
{"name": "Racey McRacerson"}
/* Signature */
function sayHello(name) {
// ...
}
/* Usage */
sayHello("Racey McRacerson");
-
XML-RPC
-
JSON-RPC
-
Simple Object Access Protocol (SOAP)
Spécifications
REpresentational State Transfer
GET /myresource HTTP/1.1
HOST: api.example.com
Content-Type: application/json
[]
- JSON
- XML
Formats
-
JSON-API
-
OData
-
OpenAPI
Spécifications
Verbes HTTP
ressource == route
QraphQL
RPC-like
Et si on faisait notre API en PHP
Partons d'une application existante
https://github.com/Darkweak/UsainBoltAPI
L'authentification
composer req jwt-auth
jwt_passphrase=${JWT_PASSPHRASE:-$(grep ''^JWT_PASSPHRASE='' .env | cut -f 2 -d ''='')}
echo "$jwt_passphrase" | openssl genpkey -out config/jwt/private.pem -pass stdin \
-aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096
echo "$jwt_passphrase" | openssl pkey -in config/jwt/private.pem -passin stdin \
-out config/jwt/public.pem -pubout
setfacl -R -m u:www-data:rX -m u:"$(whoami)":rwX config/jwt
setfacl -dR -m u:www-data:rX -m u:"$(whoami)":rwX config/jwt
authentication_token:
path: /authentication_token
methods: ['POST']
# ...
firewalls:
# ...
main:
anonymous: true
lazy: true
provider: app_user_provider
json_login:
check_path: /authentication_token
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
access_control:
# ...
- { path: ^/docs, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/authentication_token, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: IS_AUTHENTICATED_FULLY }
Mise en place de l'API
composer req api
On expose nos endpoints
// ...
use ApiPlatform\Core\Annotation\ApiResource;
// ...
/**
* @ApiResource()
* ...
*/
class MyClass
Et la sécurité ?
* @ApiResource(
* collectionOperations={
* "post"={"security"="is_granted('IS_AUTHENTICATED_FULLY')"}
* },
* itemOperations={
* "put"={"security"="is_granted('ROLE_ADMIN') or user == object.author"}
* }
* )
Sécurité
> Par resource
> Par endpoint
> Par rôles
Exemple
| Nom | Méthode | Operation | Sécurité |
|:------------------------:|:-------:|:----------:|:--------------------------------------------------------:|
| La liste | GET | collection | `-` |
| La création | POST | collection | L'utilisateur doit être authentifié |
| L'objet simple | GET | item | `-` |
| La mise à jour | PUT | item | L'utilisateur est admin ou est le créateur de la recette |
| La mise à jour partielle | PATCH | item | L'utilisateur est admin ou est le créateur de la recette |
| La suppression | DELETE | item | L'utilisateur est admin ou est le créateur de la recette |
* @ApiResource(
* collectionOperations={
* "get",
* "post"={"security"="is_granted('IS_AUTHENTICATED_FULLY')"}
* },
* itemOperations={
* "get",
* "patch"={"security"="is_granted('ROLE_ADMIN') or user == object.author"},
* "put"={"security"="is_granted('ROLE_ADMIN') or user == object.author"},
* "delete"={"security"="is_granted('ROLE_ADMIN') or user == object.author"}
* }
* )
* ...
* @ApiResource(
* security="is_granted('ROLE_ADMIN')",
* collectionOperations={
* "get"={"security"="is_granted('IS_AUTHENTICATED_ANONYMOUSLY')"},
* "post",
* },
* itemOperations={
* "get"={
* "controller"=NotFoundAction::class,
* "read"=false,
* "output"=false,
* },
* "patch",
* "put"
* }
* )
* ...
| Nom | Méthode | Operation | Sécurité |
|:------------------------:|:-------:|:----------:|:-----------------------:|
| La liste | GET | collection | `-` |
| La création | POST | collection | L'utilisateur est admin |
| La mise à jour | PUT | item | L'utilisateur est admin |
| La mise à jour partielle | PATCH | item | L'utilisateur est admin |
| La suppression | DELETE | item | L'utilisateur est admin |
Améliorer les perfs
Parce que PHP c'est leeeeeeent !!!
Cache
github.com/Darkweak/Souin
default_cache:
port:
web: 80
tls: 443
ttl: 10
reverse_proxy_url: 'http://reverse-proxy'
default_cache:
headers:
- Authorization
providers:
- all
redis:
url: 'redis:6379'
regex:
exclude: 'ARegexHere'
ssl_providers:
- traefik
urls:
'https:\/\/domain.com\/first-.+':
ttl: 1000
'https:\/\/domain.com\/second-route':
ttl: 10
headers:
- Authorization
'https?:\/\/mysubdomain\.domain\.com':
ttl: 50
headers:
- Authorization
- 'Content-Type'
Configuration
RESULTS
================================================================================
---- Global Information --------------------------------------------------------
> request count 101000 (OK=101000 KO=0 )
> min response time 0 (OK=0 KO=- )
> max response time 56 (OK=56 KO=- )
> mean response time 8 (OK=8 KO=- )
> std deviation 4 (OK=4 KO=- )
> response time 50th percentile 8 (OK=8 KO=- )
> response time 75th percentile 10 (OK=10 KO=- )
> response time 95th percentile 15 (OK=15 KO=- )
> response time 99th percentile 21 (OK=21 KO=- )
> mean requests/sec 3884.615 (OK=3884.615 KO=- )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms 101000 (100%)
> 800 ms < t < 1200 ms 0 ( 0%)
> t > 1200 ms 0 ( 0%)
> failed 0 ( 0%)
================================================================================
Les problèmes avec REST
Under-fetching
Problème N+1
Over-fetching
Configuration
3 variables d'environnement à passer
CERT_FILE=/certs/localhost.crt KEY_FILE=/certs/localhost.key UPSTREAM=http://liendevotreapi |
Vous utilisez websocket pour la synchro avec votre API ?
composer require mercure
Ça doit être super compliqué à mettre en place non ?
* @ApiResource(
* ...
* mercure=true
* )
* ...
Configuration
6 variables d'environnement à passer au serveur
JWT_KEY=superkey
DEMO=1
ALLOW_ANONYMOUS=1
CORS_ALLOWED_ORIGINS=*
PUBLISH_ALLOWED_ORIGINS=http://localhost
ADDR=http://localhost:4000
Configuration
3 variables d'environnement à passer à l'api
MERCURE_PUBLISH_URL=http://mercure-url-innner
MERCURE_SUBSCRIBE_URL=https://mercure.domain.com
MERCURE_JWT_SECRET=superkey
Et on en fait quoi après ?
const mercureHub = 'https://mercure.domain.com/.well-known/mercure'
const eventSource = new EventSource(
`${mercureHub}?topic=${encodeURIComponent('http://example.com/recipes/1')}`
);
eventSource.onmessage = event => {
console.log(JSON.parse(event.data));
}
Promis c'est la seule fois que l'on fera du JS
Reste à faire
Le client
Tout
Sur l'API
Rien
Bon courage
Merci de votre attention
Ton API plus rapide qu'Usain Bolt(sans trop d'effort)
By darkweak
Ton API plus rapide qu'Usain Bolt(sans trop d'effort)
- 595