Introducing Mapped Types for NestJS.

Kamil Mysliwiec | Trilon Consulting
Kamil Mysliwiec

In this article, we'll be looking at the recently added NestJS Mapped Types feature. We'll be showing how Mapped Types can drastically reduce the amount of boilerplate code required to build NestJS applications, speeding up our development and improving overall application maintainability.

In case you're not familiar with NestJS, it is a TypeScript Node.js framework that helps you build enterprise-grade efficient and scalable Node.js applications.


Introduction to mapped types

As you build out features, it's often useful to construct variants on a base entity type. A good example of such a variant is a Data Transfer Object (DTO). A Data Transfer Object is an object that is used to encapsulate data, and send it from one part of your application to another. DTO’s help us define the input and output interfaces of our system.

Let's imagine a real-world example, where we typically need to build both a create and update variations for the same entity type.

The create variant may require all fields, while the update variant may make all fields optional. Not to mention, both these types can also be variants of an entity type (to some extent).

That's a lot of redundant code!

Thus, NestJS now provides several utility functions that perform type transformations to help us avoid doing this, and make life a little bit easier.

Mapped Types are supported by both REST (@nestjs/swagger) and GraphQL (@nestjs/graphql) applications.

Construct partial types

Let's say that we have a User resource in our application. To allow creating new users, we'll define the CreateUserDto class (input interface), as follows:

import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail()
@ApiProperty()
email: string;
@MinLength(10)
@ApiProperty()
password: string;
}

As you can see in the example above, to create a new user we must provide an email and password. We've used the @ApiProperty() decorator to make properties visible to the @nestjs/swagger (note it's not needed with the CLI plugin enabled) package. Also, we've added some basic validation rules above.

By default, all of these fields are required.

With our first DTO in place, let's define another DTO for the update operation. The update operation expects the same fields, but with each one optional. To construct such a type, use the PartialType() function and pass the class reference (CreateUserDto) as an argument:

export class UpdateUserDto extends PartialType(CreateUserDto) {}

Hint The PartialType() function is imported from the @nestjs/swagger package.

That's it! 🐈

Now all fields are optional. Furthermore, when any value is not present, the validation is skipped for this specific property. You can read more about mapped types for REST APIs here.

Everything we've looked at today is very helpful and relevant for GraphQL applications as well! For instance, let's say we have an input type called CreateUserInput:

import { InputType, Field } from '@nestjs/graphql';
@InputType()
class CreateUserInput {
@Field()
email: string;
@Field()
password: string;
}

Hint Note that using @Field() decorator is not required when the CLI Plugin is enabled.

By default, all of these fields are required. To create a type with the same fields, but with each one optional, use PartialType() passing the class reference (CreateUserInput) as an argument:

@InputType()
export class UpdateUserInput extends PartialType(CreateUserInput) {}

Hint The PartialType() function is imported from the @nestjs/graphql package.

To learn more about mapped types for GraphQL application, visit this page.

Picking properties from types

What if our system requires that each email must be unique and immutable? With our new Mapped Types functionality, we have the option to exclude the email property from the UpdateUserDto class which will disable users from being able to update already taken emails.

For this, we can use either the OmitType() or the PickType() function.

The OmitType() function creates a new type by picking all properties from an input type and then removing a particular set of keys, while the PickType() function creates a new type by picking a set of properties from an input type.

Since we only want to exclude a single property, using OmitType() function is the simpler option here.

export class UpdateUserDto extends PartialType(
OmitType(CreateUserDto, ['email'] as const)
) {}

Hint The OmitType() function is imported from the @nestjs/swagger package.

Now UpdateUserDto has every property except email. All of the other properties are marked as optional (due to the PartialType function), as we mentioned before.

You can read more about the OmitType() and PickType() for REST APIs here.

Likewise, you can use the same construction with GraphQL applications:

@InputType()
export class UpdateUserInput extends PartialType(
OmitType(CreateUserInput, ['email'] as const)
) {}

Hint The OmitType() function is imported from the @nestjs/graphql package.

To learn more about the OmitType() and PickType() functions for GraphQL application, visit this page.

