CS174
Chris Pollett
Nov 30, 2022
mkdir graphql cd graphql npm init #go through prompts to make initial package.json file npm install nodemon #will use this to avoid having to restart server # when make changes to code npm install express --save npm install apollo-server-express --save
{ "name": "graphql", "version": "1.0.0", "description": "", "main": "index.js", "type": "module", //run as module "scripts": { "start": "node src/index.js", //run not in production mode "dev": "nodemon src/index.js", //run in dev mode //(slower, but don't need to restart on change) "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "apollo-server-express": "^3.11.1", "express": "^4.18.2", "nodemon": "^2.0.20" } }
import { ApolloServer, gql } from 'apollo-server-express'; import express from 'express'; const port = process.env.PORT || 8888; /* typedefs are used by the gql module to specify the types for the field in JSON return by a GraphQL query */ const typeDefs = gql` type Query { hello: String } `; /* resolvers are used to specify functions to handle filling in the values for fields on a GraphQL query. */ const resolvers = { Query : { hello: () => 'Hello there!' } }; const app = express(); // Create and start Apollo Server const server = new ApolloServer({typeDefs, resolvers}); await server.start() server.applyMiddleware( {app, path: '/api'} ); // Get Express app to lister on port 8888 app.listen( {port }, () => console.log(`GraphQL Example on ${port}${server.graphqlPath}`));
npm run dev # if wanted to run production app code type npm run start
type Note { id: ID! content: String! author: String! } type Query { hello: String notes: [Note!]! //idea is resolver for this field will return all notes // the exclamation point inside the [] indicates that we must say which subfield we want note (id: ID): Note! //idea is resolver for this //field will return a particular note }
import { ApolloServer, gql } from 'apollo-server-express'; import express from 'express'; const port = process.env.PORT || 8888; let notes = [ {id: '1', content: 'First note', author: 'bob'}, {id: '2', content: 'Another note', author: 'sally'}, {id: '3', content: 'super note', author: 'alice'}, ]; const typeDefs = gql` type Note { id: ID! content: String! author: String! } type Query { hello: String notes: [Note!]! note(id: ID): Note! } type Mutation { newNote(content: String!): Note! } `; const resolvers = { Query : { // these are the handlers for queries hello: () => 'Hello there!', notes: () => notes, note: (parent, args) => { return notes.find(note => note.id === args.id) } }, Mutation: { newNote: (parent, args) => { let noteValue = { id: String(notes.length + 1), content: args.content, author: "Chris Pollett" }; notes.push(noteValue); return noteValue; } } }; const app = express(); const server = new ApolloServer({typeDefs, resolvers}); await server.start() server.applyMiddleware( {app, path: '/api'} ); app.listen( {port }, () => console.log(`GraphQL Example on ${port}${server.graphqlPath}`));
npx create-react-app react-test
cd react-test npm start
import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App;
import './App.css'; function App() { const name = 'Chris'; const now = String(new Date()); return ( <div className="App"> <p>Hello {name}!</p> <p>The current time is {now}</p> <p>I can also evaluate expressions such as 2+(3*5): {2+(3*5)}.</p> </div> ); } export default App;
npm install @apollo/client graphql --save
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client' const client = new ApolloClient({ uri: "http://localhost:8888/api", cache: new InMemoryCache(), }) const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <ApolloProvider client={client}> <App /> </ApolloProvider> );
import { useQuery, gql } from "@apollo/client"; const GET_LISTS = gql` query { notes { id content } } `; function App() { const { data, loading, error } = useQuery(GET_LISTS); if (loading) return 'Loading...'; if (error) return `Error! ${error.message}`; return ( <div> { data.results.map(item => { return( <div key={item.id} style={{display: 'flex', flexDirection: 'row'}}> <h2>{item.id}. </h2> <h2>{item.content}</h2> </div> ) }) } </div> ) } export default App;
npm install --save react-router react-router-dom
import { BrowserRouter, Link, Route, Routes, useParams,} from 'react-router-dom'; import React from 'react'; /* This page has multiple components on it we could have split this across multiple files and imported them. */ /* This component shows react components can have forms. It also shows a crude way to get the values off forms. You could then send these values as a GraphQL query */ class Home extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(event) { let user_name = document.getElementById('user-name').value; alert(user_name); event.preventDefault(); } render() { return ( <div className="app"> {/* We can enclosed a C comment in braces to make a JSX comment */} <h1>Home Page Body</h1> <form onSubmit={this.handleSubmit}> <p><label for="user-name">User Name:</label> <input id="user-name" type="text"/> <button type="submit">Submit</button> </p> </form> </div> ); } }; /* Here is a functional way to declare a component... Notice a previously defined component can be used as a tag in another component. This allows one to have code that looks more like HTML rather than have to call functions from your scripting language which render elements. */ const AnotherHome = () => { return ( <div className="app"> <h1>About Page</h1> <p>Demoing we can use tags we have just defined...</p> <Home /> </div> ); }; /* We are going to use the number component below to show how to bind url path variables to Javascript variables */ const Number = () => { const { my_number1, my_number2 } = useParams(); return ( <div className="app"> <h1>MyNumber {my_number1} {my_number2}</h1> </div> ); }; // We'll render this component on failure to find a Route const NotFound = () => { return ( <div className="app"> <h1>Page not found</h1> </div> ); }; const App = () => ( <BrowserRouter> <div> {/*Notice how make links in React for routing */} <Link to="/">Home</Link>{' '} <Link to='/number/5/6'>Number-5-6</Link>{' '} <Link to='/another_home'>Another Home</Link>{' '} {/*React renders first route which matches */} <Routes> <Route exact path="/" element={<Home />} /> <Route path="/another_home" element={<AnotherHome />} /> {/*notice this route matches paths like /number/1/4 */} <Route path="/number/:my_number1/:my_number2" element={<Number />} /> {/*notice the wildcard in this route */} <Route path="/*" element={<NotFound />} /> </Routes> </div> </BrowserRouter> ); export default App;