Node.캐시가 js를 통해 API에서 얻은 결과

30072 단어 Node.jstech

하고 싶은 일

  • 캐시가 다른 서비스의 API에서 얻은 결과(스토리지에 남음)
  • 캐시에 데이터가 있는 경우 다른 서비스의 API를 호출하지 않고 캐시에서 응답
  • 을 반환합니다.

    장면


    Next.js의 API를 두드리다.
    API를 두들긴 넥스트.js는 RESAS의 API를 호출하여 도도부현의 일람과 도도부현의 전체 인구를 얻었다.
    Next.js는 도도부현의 일람표와 총인구의 정보를 API를 두드린 사람에게 되돌려준다.
    API를 반복해서 두드리면 Rate Limit에 걸릴 수 있습니다.유료인 API라면 유료다.
    도도부현의 정보는 그렇게 빈번하게 변하지 않기 때문에 반복해서 사용해도 문제가 없을 것이다.
    Next.js의 서버가 시작된 후, 첫 번째로 RESA의 API를 호출할 것을 요청하였는데, 그 결과는 캐시하고 사용할 수 있습니다.

    사용한 라이브러리


    node-cache 캐시를 사용하여 결과를 얻습니다.

    소스 코드


    lib/resas.ts
    import axios from 'axios'
    import NodeCache from 'node-cache'
    
    const RESAS_API_KEY = process.env.RESAS_API_KEY
    const RESAS_ENDPOINT = 'https://opendata.resas-portal.go.jp/api/v1'
    
    type Prefecture = {
      prefCode: number
      prefName: string
    }
    
    type PopulationOfPrefecture = {
      prefecture: Prefecture
      populations: Population[]
    }
    
    type Population = {
      year: string
      value: string
    }
    
    const cache = new NodeCache({ stdTTL: 1000 * 60 * 60 * 24 })
    
    /**
     * RESAS API から都道府県リストを取得する。
     * @return {Prefecture[]} 都道府県のリスト
     * @see [人口構成](https://opendata.resas-portal.go.jp/docs/api/v1/population/composition/perYear.html)
     */
    export const getPrefList = async (): Promise<Prefecture[]> => {
      if (RESAS_API_KEY == null) throw new Error()
      if (cache.get('prefectures')) {
        console.log('キャッシュから県の情報を返します。')
        return cache.get('prefectures') as Prefecture[]
      }
      const prefecturesUrl = `${RESAS_ENDPOINT}/prefectures`
      const { data } = await axios.get(prefecturesUrl, { headers: { 'X-API-KEY': RESAS_API_KEY } })
      const prefectures: Prefecture[] = data.result
      cache.set('prefectures', prefectures)
      return prefectures
    }
    
    /**
     * 都道府県コードから都道府県を返す。
     * @param {number} prefCode 都道府県コード
     * @return {Promise<Prefecture>>} 都道府県
     */
    const getPrefById = async (prefCode: number): Promise<Prefecture> => {
      const prefectures =
        cache.get('prefectures') != null
          ? (cache.get('prefectures') as Prefecture[])
          : await getPrefList()
      const pref = prefectures.filter((prefecture) => prefecture.prefCode === prefCode)
      if (pref && pref.length !== 0) {
        return pref[0]
      } else {
        throw new Error(`prefCode ${prefCode} doesn't exist.`)
      }
    }
    
    /**
     * prefCode で渡した都道府県の総人口の配列を返す
     * @param {number} prefCode RESASの都道府県APIから取得した都道府県ID
     * @return {Promise<PopulationOfPrefecture>} 都道府県の総人口推移
     */
    export const getAllPopulationByPrefId = async (
      prefCode: number
    ): Promise<PopulationOfPrefecture> => {
      if (RESAS_API_KEY == null) throw new Error()
      const prefecture = await getPrefById(prefCode)
      if (cache.get(`population-prefId-${prefCode}`) != null) {
        console.log('キャッシュから総人口の情報を返します。')
        const cachedPrefAllPopulations = cache.get(`population-prefId-${prefCode}`) as Population[]
        return { prefecture, populations: cachedPrefAllPopulations }
      }
      const populationByPrefIdUrl = `${RESAS_ENDPOINT}/population/composition/perYear?cityCode=-&prefCode=${prefCode}`
      const { data } = await axios.get(populationByPrefIdUrl, {
        headers: { 'X-API-KEY': RESAS_API_KEY },
      })
    
      const filteredAllPopulations = data.result.data.filter(
        (populationData: any) => populationData.label === '総人口'
      )
      const allPopulations = filteredAllPopulations[0].data
    
      cache.set(`population-prefId-${prefCode}`, allPopulations)
      return { prefecture, populations: allPopulations }
    }
    
    
    캐시를 준비하는 부분은 다음과 같습니다.
    import NodeCache from "node-cache";
    
    const cache = new NodeCache({ stdTTL: 1000 * 60 * 60 * 24 });
    
    캐시가 있는지 확인하는 부분은 다음과 같습니다.
    if (cache.get(`population-prefId-${prefCode}`) != null) {
      console.log('キャッシュから総人口の情報を返します。')
      const cachedPrefAllPopulations = cache.get(`population-prefId-${prefCode}`) as Population[]
      return { prefecture, populations: cachedPrefAllPopulations }
    }
    
    캐시에 저장된 부분은 다음과 같습니다.
    cache.set(`population-prefId-${prefCode}`, allPopulations);
    

    API


    Next.js pages/api 이하의 API에서 사용한 그림을 호출합니다.await getAllPopulationByPrefId(1) 견본에 고정 파라미터를 전달한다.
    원래 req 에서 파라미터를 얻어야 하는데, 이 보도의 주제는 아니다.
    pages/api/populations.ts
    import { NextApiRequest, NextApiResponse } from 'next'
    import { getAllPopulationByPrefId } from '../../lib/resas'
    
    export default async function handler(req: NextApiRequest, res: NextApiResponse) {
      const populations = await getAllPopulationByPrefId(1)
      res.status(200).json(populations)
    }
    
    http://localhost:3000/api/populations의 응답은 이렇다.
    {
    "prefecture": {
    "prefCode": 1,
    "prefName": "北海道"
    },
    "populations": [
    {
    "year": 1960,
    "value": 5039206
    },
    첫 번째 반응이 좀 느렸어요.RESAS에 데이터를 찾으러 가야 하기 때문이다.
    두 번째부터는 폭발 속도다.
    서버 측(Next.js 측)에 두 번째 이후 메시지キャッシュから総人口の情報を返します。가 표시됩니다.
    도도부현 정보를 얻는 API.
    pages/api/prefectures.ts
    import { NextApiRequest, NextApiResponse } from 'next'
    import { getPrefList } from '../../lib/resas'
    
    export default async function handler(req: NextApiRequest, res: NextApiResponse) {
      const prefectures = await getPrefList()
      res.status(200).json(prefectures)
    }
    
    http://localhost:3000/api/prefectures의 응답은 이렇다.
    [
    {
    "prefCode": 1,
    "prefName": "北海道"
    },
    {
    "prefCode": 2,
    "prefName": "青森県"
    },
    {
    "prefCode": 3,
    "prefName": "岩手県"
    },

    브라우저도 캐시해야 합니까?


    나는 API에서 캐시를 되돌려줌으로써 브라우저 측도 캐시를 할 수 있고 더 빨리 응답할 수 있으며 불필요한 요청을 줄일 수 있다고 생각한다.
    브라우저memory-cache를 사용하는 것이 좋을 것 같아서 다른 글로 시도해 보세요.
    How to Cache API Calls in Next.js

    좋은 웹페이지 즐겨찾기