But why?

Why do we even need all these utility functions? After all, TypeScript supports mapped types feature already, right?

Built-in types like Partial<T>, Omit<T, K extends keyof T>, or Pick<T, K extends keyof T> are just TypeScript instructions. For example, Partial will inform the compiler that all properties are optional, but it won't generate any additional metadata (in the transpiled JavaScript files) that we could read at runtime.

Similarly, Pick will not instruct the compiler to internally define a new, derived class with wiped out decorators (applied to excluded properties) because this would introduce side-effects.

Nest utility functions, on the other hand, will exclude, pick, or apply validation decorators based on the expression you used.

These utility functions we've looked at will remove @nestjs/graphql or @nestjs/swagger metadata (if needed) which makes them fully compatible and ready to use in combination with these packages.

How do I get started?

NestJS mapped types feature makes type transformations more convenient. They help to dramatically reduce the boilerplate code by leveraging existing classes to construct type variants.

To start using them now, simply update your package (either @nestjs/graphql or @nestjs/swagger depending on the type of your application) to the latest version. 🐱


Support

Nest is an MIT-licensed open source project with its ongoing development made possible thanks to the support by the community, thank you!

<script>

If you want to join them, you can find more here.

Official consulting

Our goal is to help elevate teams — giving them the push they need to truly succeed in today’s ever-changing tech world. Contact us to find out more about expertise consulting, code reviews, architectural support, on-site enterprise workshops, trainings, and private sessions: support@nestjs.com.

Thank you

To all backers, sponsors, contributors, and community, thank you once again! This product is for you. And this is only the beginning of the long 🚀 story.

Become a Backer or Sponsor to Nest by donating to our open collective. ❤


Learn NestJS - Official NestJS Courses 📚

Level-up your NestJS and Node.js ecosystem skills in these incremental workshop-style courses, from the NestJS Creator himself, and help support the NestJS framework! 🐈

🚀 The NestJS Fundamentals Course is now LIVE and 25% off for a limited time!

🎉 NEW - NestJS Course Extensions now live!
#NestJS
#NodeJS

Share this Post!

📬 Trilon Newsletter

Stay up to date with all the latest Articles & News!

More from the Trilon Blog .

Jay McDoniel | Trilon Consulting
Jay McDoniel

NestJS Metadata Deep Dive

In this article we'll be doing a deep-dive and learning about how NestJS uses Metadata internally for everything from dependency injection, to decorators we use everyday!

Read More
Kamil Mysliwiec | Trilon Consulting
Kamil Mysliwiec

NestJS v10 is now available

Today I am excited to announce the official release of Nest 10: A progressive Node.js framework for building efficient and enterprise-grade, server-side applications.

Read More
Manuel Carballido | Trilon Consulting
Manuel Carballido

Implementing data source agnostic services with NestJS

Learn how to implement data source logic in an agnostic way in yours NestJS applications.

Read More

What we do at Trilon .

At Trilon, our goal is to help elevate teams - giving them the push they need to truly succeed in today's ever-changing tech world.

Trilon - Consulting

Consulting .

Let us help take your Application to the next level - planning the next big steps, reviewing architecture, and brainstorming with the team to ensure you achieve your most ambitious goals!

Trilon - Development and Team Augmentation

Development .

Trilon can become part of your development process, making sure that you're building enterprise-grade, scalable applications with best-practices in mind, all while getting things done better and faster!

Trilon - Workshops on NestJS, Node, and other modern JavaScript topics

Workshops .

Have a Trilon team member come to YOU! Get your team up to speed with guided workshops on a huge variety of topics. Modern NodeJS (or NestJS) development, JavaScript frameworks, Reactive Programming, or anything in between! We've got you covered.

Trilon - Open-source contributors

Open-source .

We love open-source because we love giving back to the community! We help maintain & contribute to some of the largest open-source projects, and hope to always share our knowledge with the world!

Explore more

Write us a message .

Let's talk about how Trilon can help your next project get to the next level.

Rather send us an email? Write to:

hello@trilon.io
© 2019-2023 Trilon.