Relationships
In a relational database you express links with foreign keys and join tables. In GraphQL you express the same links far more directly: a relationship is simply a field whose type is another type. There is no special syntax — the type system you already know is all you need. What changes from one relationship to the next is only the shape of the field: a single value, a list, or a list on both sides.
One-to-one and one-to-many
Section titled “One-to-one and one-to-many”The simplest link returns a single object. A Post belongs to exactly one Author, so the field’s type is just Author:
type Post { id: ID! title: String! author: Author!}The reverse direction is one-to-many: one author has many posts. The field type becomes a list, [Post!]!:
type Author { id: ID! name: String! posts: [Post!]!}That is the entire pattern. A “to-one” side returns a type; a “to-many” side returns a list of that type. The same two types can expose the relationship from both ends, letting a client walk in either direction.
Many-to-many
Section titled “Many-to-many”When both sides can have many of the other, you have a many-to-many relationship — say a Post carries several Tags, and each Tag is attached to many posts. In SDL this is just a list field on each type:
type Post { id: ID! title: String! tags: [Tag!]!}
type Tag { id: ID! label: String! posts: [Post!]!}There is no visible “join table” in the schema — that bookkeeping lives in your data layer. The client only sees two types, each with a list field pointing at the other.
flowchart LR Author["type Author"] Post["type Post"] Tag["type Tag"] Author -->|"posts: [Post!]! (one-to-many)"| Post Post -->|"author: Author!"| Author Post -->|"tags: [Tag!]! (many-to-many)"| Tag Tag -->|"posts: [Post!]!"| Post
Resolving the other side
Section titled “Resolving the other side”Declaring the field is half the job; the other half is the resolver that fetches the related data when a client actually selects that field. A resolver for a relationship field receives the parent object and uses its id to look up what is connected. Because GraphQL only runs a resolver when its field is requested, a client that asks for name but not posts never pays the cost of loading posts at all.
Crossing relationships, run for real
Section titled “Crossing relationships, run for real”The schema below wires up a one-to-many (Author → posts) and a many-to-many (Post ↔ Tag), with resolvers that look up the other side by id. The query crosses three relationships in one request. Press Run.
Trace the output: from one author we resolved the one-to-many posts, and inside each post we resolved the many-to-many tags. Each relationship field had its own resolver that looked up the connected records by id. Notice we never wrote a SQL JOIN — the graph is the join, expressed as nested field selections.