lapapooh / dto-schema

Data Transfer Object Schema

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

DTO Schema

Build codecov npm version npm minzipped size npm type definitions npm downloads npm license

Installation

# NPM
npm i dto-schema
# Yarn
yarn add dto-schema

Babel

Setup @babel/plugin-proposal-decorators with legacy mode.

{
  "plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
}

TypeScript

Enable experimentalDecorators property in tsconfig.json

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

Browser

Current build targets modern browsers that supports ESModules (see the browserlist), so you will need to polyfill required ES2015 features like Map and WeakMap and transpile unsupported syntax like classes or arrow functions.

API

parseDTO

function parseDTO<T extends object>(Cls: DTOConstructor, raw: T | object): T;

Transforms provided raw value with rules defined in the provided Cls constructor.

Usage
import { parseDTO, StringProp } from 'dto-schema';

class UserDTO {
  @StringProp() guid: string;
  @StringProp() name: string;
  password: string; // this one is not annotated, so it would not be transformed.
}

expect(parseDTO(UserDTO, {})).toEqual({ guid: '', name: '' });

expect(
  parseDTO(UserDTO, { guid: 1, name: 'Leeroy Jenkins', password: 'yolo' }),
).toEqual({ guid: '1', name: 'Leeroy Jenkins' });

const user = new UserDTO();
user.name = 'John';
expect(parseDTO(UserDTO, user)).toEqual({ guid: '', name: 'John' });

serializeDTO

function serializeDTO<T extends object>(
  Cls: DTOConstructor,
  raw: T | object,
): object;

Unlike parseDTO, serializedDTO uses DTOSchemaOptions#serialize method which prepares a valid JSON object. Check DateProp and Prop examples.

@BooleanProp

interface BooleanPropOptions {
  defaultValue?: null | boolean;
}

function BooleanProp(options?: BooleanPropOptions): PropertyDecorator;

Annotates property as a boolean. Accepts:

  • defaultValue - default value to use when input value is null or undefined
Usage
import { parseDTO, BooleanProp } from 'dto-schema';

class UserDTO {
  @BooleanProp() isVerified: boolean;
  @BooleanProp({ defaultValue: true }) isActive: boolean;
  @BooleanProp({ defaultValue: null }) isSubscribed: boolean;
}

expect(parseDTO(UserDTO, {})).toEqual({
  isVerified: false,
  isActive: true,
  isSubscribed: null,
});

expect(
  parseDTO(UserDTO, {
    isVerified: true,
    isActive: false,
    isSubscribed: false,
  }),
).toEqual({
  isVerified: true,
  isActive: false,
  isSubscribed: false,
});

@NumberProp

interface NumberPropOptions {
  defaultValue?: null | number;
  clampMin?: number;
  clampMax?: number;
  round?: boolean | 'ceil' | 'floor' | 'trunc';
}

function NumberProp(options?: NumberPropOptions): PropertyDecorator;

Annotates property as a number. Accepts:

  • clampMin - lower bound of the number to clamp
  • clampMax - upper bound of the number to clamp
  • round - round method to use to adjust a value
  • defaultValue - Default value to use when input value is null or undefined
Usage
import { parseDTO, NumberProp } from 'dto-schema';

class ProductFilter {
  @NumberProp()
  id: number;

  @NumberProp({ defaultValue: 1, round: true, clampMin: 1 })
  page: number;

  @NumberProp({ defaultValue: null, round: true, clampMin: 1, clampMax: 50 })
  pageSize: number;
}

expect(parseDTO(ProductFilter, {})).toEqual({
  id: NaN,
  page: 1,
  pageSize: null,
});

expect(
  parseDTO(ProductFilter, {
    id: 42,
    page: 0,
    pageSize: 100,
  }),
).toEqual({
  id: 42,
  page: 1,
  pageSize: 50,
});

expect(
  parseDTO(ProductFilter, {
    id: 42,
    page: 3.33333,
    pageSize: 3.3333,
  }),
).toEqual({
  id: 42,
  page: 3,
  pageSize: 3,
});

