Bird
Raised Fist0
NestJSframework~15 mins

Object types and input types in NestJS - Deep Dive

Choose your learning style10 modes available
Overview - Object types and input types
What is it?
In NestJS, object types and input types are special classes used to define the shape of data for GraphQL APIs. Object types describe the structure of data that the API sends back to clients, while input types define the shape of data that clients send to the API. They help NestJS understand what fields exist, their types, and how to validate and transform data automatically.
Why it matters
Without object and input types, APIs would not have clear rules about what data they accept or return, leading to confusion and errors. These types ensure data is consistent, validated, and easy to work with, making APIs reliable and easier to maintain. They also enable powerful developer tools like auto-generated documentation and type checking.
Where it fits
Before learning object and input types, you should understand basic TypeScript classes and decorators, and have a basic grasp of GraphQL concepts. After mastering these types, you can learn advanced GraphQL features in NestJS like resolvers, middleware, and schema stitching.
Mental Model
Core Idea
Object types define the data shape sent out, input types define the data shape accepted in, both using classes to describe and validate GraphQL data.
Think of it like...
Think of object types as the menu a restaurant offers to customers (what they get), and input types as the order form customers fill out to request their meal (what they send). Both need clear formats so the kitchen and customers understand each other perfectly.
┌───────────────┐       ┌───────────────┐
│  Input Types  │──────▶│  Resolver     │
│ (Client Data) │       │ (Handles Data)│
└───────────────┘       └───────────────┘
                             │
                             ▼
                      ┌───────────────┐
                      │ Object Types  │
                      │ (API Output)  │
                      └───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Object Types Basics
