More Related Content More from Akira Otsuka (6) GraphQL
15. ・Schema と Types
GraphQL schema languageを使って
Queries と Mutationの構造を定義
・Queries と Mutation
Queries :データ取得
Mutations:データ変更
GraphQL
16. schema {
query : Query
mutation : Mutation
}
schema
ここでのQueryとMutationは型。
Query と Mutationをさらに定義していく。
18. type Character { … GraphQL Object Type
name: String! … 文字列、Nullはダメ
appearsIn: [Episode]! … Episode の配列 … [] or [a,b]
}
enum Episode { … ENUMも定義できる
NEWHOPE
EMPIRE
JEDI
}
使える型 … Int / Float / String / Boolean / ID / 自分が定義したtype
Object types と fields
19. myField : [String!]
myField: null // ok
myField: [] // ok
myField: [a,b] // ok
myField: [null,a] // ng
myField : [String]!
myField: null // ng
myField: [] // ok
myField: [a,b] // ok
myField: [null,a] // ok
List と Non-Null
「!」の位置で意味が変わるよ
20. type Character {
id : ID!
name : String!
length(unit: LengthUnit = METER) : Float
}
Arguments
引数を持たせることもできる
21. interface Character {
id : ID!
name : String!
}
type Human implements Character {
id : ID!
name : String!
starships : [Starship]
}
type Droid implements Character {
id : ID!
name : String!
primaryFunction : String
}
interfaces
23. union SearchResult = Human | Droid | Starship
Union Types
複数の型をまとめて定義することができるが
個人的にこのレスポンスは情報が混ざっていて嫌い
30. {
hero {
name
}
}
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
Request Response
Queries - Fields
取得するフィールドを明示的に指定するので
フィールドが増えても無駄な通信量が増えない
つまり「name」を「fullname」に仕様変更する時
新フィールドで追加してもクライアントには影響なし!
32. {
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
{
"data": {
"empireHero": {
"name": "Luke Skywalker"
},
"jediHero": {
"name": "R2-D2"
}
}
}
Request Response
Queries - Aliases
episodeがEMPIREのデータは「empireHero」
JEDIのデータは「jediHero」というフィールドで取得
33. {
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
{
"data": {
"leftComparison": {
"name": "Luke Skywalker",
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
"friends": [
{"name": "Han Solo"}, {"name": "Leia Organa"},
{"name": "C-3PO"}, {"name": "R2-D2"}
]
},
"rightComparison": {
"name": "R2-D2",
"appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI"],
"friends": [
{"name": "Luke Skywalker"},
{"name": "Han Solo"},
{"name": "Leia Organa"}
]}}}
Request Response
Queries - Fragments
再利用可能なフィールドセットを定義できる
34. query HeroNameAndFriends($episode: Episode)
{
hero(episode: $episode) {
name
friends {
name
}
}
}
VALIABLES
{
"episode": "JEDI"
}
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
Request Response
Queries - Variables
値を外部から渡す
36. mutation CreateReviewForEpisode($ep: Episode!,
$review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
VARIABLES
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great
movie!"
}
}
}
Request Response
Mutations
・エピソードとレビューを受けて、レビューを作成
・クライアントにはスターと解説を返却する
39. {
hero {
...NameAndAppearances
friends {
...NameAndAppearances
friends {
...NameAndAppearances
}
}
}
}
fragment NameAndAppearances on Character {
name
appearsIn
}
{
"data": {
"hero": {
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Luke Skywalker",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Han Solo",
"appearsIn": [
Request Response
階層数チェック
fragment の無限循環が起きないように循環参照はエラーとする
42. Base Information
{
"data": {
"human": {
"name": "Han Solo",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"starships": [
{
"name": "Millenium Falcon"
},
{
"name": "Imperial shuttle"
}
]
}
}
}
Schema Query Request Response
43. Query: {
human(obj, args, context) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
}
obj : previous object, ルートではあまり使うことはない
args : query の argument
context : ユーザーやDBアクセスのようなコンテキスト情報を保持
javascript sample
Root type
IDを元にDBからHumanを取得
Promiseを返却
44. Human: {
name(obj, args, context) {
return obj.name
}
}
Humanオブジェクトが使える状態になったので
フィールドの値も何を返却するのか定義する。
Trivial Resolvers
45. Human: {
starships(obj, args, context) {
return obj.starshipIDs.map(
id => context.db.loadStarshipByID(id).then(
shipData => new Starship(shipData)
)
)
}
}
List Resolvers
StarshipのID一覧を元にからStarshipを取得
Promiseのリストを返却
47. {
__type(name: "Droid") {
name
fields {
name
type {
name
kind
ofType {
name
kind
}
}
}
}
}
スキーマ情報の確認
{
"data": {
"__type": {
"name": "Droid",
"fields": [
{
"name": "name",
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "String",
"kind": "SCALAR"
} } },
{
"name": "friends",
"type": {
"name": null,
"kind": "LIST",
"ofType": {
"name": "Character",
"kind": "INTERFACE"
} } },
定義したスキーマの有効活用!
49. const schema = buildSchema(`
type Query {
hello: String
world: String
}
`);
const root = {
hello: () => 'Hello!',
world: () => 'World!',
};
const app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000));
sample express
50. curl localhost:4000/graphql
-d '{"query":"{hello}"}'
-H "Content-Type:application/json"
curl localhost:4000/graphql
-d '{hello}'
-H “Content-Type:application/graphql"
sample curl
application/graphql !!
54. Backend For Frontend
WebApp (SPA)
iOS App
Android App
http/s
バックエンド
json
フロントエンド
users
REST
microservice
auth
articles
comments
favorites
GraphQL