├── .gitignore ├── .meteor ├── .finished-upgraders ├── .gitignore ├── .id ├── packages ├── platforms ├── release └── versions ├── README.md ├── client ├── main.css ├── main.html ├── main.js └── startup.js ├── imports ├── client │ └── components │ │ └── App.js └── data │ ├── resolvers.js │ ├── schema.js │ └── youtube-connector.js ├── package.json └── server └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | -------------------------------------------------------------------------------- /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | xiq9w71e8d27gvk0cmi 8 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base # Packages every Meteor app needs to have 8 | mobile-experience # Packages for a great mobile UX 9 | mongo # The database Meteor supports right now 10 | blaze-html-templates # Compile .html files into Meteor Blaze views 11 | reactive-var # Reactive variable for tracker 12 | jquery # Helpful client-side library 13 | tracker # Meteor's client-side reactive programming library 14 | 15 | standard-minifier-css # CSS minifier run for production mode 16 | standard-minifier-js # JS minifier run for production mode 17 | es5-shim # ECMAScript 5 compatibility for older browsers. 18 | ecmascript # Enable ECMAScript2015+ syntax in app code 19 | 20 | autopublish # Publish all data to the clients (for prototyping) 21 | insecure # Allow all DB writes from clients (for prototyping) 22 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.3.2.4 2 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | allow-deny@1.0.4 2 | autopublish@1.0.7 3 | autoupdate@1.2.9 4 | babel-compiler@6.6.4 5 | babel-runtime@0.1.8 6 | base64@1.0.8 7 | binary-heap@1.0.8 8 | blaze@2.1.7 9 | blaze-html-templates@1.0.4 10 | blaze-tools@1.0.8 11 | boilerplate-generator@1.0.8 12 | caching-compiler@1.0.4 13 | caching-html-compiler@1.0.6 14 | callback-hook@1.0.8 15 | check@1.2.1 16 | ddp@1.2.5 17 | ddp-client@1.2.7 18 | ddp-common@1.2.5 19 | ddp-server@1.2.6 20 | deps@1.0.12 21 | diff-sequence@1.0.5 22 | ecmascript@0.4.3 23 | ecmascript-runtime@0.2.10 24 | ejson@1.0.11 25 | es5-shim@4.5.10 26 | fastclick@1.0.11 27 | geojson-utils@1.0.8 28 | hot-code-push@1.0.4 29 | html-tools@1.0.9 30 | htmljs@1.0.9 31 | http@1.1.5 32 | id-map@1.0.7 33 | insecure@1.0.7 34 | jquery@1.11.8 35 | launch-screen@1.0.11 36 | livedata@1.0.18 37 | logging@1.0.12 38 | meteor@1.1.14 39 | meteor-base@1.0.4 40 | minifier-css@1.1.11 41 | minifier-js@1.1.11 42 | minimongo@1.0.16 43 | mobile-experience@1.0.4 44 | mobile-status-bar@1.0.12 45 | modules@0.6.1 46 | modules-runtime@0.6.3 47 | mongo@1.1.7 48 | mongo-id@1.0.4 49 | npm-mongo@1.4.43 50 | observe-sequence@1.0.11 51 | ordered-dict@1.0.7 52 | promise@0.6.7 53 | random@1.0.9 54 | reactive-var@1.0.9 55 | reload@1.1.8 56 | retry@1.0.7 57 | routepolicy@1.0.10 58 | spacebars@1.0.11 59 | spacebars-compiler@1.0.11 60 | standard-minifier-css@1.0.6 61 | standard-minifier-js@1.0.6 62 | templating@1.1.9 63 | templating-tools@1.0.4 64 | tracker@1.0.13 65 | ui@1.0.11 66 | underscore@1.0.8 67 | url@1.0.9 68 | webapp@1.2.8 69 | webapp-hashing@1.0.9 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Meteor Apollo Youtube Search 2 | 3 | ```js 4 | npm install 5 | 6 | meteor 7 | ``` 8 | 9 | ## Contribute 10 | It'd be cool to make this example better! Help out! 11 | -------------------------------------------------------------------------------- /client/main.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ 2 | -------------------------------------------------------------------------------- /client/main.html: -------------------------------------------------------------------------------- 1 | 2 | Apollo Youtube Searcher 3 | 4 | 5 | 6 |
7 | 8 | -------------------------------------------------------------------------------- /client/main.js: -------------------------------------------------------------------------------- 1 | import { Meteor } from 'meteor/meteor'; 2 | import { render } from 'react-dom'; 3 | import React from 'react'; 4 | import { ApolloProvider } from 'react-apollo'; 5 | import App from '/imports/client/components/App'; 6 | import ApolloClient from 'apollo-client'; 7 | 8 | const client = new ApolloClient(); 9 | 10 | Meteor.startup(() => { 11 | render( 12 | 13 | , document.getElementById('root')); 14 | }); 15 | -------------------------------------------------------------------------------- /client/startup.js: -------------------------------------------------------------------------------- 1 | import { registerGqlTag } from 'apollo-client/gql'; 2 | 3 | Meteor.startup(() => { 4 | registerGqlTag(); 5 | }); 6 | -------------------------------------------------------------------------------- /imports/client/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-apollo'; 3 | 4 | class App extends React.Component { 5 | constructor() { 6 | super(); 7 | this.state = { 8 | keywords: "Apollo Stack" 9 | } 10 | } 11 | render() { 12 | const { youtubeVideos } = this.props; 13 | const videos = youtubeVideos && youtubeVideos.data; 14 | const searchCount = _.size(videos) || 0; 15 | let input; 16 | return ( 17 |
18 |