🤔
Concept: Object types define the structure of data returned by GraphQL queries or mutations.
In NestJS, you create an object type by defining a class and decorating it with @ObjectType(). Each property you want to expose is decorated with @Field() specifying its type. This tells GraphQL what fields are available and their types. Example: import { ObjectType, Field, Int } from '@nestjs/graphql'; @ObjectType() export class User { @Field(type => Int) id: number; @Field() name: string; } This defines a User object with an id and name that GraphQL can return.
Result
GraphQL knows the User type has an id (number) and name (string) fields to send back to clients.
Understanding object types is key because they form the blueprint of data your API sends, ensuring clients know exactly what to expect.
2
FoundationIntroduction to Input Types
🤔
Concept: Input types define the shape of data clients send to the API, such as arguments for mutations.
Input types are similar to object types but are used for incoming data. You create them by defining a class and decorating it with @InputType(). Fields are decorated with @Field() to specify their types. Example: import { InputType, Field } from '@nestjs/graphql'; @InputType() export class CreateUserInput { @Field() name: string; } This input type tells GraphQL what data is expected when creating a user.
Result
GraphQL expects clients to send a name field when calling a mutation that uses CreateUserInput.
Input types help validate and structure incoming data, preventing errors and making APIs safer and clearer.
3
IntermediateUsing Object and Input Types Together
🤔Before reading on: Do you think object types and input types can be the same class or must they always be separate? Commit to your answer.
Concept: Object types and input types often look similar but serve different roles and usually require separate classes.
While object and input types may share fields, they are distinct because input types can have different validation rules or omit fields like IDs. NestJS requires separate classes decorated with @ObjectType() and @InputType() respectively. Example: @ObjectType() class User { id: number; name: string; } @InputType() class CreateUserInput { name: string; } This separation ensures clear API contracts for input and output.
Result
Clear separation prevents confusion and errors when handling data in resolvers.
Knowing that input and object types are separate helps avoid bugs and clarifies API design.
4
IntermediateField Types and Nullable Fields
🤔Before reading on: Do you think all fields in object and input types must always have values? Commit to yes or no.
Concept: Fields can be marked as nullable to indicate they are optional in input or output data.
By default, fields are required. You can make a field optional by passing { nullable: true } to the @Field() decorator. Example: @Field({ nullable: true }) email?: string; This means the email field can be missing or null in the data. Nullable fields are useful for partial updates or optional inputs.
Result
GraphQL schema reflects which fields are optional, improving flexibility and clarity.
Understanding nullable fields prevents errors when clients omit optional data and helps design flexible APIs.
5
IntermediateNested Object and Input Types
🤔
Concept: Object and input types can contain other object or input types as fields to model complex data.
You can nest types by referencing other classes in @Field() decorators. Example: @ObjectType() class Profile { @Field() bio: string; } @ObjectType() class User { @Field() name: string; @Field(type => Profile) profile: Profile; } This models a user with a nested profile object.
Result
GraphQL schema supports complex data structures with nested types.
Knowing how to nest types allows modeling real-world data accurately and cleanly.
6
AdvancedValidation and Transformation with Input Types
🤔Before reading on: Do you think input types automatically validate data or do you need extra setup? Commit to your answer.
Concept: Input types can integrate with validation libraries to automatically check incoming data correctness.
NestJS supports class-validator and class-transformer libraries to validate input types. Example: import { IsEmail, Length } from 'class-validator'; @InputType() class RegisterUserInput { @Field() @Length(3, 20) username: string; @Field() @IsEmail() email: string; } When a mutation receives this input, NestJS validates fields automatically and returns errors if invalid.
Result
Invalid input data is caught early, improving API robustness and user experience.
Integrating validation with input types prevents bad data from reaching business logic, reducing bugs and security risks.
7
ExpertCustom Scalars and Advanced Type Mapping
🤔Before reading on: Can you use any TypeScript type directly in GraphQL fields without extra work? Commit to yes or no.
Concept: GraphQL supports custom scalar types for special data like dates or JSON, which require custom mapping in NestJS object and input types.
By default, GraphQL supports basic types like String, Int, Boolean. For others like Date, you define custom scalars. Example: import { Scalar, CustomScalar } from '@nestjs/graphql'; import { Kind } from 'graphql'; @Scalar('Date') export class DateScalar implements CustomScalar { parseValue(value: string): Date { return new Date(value); } serialize(value: Date): string { return value.toISOString(); } parseLiteral(ast: any): Date { if (ast.kind === Kind.STRING) { return new Date(ast.value); } return null; } } Then use @Field(type => DateScalar) in your types. This allows precise control over complex data types.
Result
Your API can handle complex data types correctly and consistently.
Understanding custom scalars unlocks powerful data handling beyond basic types, essential for real-world APIs.
Under the Hood
NestJS uses TypeScript classes decorated with special decorators to generate GraphQL schema definitions automatically. At runtime, these decorators add metadata about fields and types. The GraphQL module reads this metadata to build the schema, which defines what queries and mutations exist and their input/output types. Input types are used to validate and transform incoming data before it reaches resolver functions. Object types define the shape of data sent back to clients, ensuring type safety and consistency.
Why designed this way?
This design leverages TypeScript's strong typing and decorators to keep schema definitions close to code, reducing duplication and errors. It avoids manual schema writing, which is error-prone and hard to maintain. Using classes aligns with object-oriented programming familiar to many developers, making the learning curve gentler. Alternatives like schema-first require writing GraphQL schema language separately, which can cause mismatches and extra maintenance.
┌───────────────┐  Decorators add metadata  ┌───────────────┐
│ TypeScript    │──────────────────────────▶│ GraphQL       │
│ Classes       │                           │ Schema Builder│
└───────────────┘                           └───────────────┘
        │                                            │
        │ Incoming data validated by input types     │
        │                                            │
        ▼                                            ▼