@StringProp

interface StringPropOptions {
  defaultValue?: null | string;
  trim?: boolean | 'start' | 'end';
}

function StringProp(options?: StringPropOptions): PropertyDecorator;

Annotates property as a string. Accepts:

  • defaultValue - default value to use when input value is null or undefined
  • trim - trim method to adjust input value
Usage
import { parseDTO, StringProp } from 'dto-schema';

class UserDTO {
  @StringProp() guid: string;
  @StringProp({ trim: true }) name: string;
  @StringProp({ trim: true, defaultValue: null }) email: string;
}

expect(parseDTO(UserDTO, {})).toEqual({
  guid: '',
  name: '',
  email: null,
});

expect(
  parseDTO(UserDTO, {
    guid: '123',
    name: ' Leeroy\n ',
    email: ' leeroy@jenkins.dev \n',
  }),
).toEqual({
  guid: '123',
  name: 'Leeroy',
  email: 'leeroy@jenkins.dev',
});

@DateProp

interface DatePropOptions {
  defaultValue?: () => null | number | string | Date;
}

function DateProp(options?: DatePropOptions): PropertyDecorator;

Annotates property as a Date. Converted to the ISO string when serialized. Accepts:

  • defaultValue - default value to use when input value is null or undefined
Usage
import { parseDTO, serializeDTO, DateProp } from 'dto-schema';

class PostDTO {
  @DateProp() updatedAt: string;
  @DateProp({ defaultValue: null }) deletedAt: string;
  @DateProp({ defaultValue: () => Date.UTC(2019, 4, 24) })
  createdAt: string;
}

expect(parseDTO(PostDTO, {})).toEqual({
  updatedAt: new Date(NaN), // Invalid Date,
  deletedAt: null,
  createdAt: new Date(Date.UTC(2019, 4, 24)),
});

expect(serializeDTO(PostDTO, {})).toEqual({
  updatedAt: null, // Invalid Date,
  deletedAt: null,
  createdAt: '2019-05-24T00:00:00.000Z',
});

expect(
  parseDTO(PostDTO, {
    createdAt: Date.UTC(2019, 4, 24),
    updatedAt: '2019-05-24T00:00:00.000Z',
    deletedAt: new Date(Date.UTC(2019, 4, 24)),
  }),
).toEqual({
  updatedAt: new Date(Date.UTC(2019, 4, 24)),
  deletedAt: new Date(Date.UTC(2019, 4, 24)),
  createdAt: new Date(Date.UTC(2019, 4, 24)),
});

expect(
  serializeDTO(PostDTO, {
    createdAt: Date.UTC(2019, 4, 24),
    updatedAt: '2019-05-24T00:00:00.000Z',
    deletedAt: new Date(Date.UTC(2019, 4, 24)),
  }),
).toEqual({
  updatedAt: '2019-05-24T00:00:00.000Z',
  deletedAt: '2019-05-24T00:00:00.000Z',
  createdAt: '2019-05-24T00:00:00.000Z',
});

@UnknownProp

interface UnknownPropOptions {
  defaultValue?: () => null | number | string | Date;
}

function UnknownProp(options?: UnknownPropOptions): PropertyDecorator;

Annotates property as a Date. Converted to the ISO string when serialized. Accepts:

  • defaultValue - default value to use when input value is null or undefined
Usage
import { parseDTO, serializeDTO, UnknownProp } from 'dto-schema';

class UserDTO {
  @UnknownProp() role: unknown;
}

expect(parseDTO(UserDTO, {}), { role: null });
expect(serializeDTO(UserDTO, {}), { role: null });
expect(parseDTO(UserDTO, { role: 1 }), { role: 1 });
expect(serializeDTO(UserDTO, { role: 1 }), { role: 1 });
expect(parseDTO(UserDTO, { role: 'a' }), { role: 'a' });
expect(serializeDTO(UserDTO, { role: 'a' }), { role: 'a' });
expect(parseDTO(UserDTO, { role: null }), { role: null });
expect(serializeDTO(UserDTO, { role: null }), { role: null });

