【Nestjs】郵便番号から都道府県と市区町村を取得するAPIを実装する

郵便番号の住所検索APIのデファクトスタンダートと言われている「PostcodeJP API」を利用します。

APIキーを利用するシンプルなAPIで、郵便番号API、都道府県API、市区町村APIなど必要なものが揃っている。
また、プランも豊富でサービス規模によって選択しやすくなってるのも特徴です。

今回は、Nestjsで、郵便番号から都道府県、市区町村を取得するAPIを実装してきます。

目次

APIリクエスト制限

Free, Small, Mid, Standard, Large, Pro とバリエーションあふれたプランが用意されている。

今回は分かりやすく、Free, Pro のみ記載する。

Free

  • リクエスト回数
    • 384回/日
  • リクエストレート
    • 1回/秒

Pro

  • リクエスト回数
    • 無制限
  • リクエストレート
    • 無制限

PostcodeJP API でAPIキーを発行する。

https://console.postcode-jp.com/login

にログインします。
アカウントを持っていない場合は、「アカウント作成」から進みます。

ログイン後、「APIキーを作成」をクリックします。

APIキーが作成されるので、控えておきます。
ここで「キーを制限」をクリックすると、HTTPリファラーやIPアドレス制限ができるので、必要に応じてAPIキーを制限してください。

NestjsでAPIエンドポイントを実装

nestjs の公式に記載のコマンドを叩くと自動でCRUDを作成してくれます。
コマンドを実行するとmoduleに必要なものもimportsしてくれるので便利です。

$ nest g resource <resource_name>

最終的なディレクトリ構成はこちら。

<repogitory>
 ├.env
 └src
  ├app.module.ts
  └postcode
   ├dto
   │└postcode.dto.ts
   ├postcode.controller.ts
   ├postcode.module.ts
   └postcode.service.ts
 

.env

.envにPostcodeJP APIを使用するのに必要な環境変数を定義します。

POSTCODEJP_API_BASE_URL="https://apis.postcode-jp.com/api/v5/postcodes"
POSTCODEJP_API_KEY=""

POSTCODEJP_API_KEY には、事前に控えておいたAPIキーを定義します。

PostcodeJP APIのベースURLなどは、こちらから確認ができます。

dto

APIレスポンスで返却するオブジェクトを定義します。
郵便番号、都道府県、市区町村を返却します。

import { ApiProperty } from '@nestjs/swagger';

export class PostcodeDto {
  @ApiProperty()
  postcode: string;
  @ApiProperty()
  pref: string;
  @ApiProperty()
  city: string;
}

service

getPostInfo メソッドを作成し、郵便番号に紐づく都道府県、市区町村を返します。
郵便番号に紐づくものがなければ、BadRequestExceptionをあげます。

実装としては、PostcodeJP APIをリクエスト時は、headersに apikey: process.env.POSTCODEJP_API_KEY を含めます。

1点気をつけないといけないのは、HttpService.get() はObservableが返ってくるため、rxjs –lastValueFrom を使って、リクエストのデータをプロミスの形で取り出す必要があります。

また今回は省略していますが、郵便番号のバリデーションも適時入れてください。

import { HttpService } from '@nestjs/axios';
import { BadRequestException, Injectable } from '@nestjs/common';
import { lastValueFrom, map } from 'rxjs';
import ErrorCode from '../common/constants';
import { PostcodeDto } from './dto/postcode.dto';

@Injectable()
export class PostcodeService {
  constructor(private httpService: HttpService) {}

  async getPostInfo(postcode: string): Promise<PostcodeDto> {
    const headers = {
      apikey: process.env.POSTCODEJP_API_KEY,
    };

    const postcodeList = await lastValueFrom(
      this.httpService
        .get(`${process.env.POSTCODEJP_API_BASE_URL}/${postcode}`, {
          headers: headers,
        })
        .pipe(map((response) => response.data)),
    );

    if (!postcodeList.length) {
      throw new BadRequestException();
    }

    const postcodeItem = postcodeList.shift();

    return {
      postcode: postcode,
      pref: postcodeItem.pref,
      city: postcodeItem.city,
    };
  }
}

controller

findOne メソッドを作成し、引数に郵便番号を受付け、serviceで処理を行います。

@ApiBearerAuth() はnestjsでAuthを取りいており、認証されたユーザーのみ実行できるかチェックする機構です。
必要に応じて追加、削除してください。

import { Controller, Get, Param } from '@nestjs/common';
import {
  ApiBearerAuth,
  ApiOperation,
  ApiProduces,
  ApiResponse,
  ApiTags,
} from '@nestjs/swagger';
import { PostcodeDto } from './dto/postcode.dto';
import { PostcodeService } from './postcode.service';

@Controller('postcode')
@ApiTags('/postcode')
export class PostcodeController {
  constructor(private readonly postcodeService: PostcodeService) {}

  @Get(':postcode')
  @ApiBearerAuth()
  @ApiProduces('application/json; charset=utf-8')
  @ApiOperation({ summary: '郵便番号から都道府県・市区町村を取得' })
  @ApiResponse({
    status: 200,
    description: '郵便番号から都道府県・市区町村を取得',
    type: PostcodeDto,
  })
  findOne(@Param('postcode') postcode: string): Promise<PostcodeDto> {
    return this.postcodeService.getPostInfo(postcode);
  }
}

module

@Moduleにcontrollers、imports、providersを追加して紐づけます。

import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { PostcodeController } from './postcode.controller';
import { PostcodeService } from './postcode.service';

@Module({
  controllers: [PostcodeController],
  imports: [HttpModule],
  providers: [PostcodeService],
})
export class PostcodeModule {}

app.module.ts

PostcodeModuleをインポートします。

import { PostcodeModule } from './postcode/postcode.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: [`.env`, `.env.${process.env.NODE_ENV}`],
      load: [],
    }),
    PostcodeModule,
  ]
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(RequestMiddleware, CorsMiddleware).forRoutes('*');
  }
}

APIを叩く

swaggerから郵便番号を渡してAPIを叩くと、郵便番号に紐づく都道府県、市区町村が返ってきます。

Summary

Proプランはリクエスト回数、リクエストレートが無制限になるためこのままの実装で良いが、Freeプランだと制限があるので、キャッシュ戦略を検討する必要がある。
サービス規模に応じて最適なプラン、設計をしていきましょう。

よかったらシェアしてね!
目次