Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RuhrJS 2016: The dirty little secrets of buildi...

RuhrJS 2016: The dirty little secrets of building large, highly available, scalable HTTP APIs

When you first start building an API for a new product you mostly focus on getting an MVP ready, with the goal of shipping as soon as possible so you can get feedback from customers. If you are lucky enough, your product will be successful and you will have to start worrying about things like authentication, authorization, documentation, validation, rate limiting, geo-redundancy, and no downtime deployments. In this talk I will go over some real life examples of our experience evolving our APIs at Auth0 and some of the tools we use for that.

Damian Schenkelman

July 02, 2016
Tweet

More Decks by Damian Schenkelman

Other Decks in Programming

Transcript

  1. Why? var express = require('express'); var app = express(); app.get('/',

    function (req, res) { res.send('Hello World!'); }); var server = app.listen(3000, function () { var host = server.address().address; var port = server.address().port; console.log('Example app listening at http://%s:%s', host, port); });
  2. Errors for developers { "statusCode": 400, "error": "Bad Request", "message":

    "Payload validation error: 'Expected type boolean but found type string' on property enabled (<code>true</code> if the rule is enabled, <code>false</code> otherwise).", "errorCode": "invalid_body" }
  3. ratify const ZSchemaErrors = require('z-schema-errors'); const errorReporters = ['headers', 'query',

    'path', 'payload'].reduce((current, part) => { current[part] = ZSchemaErrors.init({/*...*/}); return current; }, {}); plugins.push({ register: require('ratify'), options: { errorReporters: errorReporters, /*...*/ } });
  4. Leverage validation "enabled": { "type": "boolean", "description": "<code>true</code> if the

    connection is enabled, <code>false</code> (default) otherwise" }
  5. Route { method: 'GET', path: '/api/v2/clients/{id}', config: { auth: {

    strategy: 'jwt', scope: ['read:clients', 'read:client_keys'] }, } }
  6. limitd #port to listen on port: 9001 #db path db:

    /var/limitd/database #define the bucket types buckets: customers: size: 10 per_second: 10
  7. patova plugins.push({ register: require('patova'), options: { event: 'onPostAuth', // when

    to perform the limit check type: 'tenant', // bucket address: env.LIMITD_SERVER, extractKey: function(request, reply, done){ const key = request.auth.credentials.__tenant; done(null, key); } } });
  8. feature-change const feature_change = require('feature-change'); var options = { expected:

    function(cb){ mongo_search(mongo_opts, cb); }, actual: function(cb){ es_search(es_opts, cb); }, logAction: function(current_result, new_result){ // invoked when there is a difference in the results // (useful for logging) } }; feature_change(options, function(err, result){ // this is the original callback you were using for mongo // err and result always come from mongo_search });
  9. AuthN & AuthZ • https://tools.ietf.org/html/rfc7519 • http://jwt.io/ • https://auth0.com/blog/2014/12/02/using-json- web-tokens-as-api-keys/

    • https://auth0.com/blog/2015/03/10/blacklist-json- web-token-api-keys/ • https://github.com/auth0/hapi-auth-jwt