Next.js(React)+Goで個人開発をしていて、FetchAPIを利用してGoでローカル環境にたてたAPIをNext.jsで呼び出す際に、値が取れずにはまりました。 今回はどうやって解消したのか記事にしました。

今回発生したエラー

Next.jsfetchを使ってPOSTした際に、以下のようなエラーが発生。

Error serializing `.postData` returned from `getServerSideProps` in "/posts/get-api". Reason: `object` ("[object Promise]") cannot be serialized as JSON. Please only return JSON serializable data types.

ようわからんけど、APIから値が取れてないみたいでした。 Next.jsからfetchする際のコードは以下のようにしていました。

export const getServerSideProps: GetServerSideProps = async () => {
  const url = "http://localhost:8080/login";  APIはローカルの8080番ポートにたてていました
  let postData = {};
  await fetch(url, {
    method: "POST",
    cache: "no-cache",
    headers: {
      "Content-Type": "application/json charset=utf-8",
    },
    body: JSON.stringify({ username: "yagiyu", password: "miran" }),
  }).then(function (response) {
    console.log(response);
    postData = response.json();
  });
  return {
    props: {
      postData,
    },
  };
};

また、上記を実行した時のレスポンスが以下で、APIと通信はできたが、値を取れませんでした。

Response {
  size: 0,
  timeout: 0,
  [Symbol(Body internals)]: {
    body: PassThrough {
      _readableState: [ReadableState],
      readable: true,
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      _writableState: [WritableState],
      writable: false,
      allowHalfOpen: true,
      _transformState: [Object],
      [Symbol(kCapture)]: false
    },
    disturbed: false,
    error: null
  },
  [Symbol(Response internals)]: {
    url: 'http://localhost:8080/login',
    status: 200,
    statusText: 'OK',
    headers: Headers { [Symbol(map)]: [Object: null prototype] },
    counter: 0
  }
}

ターミナルからcurlを実行すると、以下のように成功していました。

$ curl -XPOST -d'{"username": "yagiyu", "password": "miran"}' localhost:8080/login
{"user":{"firstName":"yu","lastName":"yagishita","username":"yagiyu"}}

解消方法

getServerSidePropsの関数内で、fetchで非同期処理をしていたコードのthenをやめて、awaitにすることで値を取得して、returnできました。

export const getServerSideProps: GetServerSideProps = async () => {
  const url = "http://localhost:8080/login";
  const response = await fetch(url, {
    method: "POST",
    cache: "no-cache",
    headers: {
      "Content-Type": "application/json charset=utf-8",
    },
    body: JSON.stringify({ username: "yagiyu", password: "miran" }),
  });
  const postData = await response.json();
  return {
    props: {
      postData,
    },
  };
};

エラー原因

JavaScriptの非同期処理Promise, Async, Awaitについて理解していなかったことが原因でした。 thenで非同期処理の結果を得ようとする場合、先にreturnが実行されてしまい、空のpostDataが渡されていました。これをawaitに変えることで、非同期処理が完了するのを待ってからreturnされるようになり、エラーを解消できました。

JavaScriptの非同期処理をしっかりと理解できていないので、今後もコードをたくさん書いて覚えていきます。 この記事がわかりやすかったので、是非読んでみてください。 JavaScriptの非同期処理Promise、AsyncとAwaitの仕組みをGIFアニメで解説