├── .babelrc ├── .gitignore ├── GraphQl-SpaceX-Demo.gif ├── GraphiQL-demo.gif ├── README.md ├── graphiql.png ├── package-lock.json ├── package.json ├── public └── index.html ├── schema.js ├── server.js ├── src ├── App.js ├── Style.css ├── components │ ├── Launch.js │ ├── LaunchItem.js │ ├── Launches.js │ └── MissionKey.js ├── index.js ├── loader.gif └── logo.png └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "@babel/preset-env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": [ 7 | "last 2 Chrome versions", 8 | "last 2 Firefox versions", 9 | "last 2 Safari versions", 10 | "last 2 iOS versions", 11 | "last 1 Android version", 12 | "last 1 ChromeAndroid version", 13 | "ie 11" 14 | ] 15 | } 16 | } ], 17 | "@babel/preset-react" 18 | ], 19 | "plugins": [ "@babel/plugin-proposal-class-properties" ] 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.log 3 | npm-debug.log 4 | yarn-error.log 5 | .DS_Store 6 | build/ 7 | node_modules/ 8 | dist/ 9 | .cache 10 | -------------------------------------------------------------------------------- /GraphQl-SpaceX-Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/graphql-react-app/b4ff87d3ac03884fe3eb5ac3ef0879c88c8fc265/GraphQl-SpaceX-Demo.gif -------------------------------------------------------------------------------- /GraphiQL-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/graphql-react-app/b4ff87d3ac03884fe3eb5ac3ef0879c88c8fc265/GraphiQL-demo.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL React Application 2 | 3 | ## Description :clipboard: 4 | > A demo app for using GraphQl, Appollo Client with React and SpaceX API 5 | 6 | ## GraphQL App DEMO :video_camera: 7 | ![](GraphQl-SpaceX-Demo.gif) 8 | 9 | ## Details :scroll: 10 | 11 | 1. :arrow_right: We have set up our backend server in node and express app ( `localhost:5000/graphql` ) in `server.js`. 12 | 2. :arrow_right: React application is set up using webpack and babel ( that runs webpack-dev-server for the frontend on `localhost:8080` ) 13 | 3. :arrow_right: The schema and graphQl queries are created in `schema.js` 14 | 4. :arrow_right: We have used axios to fetch data using SpaceX API, in the created queries. ( e.g. API URL: `https://api.spacexdata.com/v3/launches` ) 15 | 5. :arrow_right: GraphiQL playground is available on `localhost:5000/graphql`. Check demo. 16 | 6. :arrow_right: We have used Appolo Client to build UI in React that fetches data from GraphQL 17 | 7. :arrow_right: React components are created and wrapped them inside `` and appolo `client` is then passed to these components. 18 | 8. :arrow_right: Reach router is used to create routes for home and individual pages. 19 | 9. :arrow_right: The `graphql-tag` ( graphQl query parsing utility ) is installed and `gpl` is imported from it. The `gpl` parses GraphQL query strings into the standard GraphQL AST. 20 | 10. :arrow_right: Use `gpl` to query the data in front react app, from the schema we have create in our node application in backend. 21 | 11. :arrow_right: We have displayed all the data received as response of the query on home page( `Launches.js` ). 22 | 12. :arrow_right: When user request for a particular launch item, we query that item by its id and display it on an individual page `Launch.js` 23 | ( e.g. We redirect the user on url `http://localhost:8080/launch/1`, when he request for launch item with id=1 ) 24 | 25 | ## GraphiQL Playground :black_square_button: 26 | 27 | ![](graphiql.png) 28 | 29 | ## GraphiQL Playground DEMO :video_camera: 30 | 31 | ![](GraphiQL-demo.gif) 32 | 33 | 34 | ## Installation :wrench: 35 | 36 | 1. Clone this repo by running `git clone https://github.com/imranhsayed/graphql-react-app` 37 | 2. `npm install` 38 | 3. `npm run server` 39 | 40 | ## Useful Links :link: 41 | 42 | 1. [Express GraphQL github link](https://github.com/graphql/express-graphql) 43 | 2. [SpaceX-API](https://github.com/r-spacex/SpaceX-API) 44 | 3. [SpaceX-Docs](https://docs.spacexdata.com/) 45 | 4. [Apollo GraphQL](https://www.apollographql.com/docs/react/) 46 | Appollo Client is way to use GraphQL to build client applications. It helps you build a UI that fetches data with GraphQL, and can be used with any JavaScript front-end. 47 | 48 | 49 | ## Instructions :point_right: 50 | 51 | * Graphiql is a tool that we can use as a client to make request to our server. 52 | * Graph Ql will be avialable at `localhost:5000/graphql` 53 | 54 | ## Common Commands :computer: 55 | 56 | 1. `npm run dev:webpack` runs webpack-dev-server for frontend on port 8080 in watch mode 57 | 2. `npm run server` runs node server for backend on `localhost:5000/graphql` 58 | 3. `npm run dev` would run both front end and backend servers on their respective ports, using concurrently 59 | 4. `start` Runs the server at `localhost:5000/graphql` in non watch mode 60 | 61 | ## Built With :zap: 62 | 63 | 1. Node 64 | 2. Express 65 | 3. React 66 | 4. GraphQL 67 | 5. Appollo Client 68 | 6. Webpack 69 | 7. Babel 70 | 71 | ## License 72 | 73 | [![License](http://img.shields.io/:license-mit-blue.svg?style=flat-square)](http://badges.mit-license.org) 74 | 75 | - **[MIT license](http://opensource.org/licenses/mit-license.php)** 76 | -------------------------------------------------------------------------------- /graphiql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/graphql-react-app/b4ff87d3ac03884fe3eb5ac3ef0879c88c8fc265/graphiql.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-react-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "server": "nodemon server.js", 9 | "webpack-dev-server": "webpack-dev-server", 10 | "dev:webpack": "webpack-dev-server --mode=development", 11 | "dev": "concurrently \"npm run server\" \"npm run dev:webpack\"", 12 | "prod": "webpack --mode=production" 13 | }, 14 | "keywords": [], 15 | "author": "Imran Sayed", 16 | "license": "ISC", 17 | "dependencies": { 18 | "@reach/router": "^1.2.1", 19 | "apollo-boost": "^0.3.1", 20 | "axios": "^0.18.0", 21 | "concurrently": "^4.1.0", 22 | "cors": "^2.8.5", 23 | "css-loader": "^2.1.1", 24 | "express": "^4.16.4", 25 | "express-graphql": "^0.8.0", 26 | "file-loader": "^3.0.1", 27 | "graphql": "^14.3.0", 28 | "graphql-tag": "^2.10.1", 29 | "json-server": "^0.14.2", 30 | "react": "^16.8.6", 31 | "react-apollo": "^2.5.5", 32 | "react-dom": "^16.8.6", 33 | "style-loader": "^0.23.1" 34 | }, 35 | "devDependencies": { 36 | "nodemon": "^1.19.0", 37 | "@babel/core": "^7.4.3", 38 | "@babel/plugin-proposal-class-properties": "^7.4.0", 39 | "@babel/preset-env": "^7.4.3", 40 | "@babel/preset-react": "^7.0.0", 41 | "babel-loader": "^8.0.5", 42 | "html-webpack-plugin": "^3.2.0", 43 | "path": "^0.12.7", 44 | "webpack": "^4.29.6", 45 | "webpack-cli": "^3.3.0", 46 | "webpack-dev-server": "^3.2.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | SpaceX Launches 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /schema.js: -------------------------------------------------------------------------------- 1 | const axios = require( 'axios' ); 2 | 3 | const { 4 | GraphQLObjectType, 5 | GraphQLInt, 6 | GraphQLString, 7 | GraphQLBoolean, 8 | GraphQLList, 9 | GraphQLSchema 10 | } = require( 'graphql' ); 11 | 12 | // RocketType 13 | const RocketType = new GraphQLObjectType({ 14 | name: 'Rocket', 15 | fields: () => ({ 16 | rocket_id: { type: GraphQLString }, 17 | rocket_name: { type: GraphQLString }, 18 | rocket_type: { type: GraphQLString }, 19 | }) 20 | }); 21 | 22 | // Launch type 23 | const LaunchType = new GraphQLObjectType({ 24 | name: 'Launch', 25 | fields: () => ({ 26 | flight_number: { type: GraphQLInt }, 27 | mission_name: { type: GraphQLString }, 28 | launch_year: { type: GraphQLString }, 29 | launch_date_local: { type: GraphQLString }, 30 | launch_success: { type: GraphQLBoolean }, 31 | rocket: { type: RocketType }, 32 | }) 33 | }); 34 | 35 | // Root Query 36 | const RootQuery = new GraphQLObjectType({ 37 | name: 'RootQueryType', 38 | fields: { 39 | // All Launches data 40 | launches: { 41 | type: new GraphQLList( LaunchType ), 42 | resolve( parent, args ){ 43 | return axios.get( 'https://api.spacexdata.com/v3/launches' ) 44 | .then( res => res.data ); 45 | } 46 | }, 47 | // Single Launch data with id 48 | launch: { 49 | type: LaunchType, 50 | args: { 51 | flight_number: { type: GraphQLInt } 52 | }, 53 | resolve( parent, args ) { 54 | return axios.get( `https://api.spacexdata.com/v3/launches/${args.flight_number}` ) 55 | .then( res => res.data ); 56 | } 57 | }, 58 | // All Rockets data 59 | rockets: { 60 | type: new GraphQLList( RocketType ), 61 | resolve( parent, args ){ 62 | return axios.get( 'https://api.spacexdata.com/v3/rockets' ) 63 | .then( res => res.data ); 64 | } 65 | }, 66 | // Single Rocket data with id 67 | rocket: { 68 | type: RocketType, 69 | args: { 70 | flight_number: { type: GraphQLInt } 71 | }, 72 | resolve( parent, args ) { 73 | return axios.get( `https://api.spacexdata.com/v3/launches/${args.id}` ) 74 | .then( res => res.data ); 75 | } 76 | } 77 | } 78 | }); 79 | 80 | module.exports = new GraphQLSchema( { 81 | query: RootQuery 82 | } ); 83 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const graphqlHTTP = require('express-graphql'); 3 | const cors = require( 'cors' ); 4 | const MyGraphQLSchema = require( './schema' ); 5 | 6 | const app = express(); 7 | 8 | // Allow Cross origin 9 | app.use( cors() ); 10 | 11 | app.use('/graphql', graphqlHTTP({ 12 | schema: MyGraphQLSchema, 13 | graphiql: true 14 | })); 15 | 16 | const PORT = process.env.PORT || 5000; 17 | 18 | app.listen(PORT, () => console.log( `Server started on Port ${PORT}` )); 19 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Logo from './logo.png'; 3 | import './Style.css'; 4 | 5 | import ApolloClient from 'apollo-boost'; 6 | import {ApolloProvider} from "react-apollo"; 7 | import Launches from "./components/Launches"; 8 | import Launch from './components/Launch'; 9 | import { Router } from "@reach/router"; 10 | 11 | const client = new ApolloClient({ 12 | uri: 'http://localhost:5000/graphql' 13 | }); 14 | 15 | class App extends React.Component { 16 | render() { 17 | return( 18 | 19 |
20 |
21 | Logo 22 |
23 | 24 | 25 | 26 | 27 |
28 |
29 | 30 | ) 31 | } 32 | } 33 | 34 | export default App; 35 | -------------------------------------------------------------------------------- /src/Style.css: -------------------------------------------------------------------------------- 1 | .logo { 2 | width: 500px; 3 | margin: 0 auto; 4 | display: flex; 5 | } 6 | 7 | .launch-loader { 8 | position: absolute; 9 | top: 0; 10 | bottom: 0; 11 | right: 0; 12 | left: 0; 13 | margin: auto; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Launch.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import gql from 'graphql-tag'; 3 | import { Query } from "react-apollo"; 4 | import Loading from '../loader.gif'; 5 | import { Link } from "@reach/router"; 6 | 7 | const LAUNCH_QUERY = gql` 8 | query LaunchQuery( $flight_number: Int! ) { 9 | launch( flight_number: $flight_number ) { 10 | flight_number 11 | mission_name 12 | launch_year 13 | launch_success 14 | launch_date_local, 15 | rocket { 16 | rocket_id 17 | rocket_name 18 | rocket_type 19 | } 20 | } 21 | } 22 | `; 23 | 24 | class Launch extends React.Component { 25 | render() { 26 | const flight_number = parseInt( this.props.id ); 27 | 28 | return( 29 | 30 | 31 | { 32 | ( { loading, error, data } ) => { 33 | // Show loader until the request is complete. 34 | if ( loading ) { 35 | return Loader 36 | } 37 | // If error. 38 | if ( error ) { 39 | console.warn( error ); 40 | } 41 | 42 | const { mission_name, flight_number, launch_year, launch_success, rocket } = data.launch; 43 | 44 | // Display Data 45 | return ( 46 |
47 | {/* Mission name */} 48 |

49 | Mission: { mission_name } 50 |

51 | 52 | {/*Launch Details*/} 53 |

Launch Details:

54 |
    55 |
  • Flight Number: { flight_number }
  • 56 |
  • Launch Year: { launch_year }
  • 57 |
  • 58 | Launch Successful: 59 | { 60 | launch_success 61 | ? Yes 62 | : No 63 | } 64 |
  • 65 |
  • Flight Number: { flight_number }
  • 66 |
67 | 68 | {/* Rocket Details */} 69 |

Rocket Details:

70 |
    71 |
  • Rocket ID: { rocket.rocket_id }
  • 72 |
  • Rocket Name: { rocket.rocket_name }
  • 73 |
  • Rocket Type: { rocket.rocket_type }
  • 74 |
75 | 76 |
77 | 78 | {/* Back to Home Button */} 79 | Back 80 |
81 | ) 82 | } 83 | } 84 |
85 |
86 | ); 87 | } 88 | } 89 | export default Launch; 90 | -------------------------------------------------------------------------------- /src/components/LaunchItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from "@reach/router"; 3 | 4 | export default ( props ) => { 5 | const { data } = props; 6 | 7 | return ( 8 | 9 | { data.launches.map( item => { 10 | console.warn( item.flight_number ); 11 | return ( 12 |
13 |
14 |

{item.flight_number}. 15 | Mission: 16 | {item.mission_name} 17 |

18 |

Date: {item.launch_date_local}

19 |
20 |
21 | Details 22 |
23 |
24 | ) 25 | } ) } 26 |
27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Launches.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import gql from 'graphql-tag'; 3 | import { Query } from "react-apollo"; 4 | import LaunchItem from './LaunchItem'; 5 | import MissionKey from './MissionKey'; 6 | import Loader from '../loader.gif'; 7 | 8 | const LAUNCHES_QUERY = gql` 9 | query LaunchesQuery { 10 | launches { 11 | flight_number, 12 | mission_name, 13 | launch_date_local, 14 | launch_success 15 | } 16 | } 17 | `; 18 | 19 | class Launches extends React.Component { 20 | render() { 21 | return( 22 | 23 |

Launches

24 | 25 | 26 | { ( {loading, error, data} ) => { 27 | if ( loading ) { 28 | return Loader 29 | } 30 | if ( error ) { 31 | console.warn( error ); 32 | } 33 | console.warn( data ); 34 | return 35 | } } 36 | 37 |
38 | ); 39 | } 40 | } 41 | 42 | export default Launches; 43 | -------------------------------------------------------------------------------- /src/components/MissionKey.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => ( 4 |
5 |

6 | = Success 7 |

8 |

9 | = Fail 10 |

11 |
12 | ) 13 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App.js'; 4 | 5 | ReactDOM.render( , document.getElementById( 'root' ) ); 6 | -------------------------------------------------------------------------------- /src/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/graphql-react-app/b4ff87d3ac03884fe3eb5ac3ef0879c88c8fc265/src/loader.gif -------------------------------------------------------------------------------- /src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imranhsayed/graphql-react-app/b4ff87d3ac03884fe3eb5ac3ef0879c88c8fc265/src/logo.png -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebPackPlugin = require( 'html-webpack-plugin' ); 2 | const path = require( 'path' ); 3 | module.exports = { 4 | context: __dirname, 5 | entry: './src/index.js', 6 | output: { 7 | path: path.resolve( __dirname, 'dist' ), 8 | filename: 'main.js', 9 | publicPath: '/', 10 | }, 11 | devServer: { 12 | historyApiFallback: true 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | use: 'babel-loader', 19 | }, 20 | { 21 | test: /\.css$/, 22 | use: ['style-loader','css-loader'], 23 | }, 24 | { 25 | test: /\.(png|jp?g|svg|gif)$/, 26 | use: [{ 27 | loader: "file-loader", 28 | }] 29 | } 30 | ] 31 | }, 32 | plugins: [ 33 | new HtmlWebPackPlugin({ 34 | template: path.resolve( __dirname, 'public/index.html' ), 35 | filename: 'index.html' 36 | }) 37 | ] 38 | }; 39 | --------------------------------------------------------------------------------