0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【NestJS】戻り値のデータ形式を変換して統一する方法

Last updated at Posted at 2022-04-09

概要

APIからクライアントサイドにレスポンスする際に形式を統一したかったので実装してみた

{
  "statusCode": 200,
  "message": "SUCCESS",
  "data": [
    {
      "name": "hoge",
      "email": "hoge@example.com",
      "password": "$2b$10$XuKMQ36pzZ/PSed3nT5djeuf.RzCrAPFL5bquZFnXgwO8CWLNxt0i",
      "id": "1",
      "ins_ts": "2022-04-09T16:17:06.732Z",
      "upd_ts": "2022-04-09T17:09:31.000Z",
      "delete_ts": null
    }
  ]
}

実装内容

解説

  • データを詰め込むオブジェクトの型を定義して結合します

export const Info = {
  statusCode: 200,
  message: 'SUCCESS',
};

export type Response<T> = typeof Info & {
  data: T;
};

src/transform.interceptor.ts
import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { map, Observable } from 'rxjs';

export const Info = {
  statusCode: 200,
  message: 'SUCCESS',
};

export type Response<T> = typeof Info & {
  data: T;
};

@Injectable()
export class TransformInterceptor<T>
  implements NestInterceptor<T, Response<T>>
{
  intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<Response<T>> {
    return next.handle().pipe(map((data) => Object.assign({}, Info, { data })));
  }
}

Unit テストはこんな感じになる

src/transform.interceptor.spec.ts
// https://github.com/nestjs/nest/blob/master/integration/hello-world/e2e/interceptors.spec.ts
import {
  CallHandler,
  ExecutionContext,
  INestApplication,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { map, of } from 'rxjs';
import { Test } from '@nestjs/testing';
import { APP_INTERCEPTOR } from '@nestjs/core';
import * as request from 'supertest';
import { AppModule } from './app.module';

const RETURN_VALUE = 'test';

@Injectable()
export class OverrideInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    /**
     * Observableが最初にサブスクライブされた時に呼ばれる関数
     * 値をパブリッシュされた対象を確認・通知を受け取ります
     */
    return of(RETURN_VALUE);
  }
}

@Injectable()
export class TransformInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    // RxJS いつ起こるかわからない処理を待ち受けて応答オブジェクトをプロパティに詰めて返す
    return next.handle().pipe(map((data) => ({ data })));
  }
}

@Injectable()
export class StatusInterceptor {
  constructor(private readonly statusCode: number) {}

  intercept(context: ExecutionContext, next: CallHandler) {
    // HTTP アプリケーションのコンテキストに適した HttpArgumentsHost オブジェクトを返却
    const ctx = context.switchToHttp();
    const res = ctx.getResponse();
    res.status(this.statusCode);
    return next.handle().pipe(map((data) => ({ data })));
  }
}

@Injectable()
export class HeaderInterceptor {
  constructor(private readonly headers: object) {}

  intercept(context: ExecutionContext, next: CallHandler) {
    const ctx = context.switchToHttp();
    const res = ctx.getResponse();
    // 必要なヘッダーをセット
    for (const key in this.headers) {
      if (this.headers.hasOwnProperty(key)) {
        res.header(key, this.headers[key]);
      }
    }
    return next.handle().pipe(map((data) => ({ data })));
  }
}

function createTestModule(interceptor) {
  return Test.createTestingModule({
    imports: [AppModule],
    providers: [
      {
        provide: APP_INTERCEPTOR,
        useValue: interceptor,
      },
    ],
  }).compile();
}

describe('Interceptors', () => {
  let app: INestApplication;

  it(`should transform response (sync)`, async () => {
    app = (
      await createTestModule(new OverrideInterceptor())
    ).createNestApplication();

    await app.init();
    return request(app.getHttpServer()).get('/users').expect(200, RETURN_VALUE);
  });
});

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?