郵便番号の住所検索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プランだと制限があるので、キャッシュ戦略を検討する必要がある。
サービス規模に応じて最適なプラン、設計をしていきましょう。