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

Write and use GraphQL middleware

Write and use GraphQL middleware

Avatar for Alexander Saenko

Alexander Saenko

October 06, 2018
Tweet

More Decks by Alexander Saenko

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 } } } """ }