{"id":13826333,"url":"https://github.com/exogen/next-fetch-har","last_synced_at":"2025-08-11T13:09:31.574Z","repository":{"id":40941908,"uuid":"196106848","full_name":"exogen/next-fetch-har","owner":"exogen","description":"Debug Next.js SSR requests using HTTP Archive (HAR) logs","archived":false,"fork":false,"pushed_at":"2023-01-04T04:21:45.000Z","size":2701,"stargazers_count":39,"open_issues_count":15,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-11T04:39:22.734Z","etag":null,"topics":["debugging","har","http-archive","nextjs","react"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exogen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-10T01:15:21.000Z","updated_at":"2025-01-22T10:49:01.000Z","dependencies_parsed_at":"2023-02-01T20:01:45.511Z","dependency_job_id":null,"html_url":"https://github.com/exogen/next-fetch-har","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/exogen/next-fetch-har","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Fnext-fetch-har","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Fnext-fetch-har/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Fnext-fetch-har/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Fnext-fetch-har/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exogen","download_url":"https://codeload.github.com/exogen/next-fetch-har/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Fnext-fetch-har/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269892309,"owners_count":24491905,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["debugging","har","http-archive","nextjs","react"],"created_at":"2024-08-04T09:01:35.792Z","updated_at":"2025-08-11T13:09:31.551Z","avatar_url":"https://github.com/exogen.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# next-fetch-har\n\n```console\n$ yarn add next-fetch-har\n```\n\n![Demo](./demo.gif)\n\n## Motivation\n\nWhen you click from page to page in a [Next.js](https://nextjs.org/) app, it’s\neasy to see and debug what API calls your `getInitialProps` methods are making:\njust look in your browser’s Network tab.\n\nBut what about during Server Side Rendering (SSR)? In my experience, one of the\nharder aspects of debugging Next.js apps is getting visibility into what\n`getInitialProps` is doing on the server – particularly what API requests and\nresponses the app is seeing.\n\nUsually, this is where good logging comes in. It’s very useful to have an HTTP\nclient that can be instrumented with detailed logging so that you can see what\nrequests are being made and whether they were successful. But how much can you\nreasonably log without spamming your console? All the headers? Large response\nbodies? What about timing information, like how long it took to receive each\nresponse?\n\nEven if you connect to the [Node.js Inspector](https://nodejs.org/en/docs/guides/debugging-getting-started/),\nthere is no server-side equivalent to the browser’s Network tab. So what if you\nhad some other way to populate the Network tab with the network activity from\nthe server, as if those requests were made in the browser?\n\nThat’s what [node-fetch-har](https://github.com/exogen/node-fetch-har) and this\nlibrary allow you to do!\n\n## Usage\n\n#### Step 1: Wrap your \u0026lt;App\u0026gt;\n\nThis library exports a `withFetchHar` Higher Order Component (HOC) that you can\nwrap your `\u003cApp\u003e` component with to enable recording of server-side Fetch API\ncalls.\n\nIn `_app.js`:\n\n```js\nimport App from \"next/app\";\nimport { withFetchHar } from \"next-fetch-har\";\n\nexport default withFetchHar(App);\n```\n\nOr with a custom `\u003cApp\u003e`:\n\n```js\nclass CustomApp extends App {\n  // Your customizations...\n}\n\nexport default withFetchHar(CustomApp);\n```\n\nIf you haven’t exposed a global `fetch` polyfill on the server, you can supply\nyour `fetch` instance to the HOC:\n\n```js\nimport fetch from \"isomorphic-unfetch\";\n\nexport default withFetchHar(App, { fetch });\n```\n\nIf you’d like to enable recording only for certain pages, the HOC also works\nwith individual Page components:\n\n```js\nfunction MyPage() {\n  // Your page content...\n}\n\nexport default withFetchHar(MyPage);\n```\n\n#### Step 2: Use the enhanced Fetch\n\nInstead of using a global Fetch instance, the `withFetchHar` HOC creates a\nper-request instance of Fetch that logs requests as HAR entries. It adds this\nFetch instance to the `ctx` object that your pages receive in `getInitialProps`.\nYou should switch your calls to use this instance of Fetch.\n\n```js\nclass HomePage extends React.Component {\n  static async getInitialProps(ctx) {\n    // Get `fetch` from `ctx`.\n    const { fetch } = ctx;\n\n    // Example of what you might do with your API...\n    const response = await fetch(\"/api/foo\");\n    const body = await response.json();\n\n    return { value: body.foo };\n  }\n\n  render() {\n    // Do something with value...\n  }\n}\n```\n\n#### Step 3: Download the HTTP Archive\n\nWhen `getInitialProps` is complete, the `withFetchHar` HOC adds the resulting\nHTTP Archive (HAR) to the app’s `getInitialProps` output, and renders a download\nbutton at the bottom of the page to access it.\n\n![Demo](./demo.gif)\n\n### Enable in certain environments\n\nYou probably don’t want to enable HAR logging for every request in production\n(or if you do, it should probably only happen for superusers). **Remember that\nHAR logs can contain sensitive information like passwords and cookie values!**\n\nThe `withFetchHar` HOC has an `enabled` option you can use to conditionally\nenable HAR logging. When disabled, `ctx.fetch` will be the vanilla Fetch\ninstance instead of the enhanced one.\n\n```js\nwithFetchHar(App, {\n  // For safety, this is the default!\n  enabled: process.env.NODE_ENV !== \"production\"\n});\n```\n\nIf you have multiple environments that run production builds of the app (i.e.\na staging server), you can base this check on a different condition. For\nexample:\n\n```js\nwithFetchHar(App, {\n  enabled: process.env.DEPLOY_ENV !== \"production\"\n});\n```\n\nYou can also supply a function, which will be passed the same `ctx` object\nreceived by `getInitialProps`. You can use this to inspect `req` (or other\nproperties) for special superuser status or other conditions:\n\n```js\nwithFetchHar(App, {\n  enabled: ctx =\u003e ctx.req.user.isAdmin\n});\n```\n\n## Troubleshooting\n\n#### Some browser APIs like Headers, URLSearchParams, etc. are not found!\n\n[node-fetch-har](https://github.com/exogen/node-fetch-har) may require access to\nsome of the browser APIs that Fetch uses. While [node-fetch](https://github.com/bitinn/node-fetch)\ndoes export these, they aren’t necessarily globally available; some libraries\nlike [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) set\nthem on the `global` object for you.\n\nYou have two options:\n\n- Make them available on `global` yourself or using a library.\n- Pass them as options to `withFetchHar`, for example:\n\n  ```js\n  import { Response } from \"node-fetch\";\n\n  export default withFetchHar(App, { Response });\n  ```\n\n#### What if I don’t call fetch directly in my getInitialProps?\n\nYou’ll need to find some way to pass the `ctx.fetch` instance through to your\ncode that needs to call it. It’s not possible to expose the enhanced HAR-enabled\nFetch instance globally because it is scoped per request: it should only capture\nthe requests made by each page, even if multiple are being served in parallel.\n\nFor example, if you make API calls inside Redux actions using a side-effect\nsolution like [redux-thunk](https://github.com/reduxjs/redux-thunk), you can\npass `ctx.fetch` to your store creation function so that it can be supplied to\nany side-effect middleware.\n\nFor redux-thunk in particular, you can use its [withExtraArgument](https://github.com/reduxjs/redux-thunk#injecting-a-custom-argument)\nfeature to pass a custom object containing `fetch` and whatever else you like:\n\n```js\nfunction actionCreator() {\n  return async (dispatch, getState, context) =\u003e {\n    const response = await context.fetch(\"/api/foo\");\n  };\n}\n```\n\nFor scenarios where your store may sometimes have been created by `getInitialProps`\nand other times not, you can always fall back to vanilla Fetch, since we can\nonly capture activity during `getInitialProps` anyway:\n\n```js\nconst store = createStore(\n  reducer,\n  applyMiddleware(\n    thunk.withExtraArgument({\n      fetch: ctx ? ctx.fetch : global.fetch\n    })\n  )\n);\n```\n\n#### How do I prevent this from disabling automatic prerendering?\n\nNext.js’ [automatic prerendering](https://github.com/zeit/next.js#automatic-prerendering)\nfeature is disabled if your custom `\u003cApp\u003e` component defines a `getInitialProps`\nmethod. For most advanced apps, this is often inevitable, but if this library is\nthe only thing causing your app to have `getInitialProps`, then you might want\nto work around it.\n\nUsing the `enabled` option is not good enough to prevent the resulting `\u003cApp\u003e`\nfrom having `getInitialProps`, since `ctx.fetch` needs to be defined even when\nHAR logging is disabled (for your app to work whether enabled or disabled).\n\nSo, you’ll need to conditionally apply `withFetchHar` yourself and use a\nfallback wherever you access `ctx.fetch`:\n\n```js\nlet CustomApp = App;\n\nif (process.env.NODE_ENV !== \"production\") {\n  CustomApp = withFetchHar(CustomApp);\n}\n\nexport default CustomApp;\n```\n\nIn pages:\n\n```js\nstatic async getInitialProps({ fetch = global.fetch }) {\n  const response = await fetch('/api/foo');\n}\n```\n\nIf you are only interested in recording requests for certain pages, you may\nalso use the `withFetchHar` only on those page components, and prerendering\nshould not be affected on your other pages.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexogen%2Fnext-fetch-har","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexogen%2Fnext-fetch-har","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexogen%2Fnext-fetch-har/lists"}