Youtube Searcher

19 |
20 | { 26 | this.setState({keywords: e.target.value}); 27 | }} 28 | className="form-control" 29 | placeholder="Enter search terms..." 30 | /> 31 | 39 |
40 |

We found {searchCount} results

41 |
42 | {_.map(videos, ({ videoId, title, description }, index) => { 43 | return ( 44 |
45 |

{title}

46 |

{description}

47 | 48 |
49 | ); 50 | })} 51 |
52 |
53 | ); 54 | } 55 | } 56 | 57 | function mapQueriesToProps() { 58 | return { 59 | youtubeVideos: { 60 | query: gql` 61 | query getKeywords($keywords: String) { 62 | data(keywords: $keywords) { 63 | title 64 | description 65 | videoId 66 | } 67 | } 68 | `, 69 | forceFetch: true, 70 | variables: { 71 | keywords: "Apollo Stack" 72 | } 73 | } 74 | } 75 | } 76 | 77 | 78 | export default connect({mapQueriesToProps})(App); 79 | -------------------------------------------------------------------------------- /imports/data/resolvers.js: -------------------------------------------------------------------------------- 1 | import { Youtube } from '/imports/data/youtube-connector'; 2 | 3 | const resolvers = { 4 | Query: { 5 | async data(root, { keywords }) { 6 | return Youtube.search(keywords); 7 | } 8 | } 9 | }; 10 | 11 | export default resolvers; 12 | -------------------------------------------------------------------------------- /imports/data/schema.js: -------------------------------------------------------------------------------- 1 | export default schema = [` 2 | type SearchResults { 3 | description: String, 4 | title: String, 5 | videoId: String 6 | } 7 | type Query { 8 | data(keywords: String): [SearchResults] 9 | } 10 | schema { 11 | query: Query 12 | } 13 | `]; 14 | -------------------------------------------------------------------------------- /imports/data/youtube-connector.js: -------------------------------------------------------------------------------- 1 | import rp from 'request-promise'; 2 | export const Youtube = { 3 | search(keyword) { 4 | const maxResults = 25; 5 | const key = "AIzaSyCWx8epSrJ8dvlLn7YutD5qB2y_FBrEaRg"; 6 | const uri = "https://www.googleapis.com/youtube/v3/search"; 7 | const request = { 8 | key, 9 | part: "snippet", 10 | maxResults, 11 | order: "viewCount", 12 | q: keyword, 13 | type: "video", 14 | videoEmbeddable: true 15 | }; 16 | const options = { 17 | uri, 18 | qs: request, 19 | headers: { 20 | 'User-Agent': 'Request-Promise' 21 | }, 22 | json: true // Automatically parses the JSON string in the response 23 | }; 24 | return rp(options).then((res) => { 25 | return res.items.map(({ snippet, id }) => { 26 | const videoId = id.videoId; 27 | return { videoId, ...snippet }; 28 | }); 29 | }).catch((err) => { 30 | console.error(err); 31 | }); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-youtube-search", 3 | "private": true, 4 | "scripts": { 5 | "start": "meteor run" 6 | }, 7 | "dependencies": { 8 | "apollo-client": "^0.3.9", 9 | "bluebird": "^3.4.0", 10 | "cls-bluebird": "^1.0.1", 11 | "continuation-local-storage": "^3.1.7", 12 | "express": "^4.13.4", 13 | "fbjs": "^0.8.3", 14 | "graphql": "^0.6.0", 15 | "graphql-tools": "^0.4.1", 16 | "http-proxy-middleware": "^0.15.0", 17 | "meteor-node-stubs": "~0.2.0", 18 | "react": "^15.1.0", 19 | "react-apollo": "^0.3.4", 20 | "react-dom": "^15.1.0", 21 | "react-redux": "^4.4.5", 22 | "redux": "^3.5.2", 23 | "request-promise": "^3.0.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/main.js: -------------------------------------------------------------------------------- 1 | import { apolloServer } from 'graphql-tools'; 2 | import express from 'express'; 3 | import proxyMiddleware from 'http-proxy-middleware'; 4 | import schema from '/imports/data/schema'; 5 | import resolvers from '/imports/data/resolvers'; 6 | 7 | const GRAPHQL_PORT = 4000; 8 | 9 | const graphQLServer = express(); 10 | 11 | graphQLServer.use('/graphql', apolloServer(async () => { 12 | return { 13 | graphiql: true, 14 | pretty: true, 15 | schema, 16 | resolvers 17 | }; 18 | })); 19 | 20 | graphQLServer.listen(GRAPHQL_PORT, () => console.log( 21 | `GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}` 22 | )); 23 | 24 | WebApp.rawConnectHandlers.use(proxyMiddleware(`http://localhost:${GRAPHQL_PORT}/graphql`)); 25 | --------------------------------------------------------------------------------