@ObjectProp

interface ObjectPropOptions {
  nullable?: boolean;
}

function ObjectProp(options?: ObjectPropOptions): PropertyDecorator;

Annotates property as a Object with another DTO type.

Accepts:

  • nullable - converts value to null when it's null or undefined, otherwise converts DTO with an empty object
Usage
import { parseDTO, ObjectProp, StringProp } from 'dto-schema';

class UserDTO {
  @StringProp() guid: string;
}

class PostDTO {
  @ObjectProp(() => UserDTO) createdBy: UserDTO;
  @ObjectProp(() => UserDTO, { nullable: true }) updatedBy: null | UserDTO;
}

expect(parseDTO(PostDTO, {})).toEqual({
  createdBy: { guid: '' },
  updatedBy: null,
});

expect(
  parseDTO(PostDTO, {
    createdBy: { guid: 123 },
    updatedBy: { guid: 456 },
  }),
).toEqual({
  createdBy: { guid: '123' },
  updatedBy: { guid: '456' },
});

@Prop

interface DTOSchemaOptions<TValue = unknown> {
  type: string;
  testType: (value: unknown) => boolean;
  normalize: (value: unknown) => null | TValue;
  serialize?: (value: null | TValue) => unknown;
}

function Prop<T>(options?: DTOSchemaOptions<T>): PropertyDecorator;

Annotates property with with another DTO type.

Accepts:

  • type - name of the schema type, used for error messages
  • testType - used to validate object type of the schema
  • normalize - used to convert input value to required
  • serialize - used to convert value to JSON value
Usage
import { parseDTO, serializeDTO, Prop } from 'dto-schema';

class UserFilter {
  @Prop<Set<string>>({
    type: 'set',
    testType(value) {
      return value instanceof Set;
    },
    normalize(value) {
      return value instanceof Set
        ? value
        : Array.isArray(value)
        ? new Set(value)
        : new Set();
    },
    serialize(value) {
      return value == null ? [] : Array.from(value);
    },
  })
  roles: Set<string>;
}

expect(parseDTO(UserFilter, {})).toEqual({
  roles: new Set(),
});

expect(serializeDTO(UserFilter, {})).toEqual({
  roles: [],
});

expect(
  parseDTO(UserFilter, {
    roles: ['admin', 'editor', 'admin'],
  }),
).toEqual({
  roles: new Set(['admin', 'editor']),
});

expect(serializeDTO(UserFilter, {})).toEqual({
  roles: ['admin', 'editor'],
});

@ArrayProp

interface ArrayPropOptions<T> {
  defaultValue?: () => null | T[];
}

function ArrayProp<T>(options?: ArrayPropOptions<T>): PropertyDecorator;

Annotates property as an Array, used in combination with another decorator.

Accepts:

  • defaultValue - default value to use when input value is null or undefined
Usage
import { parseDTO, ArrayProp, StringProp } from 'dto-schema';

class PostDTO {
  @ArrayProp({ defaultValue: () => null }) meta: unknown[];
  @ArrayProp({ defaultValue: () => ['News'] }) @StringProp() tags: string[];
}

expect(parseDTO(PostDTO, {})).toEqual({
  meta: null,
  tags: ['News'],
});

expect(
  parseDTO(PostDTO, {
    meta: ['this', null, 'can', undefined, 'be', NaN, 'anything', true],
    tags: ['this', null, 'will', undefined, 'be', NaN, 'transformed', true],
  }),
).toEqual({
  meta: ['this', null, 'can', undefined, 'be', NaN, 'anything', true],
  tags: ['this', '', 'will', '', 'be', 'NaN', 'transformed', 'true'],
});

About

Data Transfer Object Schema

License:MIT License


Languages

Language:TypeScript 95.9%Language:JavaScript 4.1%