┌───────────────┐                           ┌───────────────┐
│ Resolver      │◀──────────────────────────│ Client       │
│ Functions     │     Data shaped by object │ (Query/Mutation)
└───────────────┘     types sent back       └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Can you use the same class for both input and object types without issues? Commit to yes or no.
Common Belief:Many believe you can use one class decorated with @ObjectType() for both input and output data.
Tap to reveal reality
Reality:Input and object types must be separate classes decorated with @InputType() and @ObjectType() respectively to avoid schema conflicts and validation issues.
Why it matters:Using the same class causes schema errors and runtime bugs, making APIs unreliable and hard to debug.
Quick: Do you think all fields in input types are optional by default? Commit to yes or no.
Common Belief:Some think input type fields are optional unless explicitly marked required.
Tap to reveal reality
Reality:By default, fields are required unless marked with { nullable: true } in @Field().
Why it matters:Assuming fields are optional can cause unexpected validation errors and client confusion.
Quick: Is it true that NestJS automatically validates input types without any extra setup? Commit to yes or no.
Common Belief:Many believe input types validate data automatically without additional libraries.
Tap to reveal reality
Reality:Validation requires integrating class-validator and enabling validation pipes explicitly.
Why it matters:Without proper setup, invalid data can reach business logic, causing bugs or security issues.
Quick: Can you use any TypeScript type directly in GraphQL fields without custom scalars? Commit to yes or no.
Common Belief:Some think all TypeScript types map automatically to GraphQL types.
Tap to reveal reality
Reality:Only basic types map automatically; complex types like Date need custom scalars.
Why it matters:Ignoring this leads to schema errors or incorrect data serialization.
Expert Zone
1
Input types can extend other input types to reuse fields, but care is needed to avoid circular dependencies.
2
Using partial types with @InputType({ isAbstract: true }) allows creating flexible input shapes for updates.
3
Custom scalars require careful implementation of parse and serialize methods to avoid subtle bugs in data handling.
When NOT to use
Avoid using object and input types for non-GraphQL data handling or REST APIs; use DTOs or plain classes instead. For very dynamic schemas, schema-first approach might be better. Also, if you need runtime schema changes, code-first types are less flexible.
Production Patterns
In production, teams separate input and object types clearly, use validation pipes globally, and implement custom scalars for dates and JSON. They also use abstract input types for partial updates and compose types with inheritance to reduce duplication.
Connections
TypeScript Decorators
Object and input types rely on decorators to add metadata for schema generation.
Understanding decorators helps grasp how NestJS links class definitions to GraphQL schema automatically.
Data Validation
Input types integrate with validation libraries to ensure data correctness before processing.
Knowing validation principles clarifies why input types are crucial for safe API design.
Database Schema Design
Object types often mirror database entities, connecting API data shapes to stored data.
Understanding database schemas helps design object types that align well with backend data models.
Common Pitfalls
#1Using the same class for both input and output types.
Wrong approach:import { ObjectType, Field } from '@nestjs/graphql'; @ObjectType() export class User { @Field() id: number; @Field() name: string; } // Used as input type too, causing schema conflicts
Correct approach:import { ObjectType, Field, InputType } from '@nestjs/graphql'; @ObjectType() export class User { @Field() id: number; @Field() name: string; } @InputType() export class CreateUserInput { @Field() name: string; }
Root cause:Confusing input and output roles leads to schema errors and validation problems.
#2Not marking optional fields as nullable in input types.
Wrong approach:@InputType() class UpdateUserInput { @Field() name?: string; // Missing nullable: true }
Correct approach:@InputType() class UpdateUserInput { @Field({ nullable: true }) name?: string; }
Root cause:Assuming optional TypeScript fields are automatically nullable in GraphQL schema.
#3Skipping validation setup for input types.
Wrong approach:@InputType() class RegisterInput { @Field() email: string; } // No validation decorators or pipes
Correct approach:import { IsEmail } from 'class-validator'; @InputType() class RegisterInput { @Field() @IsEmail() email: string; } // Validation pipe enabled in main.ts
Root cause:Believing NestJS validates inputs automatically without explicit validation integration.
Key Takeaways
Object types define the shape of data your API sends back, while input types define the shape of data your API accepts.
You must use separate classes for object and input types, each decorated appropriately, to avoid schema conflicts.
Nullable fields allow optional data and must be explicitly marked to prevent validation errors.
Input types can integrate with validation libraries to automatically check incoming data, improving API safety.
Custom scalars extend GraphQL's basic types to handle complex data like dates, requiring careful implementation.

Practice

(1/5)
1. What is the main purpose of using @ObjectType() in NestJS GraphQL?
easy
A. To define the shape of data returned by queries and mutations
B. To define the shape of data clients send to the server
C. To create database entities automatically
D. To handle HTTP request validation

Solution

  1. Step 1: Understand the role of @ObjectType()

    This decorator is used to define the structure of data that GraphQL queries and mutations return to clients.
  2. Step 2: Differentiate from input types

    @InputType() is used for data clients send, while @ObjectType() is for data sent back to clients.
  3. Final Answer:

    To define the shape of data returned by queries and mutations -> Option A
  4. Quick Check:

    ObjectType = output data shape [OK]
Hint: ObjectType decorates output data, InputType decorates input data [OK]
Common Mistakes:
  • Confusing @ObjectType() with @InputType()
  • Thinking @ObjectType() creates database tables
  • Assuming @ObjectType() validates HTTP requests
2. Which of the following is the correct way to define an input type in NestJS GraphQL?
easy
A. @ObjectType() class CreateUserInput { name: string; }
B. @InputType() interface CreateUserInput { name: string; }
C. @InputType() class CreateUserInput { @Field() name: string; }
D. @Input() class CreateUserInput { name: string; }

Solution

  1. Step 1: Identify correct decorator and syntax

    Input types require the @InputType() decorator and fields must be decorated with @Field() to expose them to GraphQL.
  2. Step 2: Check class vs interface usage

    Input types must be classes, not interfaces, to work with decorators in NestJS.
  3. Final Answer:

    @InputType() class CreateUserInput { @Field() name: string; } -> Option C
  4. Quick Check:

    InputType + @Field + class = correct input type [OK]
Hint: Input types need @InputType() and @Field() on class properties [OK]
Common Mistakes:
  • Using @ObjectType() instead of @InputType()
  • Using interfaces instead of classes for input types
  • Omitting @Field() decorator on fields
3. Given the following code, what will be the GraphQL output type for the user query?
@ObjectType()
class User {
  @Field()
  id: string;

  @Field()
  name: string;
}

@Resolver()
class UserResolver {
  @Query(() => User)
  user() {
    return { id: '1', name: 'Alice' };
  }
}
medium
A. An error because id is not a number
B. A User object with fields id and name
C. A list of User objects
D. Null because no data source is connected

Solution

  1. Step 1: Analyze the @ObjectType and @Field decorators

    The User class defines two fields, id and name, both exposed to GraphQL via @Field().
  2. Step 2: Check the resolver return and query type

    The user query returns an object matching User shape with id and name strings, matching the GraphQL type.
  3. Final Answer:

    A User object with fields id and name -> Option B
  4. Quick Check:

    Query returns User object with id and name [OK]
Hint: Return object matches @ObjectType class shape for query output [OK]
Common Mistakes:
  • Expecting type errors for string id when GraphQL expects string
  • Assuming query returns a list without @Query(() => [User])
  • Thinking null is returned without data source
4. Identify the error in this input type definition:
@InputType()
class UpdateUserInput {
  name: string;
  @Field()
  age: number;
}
medium
A. Missing @Field() decorator on the 'name' property
B. Cannot use number type in input types
C. Class must be decorated with @ObjectType() instead
D. Input types cannot have more than one property

Solution

  1. Step 1: Check decorators on class properties

    In GraphQL input types, each field must have the @Field() decorator to be exposed.
  2. Step 2: Identify missing decorator

    The 'name' property lacks the @Field() decorator, so it won't be part of the GraphQL input type.
  3. Final Answer:

    Missing @Field() decorator on the 'name' property -> Option A
  4. Quick Check:

    All input fields need @Field() [OK]
Hint: Every input field needs @Field() decorator [OK]
Common Mistakes:
  • Forgetting @Field() on some properties
  • Confusing @InputType() with @ObjectType()
  • Thinking number types are invalid in input types
5. You want to create a mutation to update a user's profile with optional fields. Which approach correctly defines the input type to allow optional fields in NestJS GraphQL?
@InputType()
class UpdateUserInput {
  @Field({ nullable: true })
  name?: string;

  @Field({ nullable: true })
  age?: number;
}
hard
A. Use @ObjectType() instead of @InputType() for mutations
B. Remove '?' and set default values instead of nullable
C. Make all fields required and handle optional logic in resolver
D. Use @Field({ nullable: true }) and optional properties with '?' to allow partial updates

Solution

  1. Step 1: Understand optional fields in GraphQL input types

    To allow optional fields, use { nullable: true } in @Field() and mark properties optional with '?'.
  2. Step 2: Confirm correct decorator usage

    Use @InputType() for input data in mutations, not @ObjectType().
  3. Final Answer:

    Use @Field({ nullable: true }) and optional properties with '?' to allow partial updates -> Option D
  4. Quick Check:

    Nullable fields + optional properties = optional input fields [OK]
Hint: Use nullable:true and '?' for optional input fields [OK]
Common Mistakes:
  • Using @ObjectType() for input types
  • Not marking fields nullable for optional inputs
  • Making all fields required and complicating resolver logic
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard