├── .eslintignore ├── .gitignore ├── assets ├── codes │ ├── graphql-query.example │ ├── graphql-response.example │ ├── graphql-schema.example │ ├── min-query.example │ ├── cache.example │ ├── fake.example │ ├── relay-simple-schema.example │ └── relay-simple.example ├── ctlin.jpg ├── fake.png ├── logos.png ├── relay.png ├── graphiql.png ├── graphql.png ├── masking.png ├── rnplay.png ├── container.png ├── rnplay-app.png ├── react-native.png ├── relay.svg └── graphql.svg ├── .babelrc ├── dist ├── 044b5116eb37c9646d7af104e2b4a834.jpg ├── 17509a9b39e1232b9a519703901916d2.png ├── 3b2ccdea43ce740aa92b6965c7d72589.png ├── 72299e9b80aef40368b0a3df96de2e46.png ├── 7db7978f9c01dfcde05ac3179ffe8903.png └── d0054443c53264e7074d92bd297c01c3.png ├── index.js ├── .editorconfig ├── .eslintrc ├── README.md ├── server.js ├── index.html ├── LICENSE ├── webpack.config.production.js ├── webpack.config.js ├── package.json └── presentation └── index.js /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /assets/codes/graphql-query.example: -------------------------------------------------------------------------------- 1 | { 2 | user(id: "chentsulin") { 3 | name 4 | } 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/ctlin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/ctlin.jpg -------------------------------------------------------------------------------- /assets/fake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/fake.png -------------------------------------------------------------------------------- /assets/logos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/logos.png -------------------------------------------------------------------------------- /assets/relay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/relay.png -------------------------------------------------------------------------------- /assets/graphiql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/graphiql.png -------------------------------------------------------------------------------- /assets/graphql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/graphql.png -------------------------------------------------------------------------------- /assets/masking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/masking.png -------------------------------------------------------------------------------- /assets/rnplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/rnplay.png -------------------------------------------------------------------------------- /assets/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/container.png -------------------------------------------------------------------------------- /assets/rnplay-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/rnplay-app.png -------------------------------------------------------------------------------- /assets/codes/graphql-response.example: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "user": { 4 | "name": "C.T.Lin" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /assets/react-native.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/assets/react-native.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react", "stage-0"], 3 | "env": { 4 | "development": { 5 | "presets": ["react-hmre"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /dist/044b5116eb37c9646d7af104e2b4a834.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/dist/044b5116eb37c9646d7af104e2b4a834.jpg -------------------------------------------------------------------------------- /dist/17509a9b39e1232b9a519703901916d2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/dist/17509a9b39e1232b9a519703901916d2.png -------------------------------------------------------------------------------- /dist/3b2ccdea43ce740aa92b6965c7d72589.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/dist/3b2ccdea43ce740aa92b6965c7d72589.png -------------------------------------------------------------------------------- /dist/72299e9b80aef40368b0a3df96de2e46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/dist/72299e9b80aef40368b0a3df96de2e46.png -------------------------------------------------------------------------------- /dist/7db7978f9c01dfcde05ac3179ffe8903.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/dist/7db7978f9c01dfcde05ac3179ffe8903.png -------------------------------------------------------------------------------- /dist/d0054443c53264e7074d92bd297c01c3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentsulin/modernweb2016-graphql-relay-intro/HEAD/dist/d0054443c53264e7074d92bd297c01c3.png -------------------------------------------------------------------------------- /assets/codes/graphql-schema.example: -------------------------------------------------------------------------------- 1 | type User { 2 | id: String 3 | email: String 4 | name: String 5 | friends: [User] 6 | } 7 | 8 | type Query { 9 | user(id: String!): User 10 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "react-dom"; 3 | 4 | import Presentation from "./presentation"; 5 | 6 | render(, document.getElementById("root")); 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.example] 10 | insert_final_newline = false 11 | 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | "extends": 3 | - "eslint-config-defaults/configurations/walmart/es6-react" 4 | 5 | "rules": 6 | "indent": [2, 2, {"SwitchCase": 1}] 7 | "max-len": 0 8 | 9 | "env": 10 | "browser": true, 11 | "node": true 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # modernweb2016-graphql-relay-intro 2 | 3 | [https://chentsulin.github.io/modernweb2016-graphql-relay-intro](https://chentsulin.github.io/modernweb2016-graphql-relay-intro) 4 | 5 | Slides for [Modern Web Conference 2016](http://modernweb.tw/) talk - Data Fetching 的過去與未來 - from REST to GraphQL + Relay 6 | -------------------------------------------------------------------------------- /assets/codes/min-query.example: -------------------------------------------------------------------------------- 1 | // REST 2 | fetch('/posts') 3 | .then(parseJSON) 4 | .then(posts => Promise.all( 5 | posts.map( 6 | post => fetch(post.href) 7 | .then(parseJSON) 8 | ) 9 | )) 10 | .then(posts => { ... }); 11 | 12 | // GraphQL 13 | graphql.get( 14 | `query { posts { id, title, content } }` 15 | ) 16 | .then(posts => { ...}); -------------------------------------------------------------------------------- /assets/codes/cache.example: -------------------------------------------------------------------------------- 1 | Map { 2 | // `story(id: "1")` 3 | 1: Map { 4 | author: Link(2), 5 | comments: [Link(3)], 6 | }, 7 | // `story.author` 8 | 2: Map { 9 | name: 'Yuzhi', 10 | photo: 'http://.../photo1.jpg', 11 | }, 12 | // `story.comments[0]` 13 | 3: Map { 14 | author: Link(2), 15 | }, 16 | } -------------------------------------------------------------------------------- /assets/codes/fake.example: -------------------------------------------------------------------------------- 1 | { 2 | post(id: 1234) { 3 | content, photo, createdAt, 4 | postedBy { name, profilePic } 5 | likers { 6 | edges { 7 | node { ...user } 8 | } 9 | } 10 | comments { 11 | edges { 12 | node { 13 | text 14 | postedBy { ...user } 15 | replies { ... } 16 | createdAt 17 | } 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var path = require("path"); 4 | var express = require("express"); 5 | var webpack = require("webpack"); 6 | var config = require("./webpack.config"); 7 | 8 | var app = express(); 9 | var compiler = webpack(config); 10 | 11 | var serverPort = process.env.PORT || 3000; 12 | 13 | app.use(require("webpack-dev-middleware")(compiler, { 14 | noInfo: true, 15 | publicPath: config.output.publicPath 16 | })); 17 | 18 | app.use(require("webpack-hot-middleware")(compiler)); 19 | 20 | app.get("*", function(req, res) { 21 | res.sendFile(path.join(__dirname, "index.html")); 22 | }); 23 | 24 | app.listen(serverPort, "localhost", function (err) { 25 | if (err) { 26 | console.log(err); 27 | return; 28 | } 29 | 30 | console.log("Listening at http://localhost:" + serverPort); 31 | }); 32 | -------------------------------------------------------------------------------- /assets/relay.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Modern Web 2016 Graphql Relay Intro @ chentsulin 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /assets/codes/relay-simple-schema.example: -------------------------------------------------------------------------------- 1 | /* Copy from https://facebook.github.io/relay/ */ 2 | import { 3 | GraphQLInt, 4 | GraphQLList, 5 | GraphQLObjectType, 6 | GraphQLSchema, 7 | GraphQLString, 8 | } from 'graphql'; 9 | 10 | const STORE = { 11 | teas: [ 12 | {name: 'Earl Grey Blue Star', steepingTime: 5}, 13 | {name: 'Milk Oolong', steepingTime: 3}, 14 | {name: 'Gunpowder Golden Temple', steepingTime: 3}, 15 | ], 16 | }; 17 | 18 | var TeaType = new GraphQLObjectType({ 19 | name: 'Tea', 20 | fields: () => ({ 21 | name: {type: GraphQLString}, 22 | steepingTime: {type: GraphQLInt}, 23 | }), 24 | }); 25 | 26 | var StoreType = new GraphQLObjectType({ 27 | name: 'Store', 28 | fields: () => ({ 29 | teas: {type: new GraphQLList(TeaType)}, 30 | }), 31 | }); 32 | 33 | export default new GraphQLSchema({ 34 | query: new GraphQLObjectType({ 35 | name: 'Query', 36 | fields: () => ({ 37 | store: { 38 | type: StoreType, 39 | resolve: () => STORE, 40 | }, 41 | }), 42 | }), 43 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /webpack.config.production.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var path = require("path"); 4 | var webpack = require("webpack"); 5 | 6 | module.exports = { 7 | entry: [ 8 | "babel-polyfill", 9 | "./index" 10 | ], 11 | output: { 12 | path: path.join(__dirname, "dist"), 13 | filename: "bundle.js", 14 | publicPath: "/dist/" 15 | }, 16 | plugins: [ 17 | new webpack.optimize.OccurenceOrderPlugin(), 18 | new webpack.DefinePlugin({ 19 | "process.env": { 20 | "NODE_ENV": JSON.stringify("production") 21 | } 22 | }), 23 | new webpack.optimize.UglifyJsPlugin({ 24 | compressor: { 25 | warnings: false 26 | } 27 | }) 28 | ], 29 | module: { 30 | loaders: [{ 31 | test: /\.md$/, 32 | loader: "html-loader!markdown-loader?gfm=false" 33 | }, { 34 | test: /\.(js|jsx)$/, 35 | exclude: /node_modules/, 36 | loader: "babel-loader", 37 | query: { 38 | presets: ['es2015', 'react'] 39 | } 40 | }, { 41 | test: /\.css$/, 42 | loader: "style-loader!css-loader" 43 | }, { 44 | test: /\.(png|jpg|gif)$/, 45 | loader: "url-loader?limit=8192" 46 | }, { 47 | test: /\.svg$/, 48 | loader: "url?limit=10000&mimetype=image/svg+xml" 49 | }] 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /assets/codes/relay-simple.example: -------------------------------------------------------------------------------- 1 | /* Copy from https://facebook.github.io/relay/ */ 2 | 3 | class Tea extends React.Component { 4 | render() { 5 | var {name, steepingTime} = this.props.tea; 6 | return ( 7 |
  • 8 | {name} ({steepingTime} min) 9 |
  • 10 | ); 11 | } 12 | } 13 | Tea = Relay.createContainer(Tea, { 14 | fragments: { 15 | tea: () => Relay.QL` 16 | fragment on Tea { 17 | name, 18 | steepingTime, 19 | } 20 | `, 21 | }, 22 | }); 23 | 24 | class TeaStore extends React.Component { 25 | render() { 26 | return ; 31 | } 32 | } 33 | TeaStore = Relay.createContainer(TeaStore, { 34 | fragments: { 35 | store: () => Relay.QL` 36 | fragment on Store { 37 | teas { ${Tea.getFragment('tea')} }, 38 | } 39 | `, 40 | }, 41 | }); 42 | 43 | class TeaHomeRoute extends Relay.Route { 44 | static routeName = 'Home'; 45 | static queries = { 46 | store: (Component) => Relay.QL` 47 | query TeaStoreQuery { 48 | store { ${Component.getFragment('store')} }, 49 | } 50 | `, 51 | }; 52 | } 53 | 54 | ReactDOM.render( 55 | , 59 | mountNode 60 | ); 61 | 62 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var path = require("path"); 4 | var webpack = require("webpack"); 5 | 6 | module.exports = { 7 | devtool: "source-map", 8 | entry: [ 9 | "webpack-hot-middleware/client", 10 | "babel-polyfill", 11 | "./index" 12 | ], 13 | output: { 14 | path: path.join(__dirname, "dist"), 15 | filename: "bundle.js", 16 | publicPath: "/dist/" 17 | }, 18 | plugins: [ 19 | new webpack.HotModuleReplacementPlugin(), 20 | new webpack.NoErrorsPlugin() 21 | ], 22 | module: { 23 | loaders: [{ 24 | test: /\.md$/, 25 | loader: "html-loader!markdown-loader?gfm=false" 26 | }, { 27 | test: /\.(js|jsx)$/, 28 | exclude: /node_modules/, 29 | loader: "babel-loader", 30 | query: { 31 | presets:['react', 'es2015'], 32 | env: { 33 | development: { 34 | plugins: [["react-transform", { 35 | transforms: [{ 36 | transform: "react-transform-hmr", 37 | imports: ["react"], 38 | locals: ["module"] 39 | }] 40 | }]] 41 | } 42 | } 43 | } 44 | }, { 45 | test: /\.css$/, 46 | loaders: ["style", "raw"], 47 | include: __dirname 48 | }, { 49 | test: /\.svg$/, 50 | loader: "url?limit=10000&mimetype=image/svg+xml", 51 | include: path.join(__dirname, "assets") 52 | }, { 53 | test: /\.png$/, 54 | loader: "url-loader?mimetype=image/png", 55 | include: path.join(__dirname, "assets") 56 | }, { 57 | test: /\.gif$/, 58 | loader: "url-loader?mimetype=image/gif", 59 | include: path.join(__dirname, "assets") 60 | }, { 61 | test: /\.jpg$/, 62 | loader: "url-loader?mimetype=image/jpg", 63 | include: path.join(__dirname, "assets") 64 | }] 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modernweb2016-graphql-relay-intro", 3 | "version": "1.0.1", 4 | "description": "An introduction to graphql & relay", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "clean": "rimraf dist", 8 | "build": "cross-env NODE_ENV=production webpack --config webpack.config.production.js", 9 | "lint": "eslint --ext .js,.jsx .", 10 | "deploy": "npm run clean & npm run build && git add --all && git commit -nm 'deploy' && git push origin gh-pages", 11 | "start": "cross-env NODE_ENV=development node server.js" 12 | }, 13 | "author": "", 14 | "license": "MIT", 15 | "dependencies": { 16 | "normalize.css": "3.0.3", 17 | "react": "^0.14.3", 18 | "react-dom": "^0.14.3", 19 | "spectacle": "^1.0.4", 20 | "spectacle-code-slide": "^0.1.10" 21 | }, 22 | "devDependencies": { 23 | "autoprefixer-core": "^6.0.1", 24 | "babel-core": "^6.4.0", 25 | "babel-eslint": "^5.0.0-beta6", 26 | "babel-loader": "^6.2.1", 27 | "babel-plugin-react-transform": "^2.0.2", 28 | "babel-polyfill": "^6.3.14", 29 | "babel-preset-es2015": "^6.3.13", 30 | "babel-preset-react": "^6.3.13", 31 | "babel-preset-react-hmre": "^1.0.1", 32 | "babel-preset-stage-0": "^6.3.13", 33 | "cross-env": "^1.0.7", 34 | "css-loader": "^0.23.0", 35 | "eslint": "^1.8.0", 36 | "eslint-config-defaults": "^7.1.1", 37 | "eslint-plugin-filenames": "^0.1.2", 38 | "eslint-plugin-react": "^3.6.3", 39 | "express": "^4.13.3", 40 | "file-loader": "^0.8.4", 41 | "html-loader": "^0.4.0", 42 | "is-buffer": "^1.1.1", 43 | "markdown-loader": "^0.1.7", 44 | "node-libs-browser": "^0.5.3", 45 | "raw-loader": "^0.5.1", 46 | "react-transform-hmr": "^1.0.4", 47 | "rimraf": "^2.4.4", 48 | "style-loader": "^0.13.0", 49 | "surge": "latest", 50 | "url-loader": "^0.5.6", 51 | "webpack": "^1.12.8", 52 | "webpack-dev-middleware": "^1.2.0", 53 | "webpack-hot-middleware": "^2.5.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /assets/graphql.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 72 | -------------------------------------------------------------------------------- /presentation/index.js: -------------------------------------------------------------------------------- 1 | // Import React 2 | import React from "react"; 3 | 4 | // Import Spectacle Core tags 5 | import { 6 | Appear, 7 | CodePane, 8 | Deck, 9 | Fill, 10 | Fit, 11 | Heading, 12 | Image, 13 | Layout, 14 | Link, 15 | ListItem, 16 | List, 17 | Slide, 18 | Spectacle, 19 | Text 20 | } from "spectacle"; 21 | 22 | import CodeSlide from "spectacle-code-slide"; 23 | 24 | // Import image preloader util 25 | import preloader from "spectacle/lib/utils/preloader"; 26 | 27 | // Import theme 28 | import createTheme from "spectacle/lib/themes/default"; 29 | 30 | // Require CSS 31 | require("normalize.css"); 32 | require("spectacle/lib/themes/default/index.css"); 33 | 34 | 35 | const images = { 36 | ctlin: require("../assets/ctlin.jpg"), 37 | logos: require("../assets/logos.png"), 38 | graphiql: require("../assets/graphiql.png"), 39 | fake: require("../assets/fake.png"), 40 | container: require("../assets/container.png"), 41 | masking: require("../assets/masking.png") 42 | }; 43 | 44 | preloader(images); 45 | 46 | const theme = createTheme({ 47 | primary: "#7688EA" 48 | }); 49 | 50 | export default class Presentation extends React.Component { 51 | render() { 52 | return ( 53 | 54 | 55 | 56 | 57 | Data Fetching 的過去與未來 58 | 59 | 60 | - from REST to GraphQL + Relay 61 | 62 | @chentsulin 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | C. T. Lin 72 | Engineering Technical Lead@Yoctol 73 | 74 | 75 | 76 | Node, React, GraphQL.. 77 | 78 | 79 | chentsulin@github 80 | 81 | 82 | chentsulin@twitter 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | Release 93 | 94 | 95 | 96 | 97 | GraphQL
    2015/07 98 |
    99 |
    100 |
    101 | 102 | 103 | 104 | Relay
    2015/08 105 |
    106 |
    107 |
    108 | 109 | 110 | 111 | GraphiQL
    2015/08 112 |
    113 |
    114 |
    115 |
    116 |
    117 | 118 | 119 | 120 | REST 121 | 122 | 123 | Method: GET, POST, PUT, DELETE 124 | Path: /resources, /resources/:id 125 | Status Code: 200, 400, 401, 403, 404... 126 | 127 | 128 | 129 | 130 | 131 | Problems with REST 132 | 133 | 134 | Nested releations 135 | Not efficient with bad network 136 | Versioning 137 | Growing amount of api endpoints 138 | Legacy endpoints and fields 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 |
    149 | 150 | /posts/:id 151 | /posts/:id/comments 152 | /posts/:id/likers 153 | /users/:id 154 | /comments/:id/replies 155 | /comments/:id/likers 156 | 157 | Or something like.. 158 | 159 | /post-with-comments-and-replies/:id 160 | 161 |
    162 |
    163 |
    164 |
    165 | 166 | 167 | 168 | Easy to over-fetching and under-fetching! 169 | 170 | 171 | 172 | 173 | 174 | GraphQL comes in 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | GraphQL 197 | 198 | 199 | From facebook mobile team (newsfeed) 200 | 260 billion requests/day (2015) 201 | Used for 4 years 202 | One schema for all facebook data 203 | 204 | 205 | 206 | 207 | GraphQL Query 208 | 209 |
    210 | 211 | Query 212 | 213 | 219 |
    220 |
    221 | 222 | Response 223 | 224 | 230 |
    231 |
    232 |
    233 | 234 | 235 | 236 | Just like json without values! 237 | 238 | 239 | 240 | 241 | 242 | Single endpoint (usually /graphql) 243 | No more api endpoints! 244 | 245 | 246 | 247 | 248 | 249 | Only fetch what we need 250 | 251 | 252 | 253 | 254 | 255 | Type System 256 | 257 | 258 | 259 | 260 | 261 | Schema 262 | 263 | 269 | 270 | 271 | 272 | 273 | Types 274 | 275 | 276 | Scalar(Int, Float, String, Boolean, ID) 277 | Object 278 | Interface 279 | Union 280 | Enum 281 | InputObject 282 | List 283 | NonNull 284 | 285 | 286 | 287 | 288 | 289 | Future Ecosystem 290 | 291 | 292 | documentation tools 293 | editor plugins 294 | testing tools 295 | linter 296 | 297 | 298 | 299 | 300 | 301 | 302 | GraphiQL 303 | 304 | 305 | 306 | 307 | 308 | 309 | Demo 310 | 311 | 312 | 313 | 314 | 315 | 316 | Relay 317 | 318 | 319 | 320 | 321 | 322 | Container 323 | 324 | 325 | 326 | 327 | 328 | 329 | Co-locate All the things with Component 330 | 331 | 332 | JavaScript 333 | HTML (jsx) 334 | CSS (CSS in JS, CSS Modules..) 335 | Data Requirement (Relay) 336 | Tests (__tests__) 337 | 338 | 339 | 340 | 341 | 342 | Data Masking 343 | 344 | 345 | 346 | 347 | 348 | 349 | Minimize query 350 | 351 | 352 |
    353 | 359 |
    360 | 361 | 362 | Avoid N+1 Query 363 | Single Network Request 364 | 365 | 366 |
    367 |
    368 | 369 | 370 | 371 | Cache 372 | 373 | 374 |
    375 | 381 |
    382 | 383 | 384 | Normalize 385 | Immutable 386 | Views subscribe Record IDs 387 | 388 | 389 |
    390 |
    391 | 392 | 393 | 394 | Default Network Layer 395 | 396 | 397 | Fail requests after a 15 second timeout 398 | failed requests are automatically retried twice 399 | 400 | 401 | 402 | 403 | 404 | Works on React Native! 405 | 406 | 407 | 408 | 420 | 421 | 435 | 436 | 437 | 438 | Demo 439 | 440 | 441 | 442 | 443 | 444 | Resources 445 | 446 | 447 | 448 | Awesome GraphQL 449 | 450 | 451 | Let's Learn GraphQL 452 | 453 | 454 | GraphQL Cheat Sheet 455 | 456 | 457 | GraphQL 繁中文件 458 | 459 | 460 | Relay 繁中文件 461 | 462 | 463 | 464 | 465 | 466 | 467 | End 468 | 469 | 470 | Thank you for listening! 471 | 472 | 473 |
    474 |
    475 | ); 476 | } 477 | } 478 | --------------------------------------------------------------------------------