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

Write and use GraphQL middleware

Write and use GraphQL middleware

Originally posted here: https://speakerdeck.com/alexandersaenko/write-and-use-graphql-middleware

Доклад включает в себя:
- Ретроспективу опыта написания GraphQL middleware (прокси-сервера между REST API и мобильным приложением) для iOS-проекта
- Анализ преимуществ и недостатков технологии
- Lessons learned использования GraphQL в production

This talk was made for CocoaHeads Kyiv #14 which took place Oct 6 2018.

CocoaHeads Ukraine

October 06, 2018
Tweet

More Decks by CocoaHeads Ukraine

Other Decks in Programming

Transcript

  1. GRAPHQL ▸ Data query and manipulation language ▸ Open Source

    ▸ Created by Facebook ▸ 10k stars on GitHub ▸ Always* backward-compatible
  2. { "id": 118, "status": 0, "name": "The text you need",

    "due_date": 1498861069, "created_date": 1498256269, "updated_date": 1498741329, "description": "Something you definitely don't need", "is_modified": 1, "modified_by": "Alexander Saenko", "tasks": [ { "id": 38, "name": "The first task", "description": "Something about the task"}, { "id": 41, "name": "The second task", "description": "Something about the second task" } ] }
  3. { "id": 118, "status": 0, "name": "The text you need",

    "due_date": 1498861069, "created_date": 1498256269, "updated_date": 1498741329, "description": "Something you definitely don't need", "is_modified": 1, "modified_by": "Alexander Saenko", "tasks": [ { "id": 38, "name": "The first task", "description": "Something about the task"}, { "id": 41, "name": "The second task", "description": "Something about the second task" } ] }
  4. { "id": 118, "status": 0, "name": "The text you need",

    "due_date": 1498861069, "created_date": 1498256269, "updated_date": 1498741329, "description": "Something you definitely don't need", "is_modified": 1, "modified_by": "Alexander Saenko", "tasks_count": 2 [ { "id": 38, "name": "The first task", "description": "Something about the task"}, { "id": 41, "name": "The second task", "description": "Something about the second task" } ] }
  5. { "id": 118, "status": 0, "name": "The text you need",

    "updated_date": 1498741329, "tasks_count": 2 }
  6. struct TableModel: Codable { let id: Int let status: Bool

    let name: String let updatedDate: Date let tasksCount: Int enum CodingKeys:String, CodingKey { case id case status case name case updatedDate = "updated_date" case tasksCount = "tasks_count" } }
  7. struct TableModel: Codable { let id: Int let status: Bool

    let name: String let updatedDate: Date let tasksCount: Int enum CodingKeys:String, CodingKey { case id case status case name case updatedDate = "updated_date" case tasksCount = "tasks_count" } }
  8. export function TableModel(data) { return { id () { return

    data.id; }, status() { return data.status; }, name() { return data.name; }, updatedDate() { return new Date(data.updated_date * 1000); }, tasksCount() { return data.tasks.length; } } }
  9. { "id": 118, "status": 0, "name": "The text you need",

    "updatedDate": 1498741329, "tasksCount": 2 } struct TableModel: Codable { let id: Int let status: Bool let name: String let updatedDate: Date let tasksCount:Int }
  10. { "id": 118, "status": 0, "is_completed": false, "name": "The text

    you need", "updated_date": 1498741329, "tasks_count": 2 }
  11. type TableModel { id: Int! status: Boolean! @deprecated isCompleted: Boolean!

    name: String! updatedDate: Date! tasksCount: Int! }
  12. export function TableModel(data) { return { id () { return

    data.id; }, status() { return data.is_completed; }, isCompleted() { return data.is_completed; }, name() { return data.name; }, updatedDate() { return new Date(data.updated_date * 1000); }, tasksCount() { return data.tasks_count; } } }
  13. WHEN TO USE ▸ No breaking changes in API ▸

    Client-driven API ▸ Minimum network pressure
  14. export function Patient(data) { return { __typeName: "Patient", id() {

    return data.id; }, code() { return data.id_code; }, name() { return data.first_name + ' ' + data.last_name; }, group() { return data.last_name ? data.last_name.trimLeft().charAt(0).toUpperCase() : data.first_name.trimLeft().charAt(0).toUpperCase(); } } }
  15. time: 0.724 URL: /api/user time: 0.71 URL: /api/vacancies time: 0.772

    URL: /api/user time: 1.116 URL: /api/invitations time: 1.125 URL: /api/counters/1 time: 1.126 URL: /api/friends time: 1.12 URL: /api/counters/1
  16. export default function createLoaders(fetchFn) { return { User: new DataLoader(async

    (keys) => { return Promise.all(keys.map(async () => { const response = await fetchFn(‘get’, `/api/user`); return await response.json(); })); }), Counters: new DataLoader((keys) => { return Promise.all(keys.map(async (id) => { const response = await fetchFn('get', `/api/counters/${id}`; return await response.json(); }); }) }
  17. ...
 type: userType, resolve: async (rootValue) => { const {loaders}

    = rootValue; const user = await loaders.UserProfile.load(‘me’); const user1 = await loaders.UserProfile.load(‘me’); return user; }
 
 ... type: counterType, resolve: async (rootValue) => { const {loaders} = rootValue; const counter = await loaders.Counters.load(1); const counter1 = await loaders.Counters.load(1); return counter1; }
 ...
  18. time: 0.584 URL: /api/user time: 0.669 URL: /api/vacancies time: 0.982

    URL: /api/invitations time: 0.622 URL: /api/counters/1 time: 0.716 URL: /api/friends https://medium.com/@ven_korolev/make-applications-faster-with-a-dataloader-library-from-facebook-dec67d15e415
  19. static let query = """ query { patients { id

    code name group } } """ struct Response: Decodable { let patients: [Patient] struct Patient: Decodable { let id: String let code: String let name: String let group: String } }
  20. static func query(id: String) -> String { return """ query

    { case(id: "\(id)") { id code status created { date } updated patient { name } treatmentPath { name } } } """ }