Back to Blog
Lesson 9 of the GraphQL: Your First GraphQL Schema & Server course
API ArchitectureJuly 27, 20264 min read

Using Lists for Collections: Mastering GraphQL SDL Arrays

Learn to handle collections in GraphQL using SDL. We cover defining lists, managing nullability, and structuring arrays for robust, typed API contracts.

GraphQLSDLAPI DesignBackend Development
Aesthetic arrangement of vibrant spice jars on an ornate wooden shelf.

Previously in this course, we explored Defining Custom Object Types: Mastering Data Modeling in GraphQL and Implementing Relationships in SDL: Modeling Connected Data. While those lessons focused on single objects, real-world data almost always involves collections. Whether it's a list of users, a series of posts, or an array of tags, your API must be able to return multiple items at once.

In GraphQL, we use Lists to represent these collections. This lesson adds the syntax for arrays to your schema, allowing you to move beyond single-resource endpoints and start modeling complex data sets.

Understanding Lists in GraphQL SDL

In the Schema Definition Language (SDL), a list is defined by wrapping a type in square brackets []. If you want to return multiple String values, you define the field as [String]. If you want to return a collection of your custom User objects, you use [User].

Lists are flexible, but they introduce a specific challenge: nullability. Because GraphQL is strongly typed, you must decide what happens if the list itself is null or if an individual item inside the list is null.

Defining Lists: The Four-Way Matrix

When working with lists, there are four combinations of nullability you need to understand. We control this using the "bang" operator (!).

SyntaxMeaning
[String]The list can be null, and items inside can be null.
[String!]The list can be null, but items inside cannot be null.
[String]!The list cannot be null, but items inside can be null.
[String!]!The list cannot be null, and items inside cannot be null.

In practice, most APIs lean toward [Type!] or [Type!]!. If you are fetching a list of products, you rarely want a list that contains null entries—it’s usually better to have an empty list [] if no data exists.

Worked Example: Building a Blog Schema

Let’s advance our running project. We are building a blog API, and every Author should have a collection of Post objects.

GraphQL
type Post {
  id: ID!
  title: String!
  content: String!
}

type Author {
  id: ID!
  name: String!
  # A list of posts by this author
  # We use [Post!]! because an author's list of posts 
  # shouldn't be null, and each post must exist.
  posts: [Post!]!
}

By defining posts: [Post!]!, we create a strict contract:

  1. The posts field will always return an array (it cannot be null).
  2. Every item in that array is guaranteed to be a valid Post object (no null entries).

Hands-on Exercise

Open your project's schema file. Your task is to update the Query type to support a list of all authors in the system.

  1. Define a field named authors inside your Query type.
  2. Ensure the field returns a non-nullable list of non-nullable Author objects.
  3. Verify your syntax: it should look like authors: [Author!]!.

Common Pitfalls

  • The "Empty List" Trap: Beginners often think that making a list non-nullable ([Type!]!) prevents it from being empty. This is incorrect. A non-nullable list can still be empty ([]). The ! prevents the value from being null, not from being an empty collection.
  • Over-nesting: Avoid [[String]] unless absolutely necessary (e.g., a matrix of data). It makes the client-side code significantly harder to parse and usually indicates that your data model could be flattened.
  • Ignoring Inner Nullability: If you define a field as [Author], a resolver bug that returns [obj, null, obj] will be technically valid according to your schema. This forces the client to perform null checks on every item. Always prefer [Author!] to keep your data clean.

FAQ

Can I pass a list as an argument to a query? Yes. You can use the same square bracket syntax in your arguments list, for example: findPostsByTags(tags: [String!]!): [Post!].

Does this affect performance? Defining a list is just a schema contract. The performance impact depends entirely on your resolver implementation. Fetching a massive list (e.g., 10,000 items) will still be slow, which is why we will eventually look at pagination.

What happens if I return an object instead of a list? The GraphQL engine will throw a validation error. The schema is a strict contract; if you promise [Post!]!, the server must return an array.

Recap

We’ve learned that lists are defined with [] and that nullability is controlled via the ! operator. By using [Type!]!, you ensure a consistent, predictable data structure that protects your frontend from unexpected null values. You've now taken the next step in modeling your API's domain by linking objects into collections.

Up next: We will dive deeper into data integrity by mastering Non-Null fields and how they force your resolvers to handle data consistently.

Similar Posts