├── .gitignore ├── README.md ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── assets │ ├── airbnb.png │ ├── annotated-playground.png │ ├── apollo-client.png │ ├── apollo.svg │ ├── astrodaisy-blank.png │ ├── backend.png │ ├── backend2.png │ ├── cloudflare.png │ ├── collaboration.png │ ├── data-driven.png │ ├── data-source.png │ ├── engine.png │ ├── engine2.png │ ├── engine3.png │ ├── frontend.png │ ├── frontend2.png │ ├── future.png │ ├── github.png │ ├── gql-over-rest.png │ ├── graphql-layer.png │ ├── heart.png │ ├── jani.png │ ├── moon.png │ ├── peggy.jpg │ ├── persisted-queries.png │ ├── platform.png │ ├── pq1.png │ ├── pq2.png │ ├── reduce-complexity.png │ ├── schema-diffing.png │ ├── schema.png │ ├── sdd.png │ └── yoga.png ├── code │ ├── client.js │ └── server.js ├── images.js ├── index.js ├── layout.js ├── presentation.js ├── registerServiceWorker.js └── theme.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The future of GraphQL servers (GraphQL Europe 2018) 2 | 3 | GraphQL is all about enabling frontend developers to build products faster. With Apollo Server 2.0, we've made GraphQL server development more approachable for product developers, thanks to a simpler API that works in every environment. In this talk, you'll not only learn about Apollo Server 2.0's exciting new features, you'll also hear Apollo's vision for the future of GraphQL servers, including a sneak peek of what's beyond 2.0. 4 | 5 | ## 📹 [Watch the video!](https://youtu.be/_OTwfCbbVXU) 6 | ## 💻 [View the slides!](https://graphql-eu-apollo.surge.sh) 7 | 8 | ## Try Apollo Server 2.0 9 | - `npm i apollo-server@rc` 10 | 11 | ## Further resources 12 | 13 | - [Apollo Day 2018 (Matt DeBergalis)](https://dev-blog.apollodata.com/apollo-day-sf-from-promise-to-production-with-graphql-332f1b3e7a18) 14 | - [Apollo Server 2.0 docs](https://www.apollographql.com/docs/apollo-server/v2/) 15 | - [Fullstack Error Handling with GraphQL & Apollo (Clarence Ngoh)](https://dev-blog.apollodata.com/full-stack-error-handling-with-graphql-apollo-5c12da407210) 16 | - [GraphQL and Apollo best practices & learning resources](https://www.apollographql.com/docs/) 17 | - [Reconciling GraphQL & Thrift at Airbnb (Adam Neary)](https://medium.com/airbnb-engineering/reconciling-graphql-and-thrift-at-airbnb-a97e8d290712) 18 | - [Early access: Apollo Server + Cloudflare](https://www.apollographql.com/edge/) 19 | - Fullstack example app with Apollo Server 2.0 20 | - [Client](https://github.com/apollographql/fullstack-workshop-client) 21 | - [Server](https://github.com/apollographql/fullstack-workshop-server) 22 | 23 | ## Questions 24 | 25 | If you have any questions about the presentation or Apollo, please feel free to open an issue or start a conversation on [Twitter](https://twitter.com/peggyrayzis). 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-europe-apollo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "deepmerge": "^2.1.0", 7 | "emotion": "^9.1.2", 8 | "prismjs": "1.12.0", 9 | "react": "^16.3.1", 10 | "react-dom": "^16.3.1", 11 | "react-emotion": "^9.1.2", 12 | "spectacle-scripts": "2.0.0" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test --env=jsdom", 18 | "eject": "react-scripts eject", 19 | "deploy": "npm run build && surge -p build" 20 | }, 21 | "devDependencies": { 22 | "surge": "^0.20.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 23 | The future of GraphQL servers by @peggyrayzis - GraphQL Europe 2018 24 | 25 | 26 | 27 | 28 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/airbnb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/airbnb.png -------------------------------------------------------------------------------- /src/assets/annotated-playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/annotated-playground.png -------------------------------------------------------------------------------- /src/assets/apollo-client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/apollo-client.png -------------------------------------------------------------------------------- /src/assets/apollo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 11 | 12 | 13 | 16 | 20 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/assets/astrodaisy-blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/astrodaisy-blank.png -------------------------------------------------------------------------------- /src/assets/backend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/backend.png -------------------------------------------------------------------------------- /src/assets/backend2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/backend2.png -------------------------------------------------------------------------------- /src/assets/cloudflare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/cloudflare.png -------------------------------------------------------------------------------- /src/assets/collaboration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/collaboration.png -------------------------------------------------------------------------------- /src/assets/data-driven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/data-driven.png -------------------------------------------------------------------------------- /src/assets/data-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/data-source.png -------------------------------------------------------------------------------- /src/assets/engine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/engine.png -------------------------------------------------------------------------------- /src/assets/engine2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/engine2.png -------------------------------------------------------------------------------- /src/assets/engine3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/engine3.png -------------------------------------------------------------------------------- /src/assets/frontend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/frontend.png -------------------------------------------------------------------------------- /src/assets/frontend2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/frontend2.png -------------------------------------------------------------------------------- /src/assets/future.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/future.png -------------------------------------------------------------------------------- /src/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/github.png -------------------------------------------------------------------------------- /src/assets/gql-over-rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/gql-over-rest.png -------------------------------------------------------------------------------- /src/assets/graphql-layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/graphql-layer.png -------------------------------------------------------------------------------- /src/assets/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/heart.png -------------------------------------------------------------------------------- /src/assets/jani.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/jani.png -------------------------------------------------------------------------------- /src/assets/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/moon.png -------------------------------------------------------------------------------- /src/assets/peggy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/peggy.jpg -------------------------------------------------------------------------------- /src/assets/persisted-queries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/persisted-queries.png -------------------------------------------------------------------------------- /src/assets/platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/platform.png -------------------------------------------------------------------------------- /src/assets/pq1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/pq1.png -------------------------------------------------------------------------------- /src/assets/pq2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/pq2.png -------------------------------------------------------------------------------- /src/assets/reduce-complexity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/reduce-complexity.png -------------------------------------------------------------------------------- /src/assets/schema-diffing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/schema-diffing.png -------------------------------------------------------------------------------- /src/assets/schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/schema.png -------------------------------------------------------------------------------- /src/assets/sdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/sdd.png -------------------------------------------------------------------------------- /src/assets/yoga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peggyrayzis/future-of-gql-servers/b98cc27172ec2dce01b5ec3bc6bf4a50153099ad/src/assets/yoga.png -------------------------------------------------------------------------------- /src/code/client.js: -------------------------------------------------------------------------------- 1 | export const query = `const GET_DOG_PHOTO = gql\` 2 | query dog($breed: String!) { 3 | dog(breed: $breed) { 4 | id 5 | displayImage 6 | } 7 | } 8 | \`;`; 9 | 10 | export const queryComponent = `const DogPhoto = ({ breed }) => ( 11 | 12 | {({ loading, error, data }) => { 13 | if (loading) return null; 14 | if (error) return 'Error!'; 15 | 16 | return ( 17 | 18 | ); 19 | }} 20 | 21 | );`; 22 | 23 | export const dynamic = ` 26 | {({ loading, data }) => { 27 | if (loading) return 'Loading...'; 28 | return ( 29 |
30 | {(data.movies || data.likes).map(movie => ( 31 | 32 | ))} 33 |
34 | ) 35 | }} 36 |
`; 37 | 38 | export const mutation = `const Login = () => ( 39 | localStorage.setItem('token', login)} 42 | > 43 | {(loginUser, { data, error }) => ( 44 |
45 | 46 |
47 | )} 48 |
49 | );`; 50 | 51 | export const combined = `const TodoApp = () => ( 52 | 53 | {({ loading, data }) => { 54 | if (loading) return 'Loading...'; 55 | return ( 56 | 57 | {removeTodo => 58 | data.todos.map(todo => ( 59 | 60 | ))} 61 | 62 | ); 63 | }} 64 | 65 | );`; 66 | 67 | export const adopt = `const TodoContainer = adopt({ 68 | todos: , 69 | removeTodo: ({ render }) => ( 70 | 71 | {(mutation, result) => render({ mutation, result })} 72 | 73 | ), 74 | }) 75 | const App = () => ( 76 | 77 | {({ todos, removeTodo }) => /* ... */ } 78 | 79 | )`; 80 | 81 | export const testUtils = `const mocks = [{ 82 | request: { query: SINGLE_ORDER_QUERY, variables: { id: '123' } }, 83 | result: { data: { order: fakeOrder() }}, 84 | }]; 85 | 86 | describe('', () => { 87 | it('Renders loading state', () => { 88 | const wrapper = mount( 89 | 90 | 91 | 92 | ); 93 | expect(wrapper.text()).toContain('Loading...'); 94 | }) 95 | })`; 96 | 97 | export const codegen1 = `query Characters { 98 | characters(episode: NEW_HOPE) { 99 | name 100 | ... on Human { 101 | homePlanet 102 | } 103 | ... on Droid { 104 | primaryFunction 105 | } 106 | } 107 | }`; 108 | 109 | export const codegen2 = `export type CharactersQuery = { 110 | characters: Array<{ 111 | __typename: 'Human', 112 | name: string, 113 | homePlanet: ?string 114 | } | { 115 | __typename: 'Droid', 116 | name: string, 117 | primaryFunction: ?string 118 | }> 119 | }`; 120 | 121 | export const boost = `import ApolloClient from "apollo-boost"; 122 | 123 | const client = new ApolloClient({ 124 | uri: "https://your-server-url.me/graphql" 125 | });`; 126 | 127 | export const stateMgmt1 = `const GET_DOG = gql\` 128 | query getDogByBreed($breed: String!) { 129 | dog(breed: $breed) { 130 | images { 131 | url 132 | id 133 | isLiked @client 134 | } 135 | } 136 | } 137 | \`;`; 138 | 139 | export const stateMgmt2 = `const FilterLink = ({ filter, children }) => ( 140 | 141 | {client => ( 142 | 144 | client.writeData({ data: { visibilityFilter: filter } }) 145 | } 146 | > 147 | {children} 148 | 149 | )} 150 | 151 | );`; 152 | 153 | // const GET_DOGS = gql` 154 | // { 155 | // dogs { 156 | // id 157 | // breed 158 | // displayImage 159 | // } 160 | // } 161 | // `; 162 | 163 | // const Dogs = () => ( 164 | // 165 | // {({ loading, data, error, fetchMore }) => { 166 | // if (loading) return null; 167 | // if (error) return 'Error!'; 168 | 169 | // return data.dogs.map(dog => ); 170 | // }} 171 | // 172 | // ); 173 | -------------------------------------------------------------------------------- /src/code/server.js: -------------------------------------------------------------------------------- 1 | export const restExample = `GET /api/pizza/cheese 2 | GET /api/pizza/supreme 3 | GET /api/pizza/margherita`; 4 | 5 | export const gqlExample = `query { 6 | pizza { 7 | cheese { 8 | mozzarella 9 | } 10 | toppings { 11 | basil 12 | mushrooms 13 | } 14 | } 15 | }`; 16 | 17 | export const apolloServer = `const server = new ApolloServer({ 18 | typeDefs, 19 | resolvers 20 | }); 21 | 22 | server.listen() 23 | .then(() => { 24 | console.log('Server ready! 🚀') 25 | }) 26 | `; 27 | 28 | export const errors = `import { AuthenticationError } from 'apollo-server'; 29 | 30 | const resolvers = { 31 | Mutation: { 32 | protectedAction(root, args , { user }) { 33 | if (!user) { 34 | throw new AuthenticationError('You must be logged in'); 35 | } 36 | } 37 | } 38 | };`; 39 | 40 | export const yoga = `import { GraphQLServer } from 'graphql-yoga' 41 | 42 | const server = new GraphQLServer({ 43 | typeDefs, 44 | resolvers 45 | }) 46 | 47 | server.start(() => 48 | console.log('Server is running!' 49 | ))`; 50 | 51 | export const errors2 = `const client = new ApolloClient({ 52 | uri: 'https://mygraphqlapi.com/graphql', 53 | onError: ({ graphQLErrors, networkError, operation, forward }) => { 54 | if (graphQLErrors) { 55 | for (let err of graphQLErrors) { 56 | switch (err.extensions.code) { 57 | case 'UNAUTHENTICATED': 58 | const { headers } = operation.getContext() 59 | operation.setContext({ 60 | headers: { 61 | ...headers, 62 | authorization: getNewToken(), 63 | }, 64 | }); 65 | return forward(operation); 66 | case 'ANOTHER_ERROR_CODE': 67 | // ... 68 | } 69 | } 70 | } 71 | }, 72 | });`; 73 | 74 | // const fetchMovies = sort => async dispatch => { 75 | // dispatch(requestMovies(sort)); 76 | 77 | // const results = await fetch('https://movieapi.com/movies'); 78 | // const movies = await results.json(); 79 | 80 | // const sortedMovies = 81 | // sort === 'POPULARITY' 82 | // ? movies.sort((a, b) => b.popularity - a.popularity) 83 | // : movies; 84 | 85 | // return dispatch(receiveMovies(sort, sortedMovies)); 86 | // }; 87 | 88 | // const resolvers = { 89 | // Query: { 90 | // movies: async (root, { sort }) => { 91 | // const results = await fetch('https://movieapi.com/movies'); 92 | // const movies = await results.json(); 93 | 94 | // return sort === 'POPULARITY' 95 | // ? movies.sort((a, b) => b.popularity - a.popularity) 96 | // : movies; 97 | // }, 98 | // }, 99 | // }; 100 | 101 | export const dataSource = `class MovieAPI extends RESTDataSource { 102 | baseURL = 'https://movieapi.com/'; 103 | 104 | async getMostViewedMovies() { 105 | const body = await this.get('movies', { 106 | per_page: 10, 107 | order_by: 'most_viewed', 108 | }); 109 | return body.results; 110 | } 111 | willSendRequest(request: Request) { 112 | request.headers.set('Authorization', 113 | this.context.token 114 | ); 115 | } 116 | }`; 117 | -------------------------------------------------------------------------------- /src/images.js: -------------------------------------------------------------------------------- 1 | import apolloLogo from './assets/apollo.svg'; 2 | import apollo from './assets/apollo.svg'; 3 | import peggy from './assets/peggy.jpg'; 4 | import reduceComplexity from './assets/apollo-client.png'; 5 | import engine from './assets/engine.png'; 6 | import engine2 from './assets/engine2.png'; 7 | import engine3 from './assets/engine3.png'; 8 | import pq1 from './assets/pq1.png'; 9 | import pq2 from './assets/pq2.png'; 10 | import annotatedPlayground from './assets/annotated-playground.png'; 11 | import gqlOverRest from './assets/gql-over-rest.png'; 12 | import dataDriven from './assets/data-driven.png'; 13 | import schemaDiffing from './assets/schema-diffing.png'; 14 | import layer from './assets/graphql-layer.png'; 15 | import sdd from './assets/sdd.png'; 16 | import cloudflare from './assets/cloudflare.png'; 17 | import platform from './assets/platform.png'; 18 | import yoga from './assets/yoga.png'; 19 | import moon from './assets/moon.png'; 20 | import frontend from './assets/frontend.png'; 21 | import backend from './assets/backend.png'; 22 | import frontend2 from './assets/frontend2.png'; 23 | import backend2 from './assets/backend2.png'; 24 | import airbnb from './assets/airbnb.png'; 25 | import github from './assets/github.png'; 26 | import jani from './assets/jani.png'; 27 | import future from './assets/future.png'; 28 | import heart from './assets/heart.png'; 29 | import collaboration from './assets/collaboration.png'; 30 | import complexity from './assets/reduce-complexity.png'; 31 | import persistedQueries from './assets/persisted-queries.png'; 32 | import dataSource from './assets/data-source.png'; 33 | import astroDaisy from './assets/astrodaisy-blank.png'; 34 | 35 | export default { 36 | apolloLogo, 37 | peggy, 38 | reduceComplexity, 39 | apollo, 40 | annotatedPlayground, 41 | gqlOverRest, 42 | dataDriven, 43 | schemaDiffing, 44 | layer, 45 | engine, 46 | sdd, 47 | cloudflare, 48 | complexity, 49 | engine2, 50 | platform, 51 | engine3, 52 | yoga, 53 | heart, 54 | collaboration, 55 | pq1, 56 | pq2, 57 | moon, 58 | frontend, 59 | frontend2, 60 | backend, 61 | backend2, 62 | airbnb, 63 | github, 64 | jani, 65 | persistedQueries, 66 | future, 67 | dataSource, 68 | astroDaisy, 69 | }; 70 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Presentation from './presentation'; 4 | import registerServiceWorker from './registerServiceWorker'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | registerServiceWorker(); 8 | -------------------------------------------------------------------------------- /src/layout.js: -------------------------------------------------------------------------------- 1 | import styled from 'react-emotion'; 2 | 3 | export const Row = styled('div')(props => ({ 4 | display: 'flex', 5 | justifyContent: 'space-between', 6 | width: props.width || '100%', 7 | alignItems: props.align, 8 | ...props.style, 9 | })); 10 | 11 | export const Column = styled('div')(props => ({ 12 | display: 'flex', 13 | flexDirection: 'column', 14 | alignItems: props.align || 'flex-start', 15 | flex: 1, 16 | ...props.style, 17 | })); 18 | 19 | export const Cell = styled('div')(props => ({ 20 | backgroundColor: props.color, 21 | padding: '40px', 22 | borderRadius: '20px', 23 | flex: 1, 24 | margin: props.margin, 25 | ...props.style, 26 | })); 27 | -------------------------------------------------------------------------------- /src/presentation.js: -------------------------------------------------------------------------------- 1 | /* eslint jsx-a11y/accessible-emoji: 0 */ 2 | 3 | // Import React 4 | import React from 'react'; 5 | 6 | // Import Spectacle Core tags 7 | import { 8 | Appear, 9 | Deck, 10 | ListItem, 11 | List, 12 | Image, 13 | Slide, 14 | Text, 15 | Heading, 16 | CodePane, 17 | Notes, 18 | Link, 19 | } from 'spectacle'; 20 | 21 | // Spectacle Theme & Layout components 22 | import { theme, colors, size } from './theme'; 23 | import * as Layout from './layout'; 24 | 25 | // Prism plugins 26 | import 'prismjs/components/prism-bash'; 27 | import 'prismjs/components/prism-graphql'; 28 | 29 | // Server & client code samples 30 | import * as client from './code/client'; 31 | import * as server from './code/server'; 32 | 33 | // Preload images 34 | import preloader from 'spectacle/lib/utils/preloader'; 35 | import images from './images'; 36 | 37 | preloader(images); 38 | 39 | // Require CSS 40 | require('normalize.css'); 41 | 42 | export default class Presentation extends React.Component { 43 | render() { 44 | return ( 45 | 54 | 55 | 66 | The future of
67 | 74 | GraphQL servers 🚀 75 | 76 |
77 |
78 | 79 |
87 | 93 | 94 | 95 | @peggyrayzis 96 | 97 | 102 | 103 |
104 |
105 | 106 | 107 | Lately, I've been traveling around the world to conferences and 108 | workshops and I hear common themes emerge. Often the people I meet 109 | are frontend developers. They're really excited about GraphQL and 110 | all the benefits they'd stand to gain but they're having trouble 111 | adopting it in their own organization. These frontend developers are 112 | also usually concerned about building a GraphQL server. 113 |
114 |
115 | On the other hand, the backend developers are usually not as 116 | excited. They're more skeptical and rightfully so since GraphQL's 117 | focus on the product represents a huge shift in their workflow. 118 | They're mostly concerned about performance, caching, and maintaining 119 | control over their services. 120 |
121 | The road to adoption 🛣 122 | 123 | 124 | 128 | 129 |
130 | 131 | 132 | This disagreement between frontend and backend engineers can stall 133 | progress on GraphQL adoption for months, or maybe even years as was 134 | the case at Airbnb. We need a way to find a compromise here. 135 | 136 | 137 | 141 | 147 | - @AdamRNeary, "Reconciling GraphQL and Thrift at Airbnb" 148 | 149 | 150 | 151 | 152 | 153 | The benefits of GraphQL are hard to deny. We're building for mobile, 154 | wearables, VR, IOT - all separate clients requesting data from a 155 | multitude of different microservices. We have to meet our users 156 | where they are so we have to keep building for new platforms and 157 | duplicating logic across clients. GraphQL prevents this situation 158 | from spiraling out of control. 159 | 160 | Our apps are data driven 161 | 162 | 163 | 164 | 165 | GraphQL also fosters collaboration between frontend and backend 166 | teams. When the schema is a contract that both sides can adhere to, 167 | each side can work independently with a common goal in mind. The 168 | frontend doesn't even need to wait on backend changes to be 169 | productive - they can mock out the schema and keep developing Query 170 | components before the data fetching logic is finalized. 171 | 172 | GraphQL fosters collaboration 173 | 174 | 175 | 176 | 177 | Perhaps one of the greatest benefits is how GraphQL clients can 178 | reduce the complexity managing state from within your application. 179 | Instead of writing complex code for fetching and transforming the 180 | data into the shape you need, you just create a GraphQL query. 181 | Apollo client will take care of making the request, caching it, 182 | tracking loading and error state, as well as updating your UI. 183 | Whether your favorite client is Apollo, Relay, or Urql, I think we 184 | all can agree that managing state with any GraphQL client is a hell 185 | of a lot better than rolling your own solution with REST and Redux. 186 | 187 | Apollo reduces complexity 188 | 189 | 190 | 191 | 192 | So how do we bridge the gap between frontend and backend developers? 193 | To figure out a solution, we have to look at their motivations and 194 | find some common ground. 195 |
196 |
197 | Frontend devs want to have a say in what happens with their GraphQL 198 | API because they're the ones using it. Often, they're also the ones 199 | tasked with building an initial server implementation or POC, so 200 | they need an excellent out of the box server experience. They also 201 | are heavily concerned with identifying and following best practices 202 | for things like auth, testing, and schema design. 203 |
204 |
205 | On the other hand, backend developers want to ensure that their 206 | underlying services won't suffer performance hits as a result of 207 | introducing GraphQL. They're worried about things caching and 208 | monitoring. 209 |
210 | Bridging the gap 211 | 212 | 213 | 214 | 215 | 219 | 220 |
221 | 222 | 223 | Their common ground here is the server. If we can design a better 224 | server experience, complete with advanced tooling to satisfy backend 225 | developers and a great getting started experience for the frontend 226 | developers, maybe we can speed up GraphQL adoption as a whole. 227 |
228 |
229 | At Apollo, this is exactly what was on our mind as we started to 230 | plan the next generation of Apollo Server. 231 |
232 | 241 | We need a better server
experience in order to
242 | 249 | accelerate adoption. 250 | 251 |
252 |
253 | 254 | 255 | Up until now, I've used the term GraphQL server but sometimes I 256 | think the word server can be intimidating, especially for frontend 257 | developers just getting started. I think it's more accurate to call 258 | it a GraphQL layer since it often sits on top of your existing data 259 | sources. We need to lower the barrier of entry to building this 260 | layer so its achievable by product teams, since GraphQL APIs are at 261 | their best when the product team using them has input into the 262 | design. 263 | 264 | 265 | GraphQL{' '} 266 | server layer 267 | 268 | 269 | 270 | 271 | 272 | Layering GraphQL on top of REST is many developers' first experience 273 | with GraphQL, so we wanted to make this experience better. One of 274 | the best parts of GraphQL is that it doesnt require an entire 275 | rewrite. You can migrate incrementally and rather quickly by 276 | building a GraphQL server over your existing REST endpoints. One 277 | common pain point is caching however. Teams don't want to have to 278 | totally reimplement their existing caching logic when switching to 279 | GraphQL. 280 | 281 | GraphQL over REST 282 | 283 | 284 | 285 | 286 | 287 | 288 | {[ 289 | 'Start with the APIs you already have & migrate incrementally', 290 | 'Fastest path to adoption for product developers', 291 | 'What about existing caching strategies already in place?', 292 | ].map(item => ( 293 | 294 | {item} 295 | 296 | ))} 297 | 298 | 299 | 300 | 301 | 302 | 303 | Additionally, we needed a better out of the box experience. We were 304 | inspired by what Prisma did with graphql-yoga to connect the dots 305 | between APollo Server, graphql-tools and graphql-subscriptions to 306 | create an excellent getting started experience for GraphQL beginners 307 | and seasoned experts. 308 | 309 | A simpler API 310 | 311 | 318 | 319 | 320 | 321 | 322 | 323 | Ultimately, our goal for the next version of Apollo Server was to 324 | make developing this GraphQL layer approachable for everyone. We 325 | wanted to enable product developers to get GraphQL running quickly 326 | and give them the confidence they needed in order to actually ship 327 | their layer to production. With this goal in mind, we set out to 328 | totally revamp the Apollo Server experience. After a few weeks in 329 | beta and hearing all of your feedback, we're so excited to announce 330 | 331 | 340 | We want to make
341 | developing a GraphQL layer
342 | 343 | approachable 344 | {' '} 345 | for everyone. 346 |
347 |
348 | 349 | 350 | that Apollo Server 2.0 is officially out of beta!! If you haven't 351 | tried it yet, I think you're really going to love it. Let's take a 352 | look at some of the new features 353 | 354 |
355 | Apollo Server 2.0 356 | 366 | New release candidate out today 🎉 367 | 368 |
369 |
370 | 371 | 372 | Instead of giving you separate tools that you'd have to fit together 373 | yourself, Apollo Server wires everything up for you already based on 374 | years of best practices. With only a couple lines of code, you can 375 | set up an GraphQL server in minutes. Out of the box, you get an 376 | Express server that includes our new error management features, as 377 | well as a health check endpoint, and advanced features like 378 | subscriptions and schema stitching already set up for you. 379 | 380 | Apollo Server 2.0 🎉 381 | 382 | 388 | 389 | {[ 390 | 'Standard patterns based on best practices, all wired up', 391 | 'Includes error management & a health check endpoint', 392 | 'Supports subscriptions, schema stitching, & file uploads out of the box', 393 | ].map(item => ( 394 | 395 | {item} 396 | 397 | ))} 398 | 399 | 400 | 401 | 402 | 403 | I want to talk a little about the new error handling primitives 404 | because they not only improve the developer experience working with 405 | GraphQL, they also get us to speak the same language about errors. 406 | Now you can throw an AuthorizationError for example in a resolver. 407 | This new error primitive uses the extensions.code property to 408 | produce a human readable status code, which you can check in an 409 | Apollo Link on the frontend to implement a reauth flow. For more 410 | information, check out the announcement post by one of our interns 411 | CLarence. I'll send out the links to the all the articles I 412 | reference on my Twitter after the talk. 413 | 414 | Intuitive error handling 🚀 415 | 416 | 417 | 418 | Check out the blog post by @clarencengohpy! 419 | 420 | 421 | 422 | 427 | 428 | With Apollo Server 2.0, you also get GraphQL Playground 429 | automatically set up for you. Shoutout to the Prisma team for their 430 | hard work on GraphQL Playground, it continues to be my favorite way 431 | to test queries and teach developers about GraphQL. 432 | 433 | 434 | 435 | 436 | Also included in Apollo Server 2.0 are some production-ready 437 | features guaranteed to please even the most skeptical backend 438 | engineers on your team! We really wanted performance to be a first 439 | class feature of Apollo Server 2.0, so we included response caching 440 | via the cache controld directive and reporting to Apollo Engine. 441 | 442 |
443 | Production-ready features 444 | 454 | Caching and reporting to Apollo Engine,
now included in 455 | Apollo Server! 😍 456 |
457 |
458 |
459 | 460 | 461 | Apollo Engine is our cloud service that provides deep insights into 462 | the performance of your GraphQL server. It has never been easier to 463 | set up Engine than it is now. Just provide an API key as an 464 | environment variable and Apollo Server will do all the heavy lifting 465 | for you. Some of the great features that used to require the engine 466 | proxy before are now enabled by default in Apollo Server. They're 467 | 100% open source. Another one of those features is 468 | 469 | 470 | 471 | 472 | 473 | Automatic persisted queries! All you have to do to complete the 474 | setup process is add the persisted queries link to your Apollo Link 475 | chain. This allows you to send a hash instead of the full query 476 | text, which improves network performance. It's also useful for 477 | sending queries as GET requests, which allows you to cache them with 478 | a CDN. 479 | 480 | Automatic persisted queries 481 | 482 | 483 | 484 | {[ 485 | 'On the client, add Apollo Link Persisted Queries to turn on persisted queries', 486 | 'Hashed queries can be sent as GET requests, enabling CDN caching', 487 | ].map(item => ( 488 | 489 | {item} 490 | 491 | ))} 492 | 493 | 494 | 495 | 496 | 497 | Don't worry if this sounds like a lot of information - We're also 498 | excited to announce a new docs site for Apollo Server 2.0 which will 499 | guide you through all the latest features! Jani, if you're tuning in 500 | on the live stream, this one's for you. All the new features are 501 | already documented and we're hoping to add more interactive examples 502 | on Glitch in the weeks leading up to the final release. 503 | 504 | 510 | 514 | 515 | https://www.apollographql.com/docs/apollo-server/v2/ 516 | 517 | 518 | 519 | 520 | 521 | Where do we go from here? The last part of this talk is going to 522 | cover how we see the future of GraphQL servers evolving over the 523 | next year and how Apollo Server 2.0 moves us in that direction. 524 | 525 | 534 | What's{' '} 535 | 542 | next? 543 | 544 | 545 | 546 | 547 | 548 | Ultimately, our goal is to enable all developers, whether they're 549 | frontend or backend, to create a product focused API and ship it to 550 | production. What's next on the path toward that goal? 551 | 552 | 561 | We want to enable all developers to create a
562 | 569 | product-focused{' '} 570 | 571 | API. 572 |
573 |
574 | 575 | 576 | Wrapping a REST endpoint is often the fastest path to GraphQL 577 | adoption, but the best way to achieve that hasn't always been clear 578 | until now. I couldn't be more excited about the work Martijn has 579 | been doing on the new Apollo Server Data Source API. It's a new way 580 | to wrap REST endpoints that takes care of all the data fetching code 581 | for you so you just have to worry about writing business logic. It 582 | also comes with a shared cache in order to enable whole and partial 583 | query caching. One of the coolest features is that it picks up on 584 | existing cache hints from within your REST response's headers so you 585 | can reuse existing caching logic if you'd like. You also have the 586 | option of specifying cache hints on the schema using the 587 | cachecontrol directive. 588 | 589 | 590 | 591 | 592 | 593 | Apollo Server Data Source 594 | 595 | 596 | {[ 597 | 'Easiest way to wrap a REST endpoint with GraphQL', 598 | 'Shared cache for whole and partial query caching', 599 | 'Existing cache-control hints are automatically tied to the fields in your schema', 600 | ].map(item => ( 601 | 602 | {item} 603 | 604 | ))} 605 | 606 | 607 | 608 | 609 | 610 | 611 | Let's take a look at an example! Just extend from the REST data 612 | source class and you'll have access to data fetching primitives, as 613 | well as the context if you'd like to set headers from within the 614 | data source. 615 |
616 | We're also planning on introducing features like error metrics and 617 | tracing grouped by data source, which will allow you to gain full 618 | visibility into what's happening with your data sources in Apollo 619 | Engine. The new data source API is available for you to play with in 620 | the new release candidate - definitely try it out and let us know 621 | what you think! 622 |
623 | Apollo Server Data Source 624 | 625 | 632 | 633 | {[ 634 | 'Includes data fetching primitives so you only have to supply an endpoint', 635 | 'Allows developers to focus on business logic', 636 | 'Coming soon: deduplication, error handling, per source tracing & metrics 🙌', 637 | ].map((item, idx) => ( 638 | 639 | {item} 640 | 641 | ))} 642 | 643 | 644 |
645 | 646 | 647 | I've used the term partial query caching a couple times, let's see 648 | what it actually looks like. It's helpful for data that's a mix of 649 | static and dynamic, for example, we have mostly static movie data 650 | with a dynamic playback rate attached. This query is calling a REST 651 | endpoint wrapped with the Apollo Server data source API. Here's what 652 | this query looks like when you first run it. Notice all the resolver 653 | tracing metrics at the bottom. 654 | 655 | 656 | 657 | 658 | 659 | The second time you run it, all the static data is already cached 660 | and we only have to fetch the dynamic data, as you can see from the 661 | resolver tracing stats at the bottom. This is partial query caching 662 | at work thanks to the Apollo Server Data Source API. 663 | 664 | 665 | 666 | 667 | 668 | Data Sources simplify server development at the bottom of the stack, 669 | but what can we do to simplify development at the schema level? How 670 | do we ensure that changes to our schema don't break our UI? That's 671 | where Apollo's new schema management tools come in. Just specify a 672 | list of validation rules for your schema and Apollo Server will 673 | check each change you make against those rules. This allows you to 674 | refactor, add fields, deprecate fields, and clean up code with 675 | confidence that you haven't broken anything. 676 | 677 | 678 | 683 | 684 | 685 | Collaboration across the stack 686 | 687 | 688 | {[ 689 | 'New tools to validate your schema and existing queries against a set of rules to prevent breaking changes', 690 | 'Integrates with GitHub as a part of your CI workflow', 691 | ].map(item => ( 692 | 693 | {item} 694 | 695 | ))} 696 | 697 | 698 | 699 | 700 | 701 | 702 | {' '} 703 | Our schema validation integrates with GitHub so it's a natural part 704 | of your existing CI workflow. We hope to create even more natural 705 | extension points in your editor and beyond to optimize collaboration 706 | between frontend and backend engineers. 707 | 708 | 716 | 717 | 718 | 719 | Not only will backend engineers love our new schema collaboration 720 | features, they can also take advantage of schema monitoring from 721 | within Apollo Engine in order to determine which queries need 722 | optimization. Apollo Engine provides field by field monitoring for 723 | your entire schema that allows you to see how often a field is used 724 | and how much time it takes to resolve. 725 | 726 | Apollo Engine schema analysis 727 | 728 | 729 | 730 | 731 | Many teams have told us they want better tools for managing their 732 | schemas, so we're excited to see where this is heading in the next 733 | couple months. We see a sample workflow looking like this. If a 734 | frontend developer wants to add a field based on the needs of their 735 | UI, they can submit a pull request which alerts a backend engineer. 736 | THe backend engineer can use our GitHub checks integration to ensure 737 | that any schema changes are non-breaking. Then, they can use the 738 | schema validation report combined with Apollo Engine schema metrics 739 | to offer feedback on the change. We think this end to end workflow 740 | will not only improve productivity but also collaboration between 741 | both sides. 742 | 743 | Schema collaboration 744 | 745 | 746 | 747 | 748 | Another reason to use Apollo Server for your GraphQL layer is that 749 | you'll be soon able to run it on the edge. This is something 750 | experimental that we've been working on which is super cool. It uses 751 | Cloudflare's new worker platform to run Apollo Server in a truly 752 | serverless environment. This allows you to use the Data Source API 753 | to cache whole query responses from within your CDN and partial 754 | responses on the edge, delivering data to your users faster. If 755 | you'd like to be on the list for early access, check out the link 756 | below. We'd love to hear your feedback! 757 | 758 | GraphQL on the edge 759 | 760 | 761 | 762 | {[ 763 | `Run Apollo Server on the edge using Cloudflare's new worker platform`, 764 | 'Cache whole query responses from within the CDN', 765 | 'Completely serverless', 766 | 'Early access: apollographql.com/edge', 767 | ].map((item, idx) => ( 768 | 769 | {item} 770 | 771 | ))} 772 | 773 | 774 | 775 | 776 | 777 | With better visibility into our GraphQL APIs through a comprehensive 778 | tooling story and reducing the steps it takes to build and deploy a 779 | GraphQL layer into production, I think we'll be able to make both 780 | frontend and backend teams happy in the end. Between Data Sources, 781 | schema management, and running GraphQL on the edge, we're super 782 | excited with this new direction and would love to hear your thoughts 783 | on what the next generation of GraphQL servers could look like. 784 | 785 | 786 | 787 | 788 | 789 | Finally, if you're looking for information to take back to your 790 | team, definitely check out our new learning resource at 791 | apollographql.com/docs. It features implementation guides for auth, 792 | testing, schema design, state management as well as getting started 793 | guides for Apollo Server 2.0 and Apollo Client. 794 | 795 | 800 | Get started! 801 | 802 | 803 | apollographql.com/docs 804 | 805 | 806 | 807 | 808 | If you have any questions, feel free to come find me or any members 809 | of the Apollo team later today. Thank you! 810 | 811 |
819 | 825 | 826 | 827 | @peggyrayzis 828 | 829 | 830 | @apollographql 831 | 832 | 833 |
834 |
835 |
836 | ); 837 | } 838 | } 839 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | } else { 39 | // Is not local host. Just register service worker 40 | registerValidSW(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/theme.js: -------------------------------------------------------------------------------- 1 | import merge from 'deepmerge'; 2 | import createTheme from 'spectacle/lib/themes/default'; 3 | 4 | export const colors = { 5 | primary: '#FFFFFF', 6 | secondary: '#353c59', 7 | tertiary: '#10cbc4', 8 | quartenary: '#e535ab', 9 | navy: '#263C88', 10 | lightGrey: '#f7f8fa', 11 | }; 12 | 13 | export const size = { 14 | extraSmall: '1em', 15 | small: '1.5em', 16 | medium: '2.3em', 17 | large: '3.3em', 18 | }; 19 | 20 | const overrides = { 21 | components: { 22 | content: { 23 | display: 'flex', 24 | flexDirection: 'column', 25 | alignItems: 'flex-start', 26 | justifyContent: 'center', 27 | width: '100%', 28 | height: '100%', 29 | }, 30 | text: { 31 | color: colors.secondary, 32 | fontSize: size.medium, 33 | lineHeight: 1.2, 34 | margin: '0px', 35 | textAlign: 'left', 36 | letterSpacing: '1.5px', 37 | }, 38 | image: { 39 | margin: '0px', 40 | }, 41 | heading: { 42 | h1: { 43 | color: colors.tertiary, 44 | fontSize: size.large, 45 | fontWeight: 'bold', 46 | letterSpacing: '1.5px', 47 | margin: '0px 0px 40px 0px', 48 | }, 49 | h2: { 50 | color: colors.tertiary, 51 | fontSize: size.medium, 52 | fontWeight: 'bold', 53 | letterSpacing: '1.5px', 54 | }, 55 | }, 56 | link: { 57 | color: colors.tertiary, 58 | }, 59 | listItem: { 60 | fontSize: size.medium, 61 | lineHeight: 1.5, 62 | listStylePosition: 'outside', 63 | marginLeft: '1em', 64 | paddingLeft: '30px', 65 | }, 66 | code: { 67 | fontSize: '0.75em', 68 | }, 69 | codePane: { 70 | margin: 0, 71 | minWidth: '300px', 72 | maxWidth: 'none', 73 | '& .prism-code': { 74 | background: 'transparent !important', 75 | }, 76 | '& .buildin-prism-theme, .token.function': { 77 | color: `${colors.quartenary} !important`, 78 | }, 79 | '& .buildin-prism-theme, .token.attr-name': { 80 | color: `${colors.tertiary} !important`, 81 | }, 82 | fontSize: size.extraSmall, 83 | }, 84 | }, 85 | }; 86 | 87 | export const theme = merge( 88 | createTheme(colors, { 89 | primary: { 90 | name: 'Source Sans Pro', 91 | googleFont: true, 92 | styles: ['400', '200'], 93 | }, 94 | secondary: 'Helvetica', 95 | }), 96 | { screen: overrides, print: overrides }, 97 | ); 98 | --------------------------------------------------------------------------------