Overview
The data that our resolvers retrieve can come from all kinds of places: a database, a third-party API, webhooks, and so on. These are called data sources. The beauty of GraphQL is that you can mix any number of data sources to create an API that serves the needs of your client applications and graph consumers.
In this lesson, we will:
- Examine the existing Spotify REST API, the perfect data source for MusicMatcher
- Generate a C#
HttpClient
using an OpenAPI specification to handle our HTTP requests, responses and types - Register the
HttpClient
with our GraphQL server
The Spotify Web API
MusicMatcher needs music, and for this course, we're going to be using a lite, pared down version of the official Spotify Web API. We can access that version here: https://spotify-demo-api-fe224840a08c.herokuapp.com/v1/docs/
Let's explore the documentation. We've got a few endpoints available for us centered around playlists– in fact, it's all we need to implement the features we covered earlier!
Diving into the /browse/featured-playlists
endpoint, let's click "Try it out", then "Execute" a request.
We get a JSON object back with two top-level properties: message
and playlists
. Inside the playlists
property, we have an items
property, which contains a list, as well as a few other properties that seem to be related to pagination (like limit
, next
, previous
, offset
). And then the items
list contains objects with its own properties. These objects represent a playlist!
That's a lot of nested properties just to get to what we want – the list of featured playlists!
The shape of the response from our HTTP endpoints often doesn't exactly match what we want to return from our GraphQL schema's types and fields. And that's okay! One of the benefits of GraphQL is that we only expose what the client may need. In this iteration of the schema for example, we only need to expose the name, description and ID of a playlist.
The HTTP responses also come back in JSON (JavaScript Object Notation) format, whereas in .NET we're dealing with classes. For these reasons, we'll need to do some conversions, serializing, and deserializing our HTTP response types to our C# types.
A common way to make HTTP requests and handle responses is with the built-in HttpClient class, as well as the built-in System.Text.Json
package, or the Newtonsoft Json.NET package.
In this course, we want to focus on the GraphQL concepts, not fiddling around manually writing types from a REST API. To help make our work go faster, we're going to auto-generate the code for our HTTP Client handler.
Auto-generating an HTTP Client
We'll use a tool called nswag to generate a C# client based on an OpenAPI specification.
We've already included a swagger.json
file in your project inside the Data
folder. This OpenAPI file defines the endpoints, expected responses, and schemas for response objects.
We'll use nswag
from the command line so let's install it as a local .NET tool.
First, we'll create a manifest file. From the terminal, run:
dotnet new tool-manifest
We should get this message as a result:
The template "Dotnet local tool manifest file" was created successfully.
Then, install the nswag
tool.
dotnet tool install NSwag.ConsoleCore --version 14.0.1
With a successful install, we'll see this message:
You can invoke the tool from this directory using the following commands: 'dotnet tool run nswag' or 'dotnet nswag'.Tool 'nswag.consolecore' (version '14.0.1') was successfully installed. Entry is added to the manifest file [/path/to/dotnet-tools.json].
Finally, we'll run the command to use nswag
.
dotnet nswag openapi2csclient \/input:Data/swagger.json \/classname:SpotifyService \/namespace:SpotifyWeb \/output:Data/SpotifyService.cs
This command:
- Takes the
swagger.json
file as an input - Generates a C# client with the classname
SpotifyService
- Outputs it into
Data/SpotifyService.cs
under the new namespaceSpotifyWeb
Note that we've specified a different namespace from where our GraphQL server and types are defined (which is Odyssey.MusicMatcher
). This is to avoid type naming collisions from our HTTP response object types and our GraphQL schema types.
The terminal output should look something like this:
NSwag command line tool for .NET Core Net70, toolchain v13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))Visit http://NSwag.org for more information.NSwag bin directory: /Users/apollo/.nuget/packages/nswag.consolecore/13.20.0/tools/net7.0/anyCode has been successfully written to file.
Let's take a look at that newly created file SpotifyService.cs
, in the Data
folder!
Right off the top, we see a comment that the file contents have been autogenerated. Scrolling down to the constructor, we can see that this class is using HttpClient
.
If we try to run our project now, we'll see a list of errors complaining about the namespace called Newtonsoft
. We'll need to install this package for our SpotifyService
to work.
dotnet add package Newtonsoft.Json
With that, all our errors should disappear!
Feel free to examine the SpotifyService
class more thoroughly if you're curious. We won't be diving into the details of every single method and line here. However, we can rest assured that this service will manage the specifics of sending HTTP requests to the Spotify REST API and translating requests into C# classes and types that we'll be able to work with. Handy!
Adding to our GraphQL server
Let's connect this service to our GraphQL server, back in Program.cs
.
At the top, we'll import the namespace for SpotifyWeb
.
using SpotifyWeb;
We'll see an error pop up under the AddType<Playlist>
symbol.
'Playlist' is an ambiguous reference between 'Odyssey.MusicMatcher.Playlist' and 'SpotifyWeb.Playlist'CS0104
Remember the Playlist
class we defined earlier on in the Types
folder? The autogenerated SpotifyService
class has defined the same type, with the same name! This is why we specified a different namespace when we ran the nswag
command, to avoid naming collisions.
But now, AddType<Playlist>
is ambiguous; are we referring to SpotifyWeb.Playlist
or Odyssey.MusicMatcher.Playlist
? We're referring to the latter, so we should change it to AddType<Odyssey.MusicMatcher.Playlist>
.
However, we can also remove that method altogether, since it's not needed. We added it earlier on when we had no way of reaching the Playlist
object through the Query
type, just to see what it would look like in our GraphQL schema reference. But now that the Playlist
object is reachable from the Query.featuredPlaylists
field, we don't need it anymore!
Let's go ahead and remove it. Don't forget to leave that ;
to end the statement just after the AddQueryType
call.
builder.Services.AddGraphQLServer().AddQueryType<Query>();- .AddType<Playlist>;
Let's connect our SpotifyService
with the server. We'll use the AddHttpClient
method for this, just above the GraphQL server methods.
builder.Services.AddHttpClient<SpotifyService>();
Note that this line is separate from the AddGraphQLServer()
methods.
Our resolver functions should now have access to the SpotifyService
using dependency injection. Before moving on, we'll tidy up our resolver functions with one more configuration. Chaining onto our GraphQL server methods, we'll use the RegisterService
method and pass in the SpotifyService
class.
builder.Services.AddGraphQLServer().AddQueryType<Query>().RegisterService<SpotifyService>();
We'll see how exactly this affects our resolver functions (and keeps them tidier!) in the next lesson.
Practice
nswag
?AddHttpClient<SpotifyService>()
configuration in the GraphQL server setup accomplish?SpotifyService
specified differently from the GraphQL server's namespace?Key takeaways
- GraphQL allows the use of various data sources, such as databases, REST APIs, and webhooks, to create a flexible API that caters to client application needs.
- A GraphQL schema does not need to follow the shape, pattern, or naming of the data sources it uses.
- We can use the
nswag
tool to auto-generate a C# HTTP Client based on an OpenAPI specification, simplifying the handling of HTTP requests and responses. - The
SpotifyService
is registered with the GraphQL server using theAddHttpClient
andRegisterService
methods, making it available via dependency injection in resolver functions.
Up next
Let's put our SpotifyService
to work and get that list of featured playlists!
Share your questions and comments about this lesson
This course is currently in
You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.