Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Gentle Introduction to GraphQL Resolvers

Steve Kinney
January 28, 2020
140

A Gentle Introduction to GraphQL Resolvers

Steve Kinney

January 28, 2020
Tweet

Transcript

  1. A Gentle Introduction
    to Resolvers
    Steve Kinney

    View Slide

  2. @stevekinney
    Hi, I’m Steve.
    (@stevekinney)

    View Slide

  3. @stevekinney
    What even is a resolver?
    • Short answer: It’s a function or method that resolves a
    value for a type or field in your schema.
    • Every field on every type is backed by a function called a
    resolver.

    View Slide

  4. @stevekinney
    Resolving values and objects
    • If the return value of a function is a literal (e.g. a string or
    a number or a boolean). Cool, we’re done here.
    • Otherwise, if it’s an object, we’re going to dip down into
    that object and try to resolve it as well.
    • We’ll keep diving deeper until we’ve gotten to the bottom
    of things.

    View Slide

  5. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    type Artist {
    id: Int
    name: String
    }
    type Query {
    artists: [Artist!]
    }

    View Slide

  6. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    query {
    artists {
    id
    name
    }
    }

    View Slide

  7. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const resolvers = {
    Query: {
    artists() {
    return [{ id: 1, name: 'The National' }];
    }
    }
    };

    View Slide

  8. @stevekinney
    Resolvers can be
    asynchronous.

    View Slide

  9. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const resolvers = {
    Query: {
    artists() {
    return [...artists];
    },
    albums() {
    return new Promise(resolve => resolve([...albums]));
    },
    songs() {
    return axios.get(‘/some-endpoint')
    .then(response => response.data);
    }
    }
    };

    View Slide

  10. @stevekinney
    Siblings are executed in
    parallel.

    View Slide

  11. @stevekinney
    Query
    Artists Albums
    ID Name Title Year ID

    View Slide

  12. @stevekinney
    If a resolver returns a promise, we’re
    obviously going to have to—umm—
    resolve wait for it before diving down.

    View Slide

  13. @stevekinney
    Query
    Artists
    Albums ID Name
    Title Year ID

    View Slide

  14. @stevekinney

    View Slide

  15. @stevekinney
    We should probably use GraphQL for this
    problem, right?
    • A user might want to pull up all of the artists in their
    library with their albums.
    • They might just want to see a list of albums.
    • Or, maybe, they just want to a big ol’ list of songs.
    • (We’re going to ignore playlists for now.)

    View Slide

  16. @stevekinney
    What might our schema look
    like?

    View Slide

  17. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    type Artist {
    id: Int
    name: String
    albums: [Album]
    songs: [Song]
    }

    View Slide

  18. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    type Album {
    id: Int
    title: String
    year: Int
    artist: Artist
    songs: [Song]
    }

    View Slide

  19. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    type Song {
    id: Int
    title: String
    time: Int
    artist: Artist
    album: Album
    }

    View Slide

  20. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    type Query {
    artists: [Artist!]
    albums: [Album!]
    songs: [Song!]
    artist(id: Int!): Artist!
    album(id: Int!): Album!
    song(id: Int!): Song!
    }
    type Mutation {
    addArtist(name: String!): Artist
    addAlbum(title: String!, year: Int, artistId: Int): Album
    addSong(title: String!, time: Int, artistId: Int, albumId: Int): Song
    }

    View Slide

  21. @stevekinney
    Let’s talk about our data.

    View Slide

  22. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    [
    {
    "id": 1,
    "name": "The National"
    },
    {
    "id": 2,
    "name": "Camp Cope"
    },
    {
    "id": 3,
    "name": "Rilo Kiley"
    }
    ]

    View Slide

  23. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    [
    {
    "id": 1,
    "artistId": 1,
    "title": "Trouble Will Find Me",
    "year": 2013
    },
    {
    "id": 2,
    "artistId": 2,
    "title": "How to Socialize and Make Friends",
    "year": 2018
    }
    ]

    View Slide

  24. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    [
    {
    "title": "Done",
    "time": "4:47",
    "artistId": 2,
    "albumId": 5,
    "id": 1
    },
    {
    "title": "Flesh & Electricity",
    "time": "3:57",
    "artistId": 2,
    "albumId": 5,
    "id": 2
    },
    {
    "title": "West Side Story",
    "time": "5:32",
    "artistId": 2,
    "albumId": 5,
    "id": 3
    },
    ]

    View Slide

  25. @stevekinney
    Alright, let’s get into writing
    a resolver.

    View Slide

  26. @stevekinney
    Resolver functions get four arguments.
    • root or parent
    • args
    • context
    • info

    View Slide

  27. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const resolvers = {
    Query: {
    artists() {
    return [{ id: 1, name: 'The National' }];
    }
    }
    };

    View Slide

  28. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const resolvers = {
    Query: {
    artists(root, args, context, info) {
    return [{ id: 1, name: 'The National' }];
    }
    }
    };

    View Slide

  29. @stevekinney
    So, when would you use
    args?

    View Slide

  30. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    type Query {
    artists: [Artist!]
    artist(id: Int!): Artist!
    }

    View Slide

  31. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    query {
    artist(id: 1) {
    id
    name
    }
    }

    View Slide

  32. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const resolvers = {
    Query: {
    // …
    artist(root, args) {
    return artists.find(artist => artist.id === args.id);
    }
    // …
    }
    };

    View Slide

  33. @stevekinney
    What about descending
    down the tree?

    View Slide

  34. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    type Artist {
    id: Int
    name: String
    albums: [Album]
    songs: [Song]
    }

    View Slide

  35. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    Query: {
    // …
    Artist: {
    albums(artist) { // artist is the parent value
    return albums.filter(album => album.artistId === artist.id);
    },
    songs(artist) {
    return songs.filter(song => song.artistId === artist.id);
    }
    },
    // …
    }
    };

    View Slide

  36. @stevekinney
    By default, resolvers will look to see
    if whatever you’re looking for is a
    property on the object.

    View Slide

  37. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const resolvers = {
    Artist: {
    // You don't need to do this.
    name(artist) {
    return artist.name;
    },
    albums(artist) {
    return albums.filter(album => album.artistId === artist.id);
    },
    songs(artist) {
    return songs.filter(song => song.artistId === artist.id);
    }
    }
    };

    View Slide

  38. And now…
    @stevekinney
    Mutations

    View Slide

  39. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const resolvers = {
    Mutation: {
    addArtist(_, args) {
    const artist = {
    ...args,
    id: generateId()
    };
    artists.push(artist);
    return artist;
    }
    }
    };

    View Slide

  40. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const resolvers = {
    Mutation: {
    addArtist(_, args) {
    return axios.post('/artists', args)
    .then(response => response.data);
    }
    }
    };

    View Slide

  41. And now…
    @stevekinney
    What about context?

    View Slide

  42. @stevekinney
    What is context?
    • Short answer: It’s an object shared by all of the resolvers.
    • It's mutable.
    • It's destroyed between every request.

    View Slide

  43. @stevekinney
    What might you store in context?
    • Authentication data
    • The request object in something like Express
    • API fetching functions
    • Database connections

    View Slide

  44. Play: https://graph-jukebox.glitch.me/ Edit: https://glitch.com/edit/#!/graph-jukebox
    const server = new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ req }) => ({
    authScope: getScope(req.headers.authorization)
    })
    });
    // Resolver
    (root, args, context) => {
    if (context.authScope !== ADMIN) {
    throw AuthenticationError('not admin');
    }
    // More stuff…
    };

    View Slide

  45. @stevekinney
    Let’s poke around the working version of the
    application, shall we?
    • https://glitch.com/edit/#!/graph-jukebox
    • Let’s look at how to add additional properties.
    • Let’s look at an example that makes AJAX requests.

    View Slide

  46. @stevekinney
    Fin.

    View Slide