Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Lessons from integrating 1,500 APIs

Lessons from integrating 1,500 APIs

Over the past 7 years, we have integrated or helped integrate over 1,500 apps with Zapier. In this talk, we'll share what makes an API easy to work with and how we manage to process millions of requests to and from these apps, in a secure way, every day.
Fokke Zandbergen gave this talk at the WebCore meetup in Amsterdam on September 24th, 2019

Avatar for WebCore Meetup

WebCore Meetup

September 24, 2019
Tweet

More Decks by WebCore Meetup

Other Decks in Programming

Transcript

  1. Milestones • • September 2011: Idea • May 2012: Y

    Combinator Graduation • September 2012: Raised $1.3M • May 2014: Profitable • June 2016: At 1M Users, $10M ARR • August 2017: At 100 Zapiens (or Zorps) • January 2018: At 1K Apps, $35M ARR • January 2019: At 1,5K Apps, $50M ARR
  2. Don’t reinvent the wheel • • REST JSON • OAuth

    2.0 (w/refresh) • REST Hooks * • OpenAPI *
  3. Don’t save on docs to be... • • There •

    Complete • Versioned • Public * • Standard (OpenAPI / Swagger)
  4. • They should never expire… • ...unless we have a

    refresh token • (which should never expire) • ...unless the user revokes it. We encrypt and censor them, please do so to. Secret tokens are Sacred •
  5. • Limiting: number | timestamp • Pagination: (number | token)

    & meta • Filtering & Searching • Field Masks: fields GraphQL via the SDK: yarn add apollo-boost cross-fetch Work that body!
  6. • Field: created_at • Direction: DESC Assuming the above, what

    will go wrong here? ?limit=10 &is_published=true Orderrrr!
  7. • Field: id • Composite Fields: obj.object_id = obj.id; obj.id

    = hash([id,updated_at]); What else is new? •
  8. • Please call us. We’ll only call you once. POST

    you.com/subscribe?url=zapier.com/1d2x • Better UX than (static) webhooks (+24%) • Reduces server load & traffic (-98.5%) • Triggers Zaps instantly Required for public apps resthooks.org Get Hooked!
  9. HTTP/1.0 200 OK Content-Type: application/json { "status": "error", "code": 404,

    "message": "Object is not found." } HTTP Status Codes have a purpose
  10. … as do HTTP Methods GET /api/book?title=APIs+For+Dummies HTTP/1.0 201 Created

    Content-Type text/plain Created “APIs For Dummies”
  11. … and Content-Type HTTP/1.0 400 Bad Request Content-Type: application/json Parameter

    'id' is missing. GET /books Accept: application/json HTTP/1.0 401 Unauthorized Content-Type: text/html <html> <title>Login</title> ...
  12. Polling Triggers • Shedding: Skip % of regular polling request

    • Deduplication (simplified pseudo) hash = md5(obj.id) ZADD(zapId, hash) ZSCAN(zapId, hash) • Held Tasks: This… can’t be right… right?
  13. 1. Response always written to S3 for (manual) replay. 2.

    First pass by trigger integration to parse response. After that, same for polling triggers: 3. For each object, write state to ES. 4. Queue first/next step for worker boxes. 5. Worker passes state to step trigger/action. 6. Write updated state and GOTO 4 Webhook Triggers
  14. • Integrations are NodeJS apps. • Generated AWS Lambda function

    for each app version. • Lambda called to run trigger or action. output = createOrder(z, { auth, input }); • Bound to AWS limits for size of the app (50MB), payload (6MB) and time to complete (30 seconds). Running Integration Code
  15. • Each integration sandboxed. • Only access to data mapped

    to the action’s input fields. • No binary data, just public (!) URLs to download data. publicUrl = z.stash(z.request(privateUrl)) • Credentials stored, encrypted, on a separate MS. • Credentials and token-like string censored in logs. Security
  16. Is This You? Python React Scaling for 10x RabbitMQ SQL

    Django k8s empathy Redis default to action GraphQL Next.js Flow Typescript Node