GraphQLでフロントエンドの複雑性とたたかう

0f0b6159cf553aa5c931dedfd460ef70?s=47 narirou
September 03, 2019

 GraphQLでフロントエンドの複雑性とたたかう

0f0b6159cf553aa5c931dedfd460ef70?s=128

narirou

September 03, 2019
Tweet

Transcript

  1. 2019-09-03 Bonfire Frontend #4 Yahoo! JAPAN / GYAO! GraphQLͰϑϩϯτΤϯυͷෳࡶੑͱ͔ͨͨ͏

  2. Masanari Hamada @narirou ͓࢓ࣄ 2015೥৽ଔೖࣾ GYAO! WEB ͷΞʔΩςΫνϟ࡮৽ GYAO ಈըϓϨʔϠʔ࡮৽

    GYAO iOS SwiftԽ/ϦϑΝΫλϦϯά౳ σβΠϯγεςϜ / JAMStack / WEBඪ४ ʹڵຯ͕͋Γ·͢ ࣥච WEB+DB PRESS ͷϑϩϯτΤϯυ࿈ࡌهࣄͱͯ͠Storybookɺ HeadlessCMSͷهࣄΛࣥච
  3. GraphQL͕஀ੜͨ͠എܠͱ՝୊ ෳࡶੑΛղܾ͢ΔΫϥΠΞϯταΠυͷ࢓૊Έ APENDIX

  4. GraphQL GraphQL͸FacebookʹΑΓ։ൃ͞ΕͨΦʔϓϯιʔεͷΫ ΤϦݴޠ 2012೥ Facebookࣾ಺Ͱ։ൃ 2015೥ ΦʔϓϯιʔεԽ ݱࡏ͸ Githubͷ graphql/graphql-spec

    ʹͯ൚༻తͳΫΤϦ ݴޠ࢓༷ͱͯ͠ࡦఆ͞Ε͍ͯΔ
  5. GraphQL͕ͳͥඞཁ͔

  6. 1. ΫϥΠΞϯταΠυͷෳࡶͳཁٻ 2000೥ RESTful API (Roy Fielding) REST͸൚༻తʹ࡞ΒΕͨΤϯυϙΠϯτʹରͯ͠൚༻Խ͞ΕͨσʔλΛऔಘ͢Δ

  7. ୯७ͳΫΤϦͰ΋ෳ਺ճͷ௚ྻͳRESTApiϦΫΤετ͕ඞཁʹͳ͋Δ৔߹͕͋Δ औಘͨ͠σʔλʹ͸ෆཁͳ߲໨͕ଘࡏ͢Δ

  8. දࣔͷཁ݅͸ຊ࣭తʹΫϥΠΞϯτͷը໘࢓༷ʹ ґଘ͢Δ (ͦͯͦ͠Ε͸ɺසൟʹมߋ͞ΕΔ) ❖ ը໘ͷσβΠϯ͸มΘΔ.. ❖ ද͍߲ࣔͨ͠໨΋มΘΔ.. ❖ ಛघͳ৚݅ͰABςετ͍ͨ͠.. ❖

    σόΠεʹΑͬͯෳ਺ͷσʔλ͕ඞཁ.. RESTͰͲ͏΍ͬͯରԠ͢Δʁ
  9. -> RESTfulͷݶք… ͢΂ͯͷΫϥΠΞϯτଆͷཁ݅Λຬͨ͢ʹ͸ RESTfulͰ͸࠷దԽ͕ࠔ೉ ֦ுੑ / ύϑΥʔϚϯε / ։ൃ଎౓ ͕٘ਜ਼ʹͳΔ

  10. query(userId: ID!) { history(userId: $userId) { videos { id title

    } } } ྫ͑͹ө૾ͷࢹௌཤྺͷྫ͸ɺGraphQLͰ͋Ε͹ɺҎԼͷΫΤϦͰ໨ తͷσʔλ͕औಘͰ͖Δ
  11. 2. αʔϏεؒͷεΩʔϚ࿈ܞ ❖ ϚΠΫϩαʔϏεΞʔΩςΫνϟͰ͸ɺαʔϏεؒͷ࿈ܞ͕ѹ౗ తʹ૿Ճ͢Δ ❖ ֤αʔϏεͰඞཁͳσʔλ߲໨͸໌ࣔతʹ͠ɺαʔϏεؒͷ࿈ ܞΛ҆શʹߦ͏ඞཁੑ͕͋Δ

  12. ʮίϯϙʔωϯτʯ͝ͱʹඞཁͳσʔλ͸ҟͳΓɺ໌ࣔతʹ͢Δඞཁ͕͋Δ ֤ίϯϙʔωϯτͰඞཁͳσʔλΛએݴతʹهड़Ͱ͖Δ࢓૊Έ͕ͳ͍ͱε έʔϧ͠ͳ͍ ΫϥΠΞϯταΠυͰ΋ಉ༷ͷ໰୊

  13. query Heros { heros(first: 10) { name } } {

    "heros": [ { "name": "R2-D2" } ] } export function Users() { const { data } = useQuery(HeroNameQuery); return data.heros.map(hero => <User name={hero.name} />); }
  14. GraphQLΛ஌Δ

  15. type Post { id: String! title: String! publishedAt: DateTime! likes:

    Int! @default(value: 0) blog: Blog @relation(name: "Posts") } type Blog { id: String! name: String! description: String posts: [Post!]! @relation(name: "Posts") } GraphQL SDL (schema definition language) εΩʔϚઃܭͷهड़ݴޠΛSDLͱͯ͠൚༻తʹࡦఆ ܕͷఆٛͱͯ͠໌ࣔతͰཧղ͠΍͍͢
  16. Interface interface Post { id: ID! title: String! text: String!

    } ❖ ඪ४Ͱ࣋ͭܕ(Scalar Type)ʹ͸ɺString, Int ͳͲ ❖ ! ͸ non-nullable Λද͢
  17. Type ❖ implementsͰఆٛͨ͠interfaceܕΛදݱͰ͖Δ type BlogPost implements Post { id: ID!

    title: String! text: String! }
  18. Query ❖ $id ෦෼͕Ҿ਺ɺ࣮ࡍʹΫΤϦ࣌ʹม਺ͱͯ͠ೖྗ͢Δ query BrogPostQuery($id: ID!) { post(id: $id)

    { title } } { "data": { "post": { "title": "Learning GraphQL" } } }
  19. Fragment ❖ ܁Γฦ͕͠ଟ͘ग़ݱ͢ΔͳͲΫΤϦશମ͕ංେԽ͢Δ ❖ ࠶ར༻ՄೳͳΫΤϦͷҰ෦Λ੾Γग़ͯ͠දݱͰ͖Δ { leftComparison: hero(episode: EMPIRE) {

    ...comparisonFields } rightComparison: hero(episode: JEDI) { ...comparisonFields } } fragment comparisonFields on Character { name appearsIn friends { name } }
  20. Mutations ❖ σʔλͷॻ͖ࠐΈ͸mutationΫΤϦΛൃߦͯ͠ॲཧ͢Δ mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { createReview(episode:

    $ep, review: $review) { stars commentary } }
  21. ͜ΕҎ֎ʹ΋ɺ༷ʑͳఆ͕ٛ͋ΔͷͰҎԼΛࢀর https://graphql.org/learn/schema https://graphql.org/learn/queries Directives @skip @include ͳͲಛఆ৚݅ͰॲཧΛߦ͏͜ͱΛɺΫΤϦʹهड़Ͱ͖Δ UnionTypes ෳ਺ͷtypeΛ࣋ͬͨtypeΛఆٛ͢Δ

  22. APIυΩϡϝϯτͱPlayground

  23. ΠϯτϩεϖΫγϣϯ GraphQLαʔόʔ͸ɺࣗ਎ͷεΩʔϚఆٛΛެ։͢Δ࢓૊ΈΛඋ͑Δ ࣮ࡍʹΫΤϦΛͨͲͬͯΈΔͱɺ಺෦Ͱࢀর͍ͯ͠Δܕ৘ใ͕Θ͔Δ { __schema { types { name }

    } } { "data": { "__schema": { "types": [ { "name": "ID" } ] } } }
  24. GUI (GraphiQL, GraphQL Playground) ❖ ӈଆͷϖΠϯ͔Βɺܕ৘ใΛӾཡ ❖ ࠨଆͷϖΠϯ͔Βɺ࣮ࡍʹΫΤϦΛ౤͛Δ͜ͱ͕Ͱ͖Δ ఆٛʹίϝϯτΛهड़͓ͯ͘͜͠ͱͰɺ։ൃ࣌ʹ͸PlaygroundΛ υΩϡϝϯςʔγϣϯͷ୅ସͱͯ͠ར༻Ͱ͖Δ

  25. ΫϥΠΞϯτͱͷ࿈ܞ Apollo Client, Relay…

  26. Apollo Client GraphQLͷ௨৴ॲཧΛΫϥΠΞϯτଆͰѻ͏ͨΊͷϥΠϒϥϦɻ ར༻ଆ͸ɺએݴతͳΫΤϦͷهड़Ͱجຊతͳ௨৴Λར༻Ͱ͖Δɻ ❖ঢ়ଶͷ؅ཧ ❖Ωϟογϡ؅ཧͷந৅Խ ❖௨৴ؒͷॲཧͷந৅Խ

  27. Apollo Client Λར༻͢Δ import ApolloClient from 'apollo-boost'; import gql from

    'graphql-tag'; const client = new ApolloClient({ uri: 'https://48p1r2roz4.sse.codesandbox.io', }); client .query({ query: gql` { rates(currency: "USD") { currency } } ` }) .then(result => console.log(result));
  28. None
  29. ௨৴ؒͷॲཧͷந৅Խ (Apollo Link) ApolloLink͸ΫϥΠΞϯτଆͷ௨৴ͷந৅Խ૚ʹ౰ͨΔ෦෼Ͱ͋ Γɺ؆қతͳϓϩΩγͱͯ͠ػೳ͢Δ expressͷϛυϧ΢ΣΞͷΑ͏ͳߏ੒ʹͳ͍ͬͯΔ ApolloClient͸ɺ͜ͷ࢓૊ΈͰϦΫΤετͷ࠷దԽΛߦ͏͜ͱͰɺൃ ੜ͢Δ༷ʑͳෳࡶੑΛճආ͍ͯ͠Δ

  30. ௨৴ؒͷॲཧͷந৅Խ (Apollo Link) ❖ apollo-link-http (جຊతͳ௨৴) ❖ apollo-link-batch-http (όονॲཧ) ❖

    apollo-link-rest (REST APIΛGraphQLͷΑ͏ʹѻ͏ந৅Խ૚) ❖ …
  31. ΫϥΠΞϯτͷঢ়ଶΛ؅ཧ͢Δ apollo-link-state ͸ɺϩʔΧϧͷঢ়ଶΛ؅ཧ͢ΔͨΊͷApolloLink ❖ ͜ͷߏ੒͸Reduxʹࣅ͍ͯΔ ❖ ApolloͰ؅ཧ͢Δσʔλ (ApolloCache) ΛΫϥΠΞϯτ ଆͷ།ҰͷσʔλετΞͷΑ

    ͏ʹ΋ѻ͏͜ͱ͕Ͱ͖Δ
  32. import React from "react"; import { useQuery } from "@apollo/react-hooks";

    import gql from "graphql-tag"; import Link from "./Link"; const GET_VISIBILITY_FILTER = gql` { visibilityFilter @client } `; function FilterLink({ filter, children }) { const { data, client } = useQuery(GET_VISIBILITY_FILTER); return ( <Link onClick={() => client.writeData({ data: { visibilityFilter: filter } }) active={data.visibilityFilter === filter} > {children} </Link> ) }
  33. ίʔυͷࣗಈੜ੒ͱ࠷దԽ

  34. GraphQL SDL͸൚༻తʹࡦఆ͞Ε͓ͯΓɺ͜ͷఆ͔ٛΒ༷ʑͳݴޠ Ͱ׆༻Ͱ͖ΔΑ͏ม׵Ͱ͖Δ

  35. TypeScriptͱͷ࿈ܞ ❖ @graphql-codegen/typescript ❖ @graphql-codegen/typescript-operations ❖ @graphql-codegen/typescript-react-apollo graphql-codegenɺapollo-tooling ʹΑͬͯɺΫΤϦ͔ΒTypeScript ༻ͷίʔυΛग़ྗ͢Δ

  36. { posts { title text labels { name } thumbnail

    { width height url } } } export type BlogPostsQuery = { posts: Maybe< Array< Pick< BlogPost, | "title" | "text" > & { labels: Maybe<Array<Pick< thumbnail: Pick<Image, "u } > >; }; ྫ͑͹ graphql-codegen ͰࠨͷΑ͏ͳΫΤϦΛม׵͢Δͱɺ ӈهͷΑ͏ͳtypes͕ੜ੒͞ΕΔ
  37. Reactͱͷ࿈ܞ @apollo/react-hooksͷΧελϜϑοΫuseQueryΛར༻ͯ͠ɺ GraphQLͷϦΫΤετΛߦ͏

  38. const GreetingQuery = gql` query getGreeting($language: String!) { greeting(language: $language)

    { message } } `; function Hello() { const { loading, error, data } = useQuery(GreetingQuery, { variables: { language: 'english' }, }); if (loading) return <p>Loading ...</p>; return <h1>Hello {data.greeting.message}!</h1>; }
  39. export function useGreetingQuery( baseOptions?: ReactApolloHooks.QueryHookOptions< GetGreetingQuery, GetGreetingQueryVariables > ) {

    return ReactApolloHooks.useQuery< GetGreetingQuery, GetGreetingQueryVariables >(StoreTitlesDocument, baseOptions); } export type GetGreetingQueryHookResult = ReturnType<typeof useGetGreetingQuery ΫΤϦ(CustomHook)ͷࣗಈੜ੒ ͞Βʹɺ@apollo/react-hooks ͱͷ࿈ܞ͢ΔͱɺΫΤϦ͔Β ReactHooks༻ͷΧελϜϑοΫΛࣗಈੜ੒͠ɺܕ҆શͳঢ়ଶͰΫΤ ϦΛར༻Ͱ͖Δɻ
  40. { "scripts": { "schema": “node ./script/graphql-codegen.js" } } ͜ΕΒͷੜ੒͸ίϚϯυ͔Β༰қʹੜ੒͢ΔΑ͏ʹ͓ͯ͘͠ͱGood

  41. ՝୊

  42. query Heros { messages(first: 99999999) { title messages(first: 99999999) {

    title messages(first: 99999999) { title } } } } 1. ύϑΥʔϚϯεΫϦςΟΧϧͳΫΤϦ͕ൃߦ͞ΕΔ ެ։͢ΔGraphQLαʔόʔ͸ɺηΩϡϦςΟΛҙࣝ͢Δඞཁ͕͋Δ ྫ͑͹ɺҎԼͷΑ͏ͳΫΤϦ͸ڐ༰ͨ͘͠ͳ͍…
  43. ❖ αΠζΛ੍ݶ͢Δ ❖ ωετͷ੍ݶ͢Δ graphql-depth-limit ͰωετΛ੍ݶ͢Δ ❖ ෳࡶ౓Λܭࢉ੍͠ݶ͢Δ ❖ ΫΤϦΛϗϫΠτϦετొ࿥͢Δ

    (Persisted Queries) ΫΤϦΛࣄલʹhashԽ͓͖ͯ͠ɺ௨৴࣌΋hash஋Λ༻͍ͯߦ͏ ڐՄ͞ΕͨhashͷΫΤϦͷΈڐ༰ͯ͠ϨεϙϯεΛฦ͢ 1. ύϑΥʔϚϯεΫϦςΟΧϧͳΫΤϦ͕ൃߦ͞ΕΔ खॱ͕ෳࡶͳͨΊɺΑΓΑ͍ղܾࡦ͕΄͍͠ͱ͜Ζ..
  44. 2. දࣔཁૉͰར༻͢Δܕʹมܗ͠ʹ͍͘ ෳࡶͳΫΤϦʹͳΔͱɺදࣔ༻ͷܕͱͯ͠ѻ͏ͨΊͷม׵͕ඞཁ ࣗಈੜ੒͞Εͨڊେͳܕ৘ใ͔Βͷม׵ͱͳΔͷͰɺܕΛҡ࣋ͯ͠ ม׵͢Δ͜ͱ͕೉͍͠ ts-toolbelt ͳͲΛར༻ͯ͠ɺܕͷҰ෦Λมߋ͢Δ͜ͱͰରԠ͍ͯ͠Δ

  45. ·ͱΊ

  46. ❖ GraphQL͸ΫϥΠΞϯτͷෳࡶͳཁ݅ʹਝ଎ʹରԠ͢ΔͨΊͷɺ 1ͭͷղܾखஈ ίʔυͷࣗಈੜ੒ ωοτϫʔΫ૚ͷࣗಈ࠷దԽ ❖ औಘ͢ΔσʔλΛએݴతʹهड़͠ɺܕ҆શʹ։ൃͰ͖ΔੈքΛ໨ ࢦ͍ͯ͠Δ ❖ ෛՙతͳηΩϡϦςΟͷ՝୊΍ɺN+1໰୊౳ɺGraphQLಛ༗ͷෳ

    ࡶੑ͕͋Γɺ͜Ε͔Βͷ՝୊Ͱ͋Δ ·ͱΊ
  47. ·ͱΊ ΫϥΠΞϯταΠυ͸ɺෳࡶͳ࢓༷มߋɾϢʔβʔૢ࡞ʹΑΔมԽͳͲɺΞϓ ϦέʔγϣϯͷதͰ΋ͬͱ΋ෳࡶͳ෭࡞༻͕ൃੜ͢Δ৔ॴͰ΋͋Γ·͢ɻ GraphQL, Falcor, gRPC… REST͕ൃ໌͞Ε͔ͯΒ਺10೥ɺ՝୊Λײ͍ͯ͡Δ֤͔ࣾΒAPIͷύϥμΠϜɾ γϑτΛ༧ײͤ͞Δٕज़͕ग़࢝Ί͍ͯ·͕͢ɺGraphQL͸ͦͷைྲྀͷҰ୺Λ୲ ͏ٕज़ͷ1ͭͰ͢ɻ ͜͏ͨ͠ෳࡶੑͷ໰୊ͱɺͦͷղܾҊͱͯ͠ߟ͑ΒΕ͍ͯΔഎܠΛ஌ͬͯɺΑ

    ΓΑ͍ٕज़Ͱ͜Ε͔ΒͷWEB։ൃΛ৐Γӽ͍͖͑ͯ·͠ΐ͏ɻ
  48. ΑΓਂ͘஌Δʹ͸ (ࢀߟจݙ) ❖ GraphQL, ApolloͷެࣜυΩϡϝϯτ ❖ ΫΤϦͷجૅΛ஌Δ: [Queries and Mutations](https://graphql.org/learn/queries/)

    ❖ എܠΛ஌Δ: [Github Blog | The GitHub GraphQL API](https://githubengineering.com/the- github-graphql-api/) ❖ ApolloLinkΛ஌Δ: [Apollo Link Overview](https://www.apollographql.com/docs/link/ overview/) ❖ ϕετϓϥΫςΟεΛ஌Δ: [GraphQL Best Practice](https://graphql.org/learn/best-practices/) ❖ ੍ݶΛ͔͚Δ: [HOW TO SECURE A GRAPHQL API (THE COMPLETE VULNERABILITY CHECKLIST)](https://leapgraph.com/graphql-api-security) ❖ ੍ݶΛ͔͚Δ: [GraphQLͱPsersisted Query by @Quramy](https://qiita.com/Quramy/items/ b3943a0c27f3ade2c57d)