Skip to content

Commit e5dd8f8

Browse files
committed
part8e betterment
1 parent a2bd592 commit e5dd8f8

File tree

2 files changed

+137
-103
lines changed

2 files changed

+137
-103
lines changed

src/content/8/en/part8e.md

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -360,13 +360,15 @@ and the file <i>index.js</i> changes to:
360360

361361
```js
362362
const { ApolloServer } = require('@apollo/server')
363+
// highlight-start
363364
const { expressMiddleware } = require('@apollo/server/express4')
364365
const { ApolloServerPluginDrainHttpServer } = require('@apollo/server/plugin/drainHttpServer')
365366
const { makeExecutableSchema } = require('@graphql-tools/schema')
366367
const express = require('express')
367368
const cors = require('cors')
368369
const bodyParser = require('body-parser')
369370
const http = require('http')
371+
// highlight-end
370372

371373
const jwt = require('jsonwebtoken')
372374

@@ -398,8 +400,7 @@ const start = async () => {
398400
const httpServer = http.createServer(app)
399401

400402
const server = new ApolloServer({
401-
typeDefs,
402-
resolvers,
403+
schema: makeExecutableSchema({ typeDefs, resolvers }),
403404
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
404405
})
405406

@@ -461,14 +462,13 @@ So when a new person is added, all of its details are sent to all subscribers.
461462
First, we have to install two packages for adding subscriptions to GraphQL and a Node.js WebSocket library:
462463

463464
```
464-
npm install graphql-subscriptions ws graphql-ws
465+
npm install graphql-ws ws @graphql-tools/schema
465466
```
466467

467468
The file <i>index.js</i> is changed to
468469

469470
```js
470471
// highlight-start
471-
const { execute, subscribe } = require('graphql')
472472
const { WebSocketServer } = require('ws')
473473
const { useServer } = require('graphql-ws/lib/use/ws')
474474
// highlight-end
@@ -479,39 +479,30 @@ const start = async () => {
479479
const app = express()
480480
const httpServer = http.createServer(app)
481481

482-
const schema = makeExecutableSchema({ typeDefs, resolvers })
483-
484-
// highlight-start
482+
// highlight-start
485483
const wsServer = new WebSocketServer({
486484
server: httpServer,
487485
path: '/',
488486
})
489-
490-
const serverCleanup = useServer({ schema }, wsServer)
487+
// highlight-end
488+
489+
// highlight-start
490+
const schema = makeExecutableSchema({ typeDefs, resolvers })
491+
const serverCleanup = useServer({ schema }, wsServer);
491492
// highlight-end
492493

493494
const server = new ApolloServer({
494495
schema,
495-
context: async ({ req }) => {
496-
const auth = req ? req.headers.authorization : null
497-
if (auth && auth.toLowerCase().startsWith('bearer ')) {
498-
const decodedToken = jwt.verify(auth.substring(7), JWT_SECRET)
499-
const currentUser = await User.findById(decodedToken.id).populate(
500-
'friends'
501-
)
502-
return { currentUser }
503-
}
504-
},
505496
plugins: [
506497
ApolloServerPluginDrainHttpServer({ httpServer }),
507498
// highlight-start
508499
{
509500
async serverWillStart() {
510501
return {
511502
async drainServer() {
512-
await serverCleanup.dispose()
503+
await serverCleanup.dispose();
513504
},
514-
}
505+
};
515506
},
516507
},
517508
// highlight-end
@@ -520,10 +511,23 @@ const start = async () => {
520511

521512
await server.start()
522513

523-
server.applyMiddleware({
524-
app,
525-
path: '/',
526-
})
514+
app.use(
515+
'/',
516+
cors(),
517+
express.json(),
518+
expressMiddleware(server, {
519+
context: async ({ req }) => {
520+
const auth = req ? req.headers.authorization : null
521+
if (auth && auth.startsWith('Bearer ')) {
522+
const decodedToken = jwt.verify(auth.substring(7), process.env.JWT_SECRET)
523+
const currentUser = await User.findById(decodedToken.id).populate(
524+
'friends'
525+
)
526+
return { currentUser }
527+
}
528+
},
529+
}),
530+
)
527531

528532
const PORT = 4000
529533

@@ -535,18 +539,17 @@ const start = async () => {
535539
start()
536540
```
537541

538-
539542
When queries and mutations are used, GraphQL uses the HTTP protocol in the communication. In case of subscriptions, the communication between client and server happens with [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API).
540543

541544
The above code registers a WebSocketServer object to listen the WebSocket connections, besides the usual HTTP connections that the server listens. The second part of the definition registers a function that closes the WebSocket connection on server shutdown.
545+
If you're interested in more details about configurations, Apollo's [documentation](https://www.apollographql.com/docs/apollo-server/data/subscriptions) explains in relative detail what each line of code does.
542546

543547
WebSockets are a perfect match for communication in the case of GraphQL subscriptions since when WebSockets are used, also the server can initiate the communication.
544548

545549
The subscription _personAdded_ needs a resolver. The _addPerson_ resolver also has to be modified so that it sends a notification to subscribers.
546550

547551
The required changes are as follows:
548552

549-
550553
```js
551554
// highlight-start
552555
const { PubSub } = require('graphql-subscriptions')
@@ -563,16 +566,24 @@ const resolvers = {
563566
const currentUser = context.currentUser
564567

565568
if (!currentUser) {
566-
throw new AuthenticationError("not authenticated")
569+
throw new GraphQLError('not authenticated', {
570+
extensions: {
571+
code: 'BAD_USER_INPUT',
572+
}
573+
})
567574
}
568575

569576
try {
570577
await person.save()
571578
currentUser.friends = currentUser.friends.concat(person)
572579
await currentUser.save()
573580
} catch (error) {
574-
throw new UserInputError(error.message, {
575-
invalidArgs: args,
581+
throw new GraphQLError('Saving user failed', {
582+
extensions: {
583+
code: 'BAD_USER_INPUT',
584+
invalidArgs: args.name,
585+
error
586+
}
576587
})
577588
}
578589

@@ -591,9 +602,13 @@ const resolvers = {
591602
}
592603
```
593604

605+
The following library needs to be installed
594606

607+
```
608+
npm install graphql-subscriptions
609+
```
595610

596-
With subscriptions, the communication happens using the [publish-subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) principle utilizing the object [PubSub](https://www.apollographql.com/docs/apollo-server/data/subscriptions/#the-pubsub-class).
611+
With subscriptions, the communication happens using the [publish-subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) principle utilizing the object [PubSub](https://www.apollographql.com/docs/apollo-server/data/subscriptions#the-pubsub-class).
597612

598613
There is only few lines of code added, but quite much is happening under the hood. The resolver of the _personAdded_ subscription registers and saves info about all the clients that do the subscription. The clients are saved to an
599614
["iterator object"](https://www.apollographql.com/docs/apollo-server/data/subscriptions/#listening-for-events) called <i>PERSON\_ADDED</i> thanks to the following code:
@@ -628,14 +643,16 @@ If the subscription does not work, check that you have correct connection settin
628643

629644
The backend code can be found on [GitHub](https://github.com/fullstack-hy2020/graphql-phonebook-backend/tree/part8-7), branch <i>part8-7</i>.
630645

646+
Implementin gsubscriptions involves a lot of configurations. You will be able to cope with the few exercises of this course without worrying much about the details. If you planning to use subsctiptions in an production use application, you should definitely read carefully Apollo's [documentation on subscriptions](https://www.apollographql.com/docs/apollo-server/data/subscriptions).
647+
631648
### Subscriptions on the client
632649

633650
In order to use subscriptions in our React application, we have to do some changes, especially on its [configuration](https://www.apollographql.com/docs/react/data/subscriptions/).
634651
The configuration in <i>index.js</i> has to be modified like so:
635652

636653
```js
637654
import {
638-
ApolloClient, ApolloProvider, HttpLink, InMemoryCache,
655+
ApolloClient, InMemoryCache, ApolloProvider, createHttpLink,
639656
split // highlight-line
640657
} from '@apollo/client'
641658
import { setContext } from '@apollo/client/link/context'
@@ -656,15 +673,11 @@ const authLink = setContext((_, { headers }) => {
656673
}
657674
})
658675

659-
const httpLink = new HttpLink({
660-
uri: 'http://localhost:4000',
661-
})
676+
const httpLink = createHttpLink({ uri: 'http://localhost:4000' })
662677

663678
// highlight-start
664679
const wsLink = new GraphQLWsLink(
665-
createClient({
666-
url: 'ws://localhost:4000',
667-
})
680+
createClient({ url: 'ws://localhost:4000' })
668681
)
669682
// highlight-end
670683

@@ -703,10 +716,7 @@ npm install @apollo/client graphql-ws
703716
The new configuration is due to the fact that the application must have an HTTP connection as well as a WebSocket connection to the GraphQL server.
704717

705718
```js
706-
const wsLink = new WebSocketLink({
707-
uri: `ws://localhost:4000/graphql`,
708-
options: { reconnect: true }
709-
})
719+
const httpLink = createHttpLink({ uri: 'http://localhost:4000' })
710720

711721
const wsLink = new GraphQLWsLink(
712722
createClient({
@@ -717,7 +727,7 @@ const wsLink = new GraphQLWsLink(
717727

718728
The subscriptions are done using the [useSubscription](https://www.apollographql.com/docs/react/api/react/hooks/#usesubscription) hook function.
719729

720-
Let's modify the code like so:
730+
Let's make the following changes to the code. Add the code defining the order to the file <i>queries.js</i>:
721731

722732
```js
723733
// highlight-start
@@ -729,11 +739,14 @@ export const PERSON_ADDED = gql`
729739
}
730740
${PERSON_DETAILS}
731741
`
732-
// highlight-end
742+
```
743+
744+
and do the subscription in the App component:
745+
746+
```js
747+
748+
import { useQuery, useMutation, useSubscription } from '@apollo/client' // highlight-line
733749

734-
import {
735-
useQuery, useMutation, useSubscription, useApolloClient // highlight-line
736-
} from '@apollo/client'
737750

738751
const App = () => {
739752
// ...

0 commit comments

Comments
 (0)