Overview
Time to level up from "Hello world" to "Hello playlists"!
In this lesson, we will:
- Examine a mockup design and create a GraphQL schema from it
- Create our first object type
- Learn how to document a GraphQL schema with the
[GraphQLDescription]
attribute
The mockup
To kick things off, let's take a look at this page from the MusicMatcher app:
It showcases a grid of featured popular playlists that you may be interested in!
Breaking each playlist down, we can start to see what pieces of data the client app will need. This process of breaking down a mockup to pieces of data is called schema-first design.
A playlist needs a name and a description, which are both pieces of text.
Even though we're not implementing the schema-first approach in Hot Chocolate (that is, we're not writing the schema in SDL), it's still helpful to think about what types and fields our resolver functions will be defining based on the mockup designs and data that the app developers expect.
The Playlist
type
Let's start with the Playlist
type. In the Types
folder, create a new class called Playlist
.
namespace Odyssey.MusicMatcher;public class Playlist{// Playlist properties go here}
Inside the Playlist
class, we'll start by defining the name of our playlist as a property, which returns a string
type.
public string Name { get; set; }
By default, this field is non-nullable, so a playlist requires a name.
Note: We're using the short-hand syntax for auto-implemented properties.
Right now, we'll get a yellow squiggly line under "Name" and a warning saying:
Non-nullable property 'Name' must contain a non-null value when exiting constructor.Consider declaring the property as nullable.
Don't worry, we'll address that in a bit.
Next, let's define the playlist's description, which is also a string
type. A description can be null
, so we'll mark it as such using the ?
symbol.
public string? Description { get; set; }
One more thing! Even though it wasn't part of the mockup, it's common practice to also define an identifier for a class. Looking ahead, when we click into a playlist, we'll need to have a way to retrieve the details for that specific playlist, and that's exactly what the identifier (or ID) is for.
The ID for a playlist will be a string
type, but we want to map it to a GraphQL scalar type for ID
, not String
. To do this, we'll add the Hot Chocolate attribute [ID]
.
[ID]public string Id { get; }
We're using the [ID]
attribute from the Relay spec because it is a commonly used pattern in GraphQL and the [ID]
attribute is simpler in our code. To use the proper Hot Chocolate types, we could replace [ID]
with [GraphQLType(typeof(IdType))]
.
We've also omitted the set;
method here because we typically don't want the ID to be changed.
The three properties we've defined for a playlist (Id
, Name
and Description
) act as the resolvers for those fields. Behind the scenes, Hot Chocolate converts each property with a get
accessor to a resolver.
Exploring the schema
What does this Playlist
class look like in our GraphQL schema now? Let's find out!
We'll need to register the Playlist
class with our GraphQL server, so open up the Program.cs
file and find where we initialized the server.
We'll chain another method called AddType
and pass in the Playlist
.
builder.Services.AddGraphQLServer().AddQueryType<Query>().AddType<Playlist>();
Save all our changes and restart the server.
Back to Sandbox, let's check out the Schema page. Select Objects on the left-hand side and click Playlist.
Awesome, we've accounted for all the playlist fields!
Right now, the details column shows "No description". Although the field names feel fairly self-explanatory right now, it's good practice to document the types and fields of your schema, especially for consumers of our graph.
Documenting our schema
We use GraphQL descriptions to document a schema. (Not to be confused with our playlist's description field!) In Hot Chocolate, we use the [GraphQLDescription]
attribute, which takes a string
as an argument that describes the type or field.
Back to our Playlist
class, let's add the [GraphQLDescription]
just above the class definitions and the properties.
[GraphQLDescription("A curated collection of tracks designed for a specific activity or mood.")]public class Playlist{[GraphQLDescription("The ID for the playlist.")][ID]public string Id { get; }[GraphQLDescription("The name of the playlist.")]public string Name { get; set; }[GraphQLDescription("Describes the playlist, what to expect and entices the user to listen.")]public string? Description { get; set; }}
Save our changes, restart the server and switch over to Sandbox to see our clear and helpful descriptions!
The Playlist
constructor
We've still got some yellow squiggly lines and warnings in our Playlist
class (two now!), so let's fix those.
We'll create a constructor for the class, passing in id
and name
arguments. We can leave description alone since it's nullable.
public Playlist(string id, string name){Id = id;Name = name;}
We'll use these to create instances of the Playlist
class in the next lesson.
Practice
Id
property in the Playlist
class?Create an Artist
class that produces a GraphQL type with the following fields: id: ID!
, name: String!
, followers: Int
and popularity: Float
. Note the types for each field and their nullability.
Key takeaways
- Breaking down a mockup into data pieces and implementing features based on client application needs is known as schema-first design.
- The
[GraphQLDescription]
attribute is used to add clear and helpful descriptions to GraphQL types and fields. It will be displayed in GraphQL IDEs such as Apollo Sandbox. - The
[ID]
attribute defines the identifier for the GraphQL type. It indicates that the associated property represents a unique identifier.
Up next
We need a way to query for a playlist. Right now, it's just floating in our schema without a way to access it. Let's make it available through our Query
type — our entry point to the schema!
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.