{"id":15553509,"url":"https://github.com/behzodfaiziev/next-netkit","last_synced_at":"2025-04-12T21:26:32.932Z","repository":{"id":257779359,"uuid":"859875636","full_name":"behzodfaiziev/next-netkit","owner":"behzodfaiziev","description":"React Netkit for Network Management","archived":false,"fork":false,"pushed_at":"2025-02-14T21:38:04.000Z","size":953,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-06T04:51:56.310Z","etag":null,"topics":["network","nextjs","react"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/next-netkit","language":"TypeScript","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/behzodfaiziev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-19T12:38:13.000Z","updated_at":"2025-02-14T21:38:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"b22a34c9-1cfd-4fcc-a875-6544b9529794","html_url":"https://github.com/behzodfaiziev/next-netkit","commit_stats":null,"previous_names":["behzodfaiziev/react-netkit","behzodfaiziev/next-netkit"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behzodfaiziev%2Fnext-netkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behzodfaiziev%2Fnext-netkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behzodfaiziev%2Fnext-netkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behzodfaiziev%2Fnext-netkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/behzodfaiziev","download_url":"https://codeload.github.com/behzodfaiziev/next-netkit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248633486,"owners_count":21136877,"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","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":["network","nextjs","react"],"created_at":"2024-10-02T14:36:03.304Z","updated_at":"2025-04-12T21:26:32.906Z","avatar_url":"https://github.com/behzodfaiziev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Next-Netkit\n\nNext-Netkit is a lightweight, injectable network manager built on top of Axios, designed to work\nseamlessly with Clean Architecture and dependency injection frameworks like Inversify.\nThis package is ideal for both TypeScript and JavaScript projects and supports test-driven\ndevelopment (TDD) by making network interactions mockable and testable.\n\n## Table of Contents\n\n- [Features](#features)\n- [Changelog](#changelog)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Setting Up the NetworkManager](#setting-up-the-networkmanager)\n  - [Making Requests](#making-requests)\n    - [Request](#request)\n    - [RequestList](#requestlist)\n    - [RequestVoid](#requestvoid)\n  - [Refresh Token](#refresh-token)\n    - [How to Configure Token Refresh](#how-to-configure-token-refresh)\n    - [How It Works](#how-it-works)\n  - [Ensuring Access Token is Refreshed Before Making a Request](#ensuring-access-token-is-refreshed-before-making-a-request)\n    - [Key Points](#key-points)\n      - [1. When to Use](#1-when-to-use)\n      - [2. How It Works](#2-how-it-works)\n      - [3. Fallback Handling](#3-fallback-handling)\n  - [Making Requests according to the Clean Architecture](#making-requests-according-to-the-clean-architecture)\n- [Integration with Inversify for Dependency Injection](#integration-with-inversify-for-dependency-injection)\n  - [Container Module Setup](#container-module-setup)\n  - [Merging Containers](#merging-containers)\n- [License](#license)\n\n  [//]: # \"[Error Handling with ApiException](#error-handling-with-apiexception-according-to-the-clean-architecture)\"\n\n## Features\n\n- **TypeScript-first**: Provides full type support and is easily usable in both TypeScript and\n  JavaScript projects.\n- **Axios Integration**: Built on top of Axios for flexible HTTP requests.\n- **Dependency Injection**: Supports `Inversify` for clean and testable architecture.\n- **Error Handling**: Customizable error handling using the `ApiException` class.\n- **Clean Architecture**: Easily integrate with Clean Architecture principles.\n- **Refresh Token Support**: Automatically refreshes the access token when it expires.\n\n## Changelog\n\nYou can find the changelog [here](CHANGELOG.md).\n\n## Installation\n\n```bash\nnpm install next-netkit axios inversify\n```\n\n## Usage\n\n### Setting Up the NetworkManager\n\nYou can create an instance of NetworkManager by passing the base URLs, mode\n(development or production), Axios configuration options, and error-handling parameters.\n\n```typescript\nimport { NetworkErrorParams, NetworkManager } from \"next-netkit\";\n\n// Define your error-handling parameters\nconst networkErrorParams: NetworkErrorParams = {\n  messageKey: \"message\",\n  statusCodeKey: \"status\",\n  couldNotParseError: \"Could not parse error\",\n  jsonIsEmptyError: \"JSON is empty\",\n  noInternetError: \"No internet connection\",\n  jsonNullError: \"JSON is null\",\n  jsonUnsupportedObjectError: \"JSON is unsupported object\",\n  notMapTypeError: \"Not map type\",\n};\n/// In here NODE_ENV is an environment variable that is set to 'production' or 'development'\n/// It may differ according to your project setup\nconst isTestMode = process.env.NODE_ENV !== \"production\";\n// Create a new instance of NetworkManager\nconst networkManagerInstance = new NetworkManager({\n  baseUrl: \"https://api.example.com\", // Production base URL\n  devBaseUrl: \"https://dev.example.com\", // Development base URL\n  testMode: isTestMode, // Test mode: false (production), true (development)\n  baseOptions: {}, // Axios config options\n  errorParams: networkErrorParams, // Error parameters\n  withCredentials: true,\n  refreshTokenPath: \"api/auth/refresh-token\",\n});\n```\n\n## Making Requests:\n\n### Request:\n\n`request` is used to fetch or send data where a single response model is expected.\n\n```typescript\n// Example GET request to fetch a single model\nconst product = await networkManager.request\u003cProductModel\u003e({\n  method: RequestMethod.GET,\n  url: \"/api/product/1\",\n});\n/// response.data is of type BookEntity\n\n// Example POST request and get response\nconst signInResponse = await networkManager.request\u003cSignInResponseDto\u003e({\n  method: RequestMethod.POST,\n  url: \"/api/auth/sign-in\",\n  data: signInRequestDto,\n});\n/// signInResponse.data is of type SignInResponseDto\n```\n\n### RequestList:\n\n`requestList` is used when you expect the API to return an array of items.\n\n```typescript\n// Example GET request to fetch a list of products\nconst products = await networkManager.requestList\u003cProductModel\u003e({\n  method: RequestMethod.GET,\n  url: \"/api/v1/products\",\n});\n/// response.data is of type ProductModel[]\n```\n\nThis method ensures the response is an array and throws an error if a non-list is returned.\n\n### RequestVoid:\n\n`requestVoid` is used for requests where no data is expected in return (e.g., DELETE or POST\noperations that don't return any data).\n\n```typescript\n// Example DELETE request with no response body expected\nawait networkManager.requestVoid({\n  method: RequestMethod.DELETE,\n  url: \"/api/v1/products/1\",\n});\n```\n\n## Refresh Token\n\nThe NetworkManager automatically handles token refresh when an access token expires. You only need\nto provide the API endpoint where the refresh token request is made. Once the access token expires,\nthe manager will automatically request a new one and retry the failed request with the new token.\n\n### How to Configure Token Refresh\n\n```typescript\nconst networkManagerInstance = new NetworkManager({\n  // Other options (e.g., baseUrl, etc.)\n  refreshTokenPath: \"api/auth/refresh-token\", // Path to the backend refresh token API\n});\n```\n\n### How It Works\n\n- **Token Expiry Detection**: When a request returns a 401 Unauthorized error due to an expired\n  token, NetworkManager detects this and triggers the refresh process.\n- **Token Refresh Request**: It sends a request to the provided refreshTokenPath to obtain a new\n  access token.\n- **Retrying Failed Requests**: Once the token is refreshed, it automatically retries the original\n  failed request with the new token.\n\n## Ensuring Access Token is Refreshed Before Making a Request\n\nIn some scenarios, you may want to ensure that an **access token is refreshed before making a\nrequest**, particularly for actions that cannot be repeated easily without potential issues.\n\n- For instance, when uploading a 100 MB video to a social media platform, you want to avoid\n  uploading the video twice in case the access token is expired. To handle such situations\n  gracefully, the request should explicitly ensure the access token is valid by triggering a token\n  refresh before making the main request.\n\nThis feature requires that the `refreshTokenPath` is correctly configured in the `NetworkManager`\nsettings. Without it, token refresh functionality will not work.\n\nHere’s an example of how to make such a request in TypeScript:\n\n```typescript\nconst product = await networkManager.request\u003cProductModel\u003e({\n  method: RequestMethod.GET,\n  url: \"/api/product/1\",\n  isTokenRefreshRequired: true, // Ensure token refresh is triggered before the request\n});\n```\n\n### Key Points:\n\n#### 1. When to Use:\n\n- Use **isTokenRefreshRequired: true** for requests that must be sent successfully and are critical\n  in\n  nature (e.g., uploading large files, important transactions).\n- This ensures the access token is refreshed if it has expired, preventing failure due to\n  unauthorized errors.\n\n#### 2. How It Works:\n\n- If **isTokenRefreshRequired** is set to **true**, the **NetworkManager** will first send a request\n  to refresh the access token (using the configured **refreshTokenPath**).\n- After refreshing the token, the main request will be executed.\n\n#### 3. Fallback Handling:\n\n- If the token refresh fails (e.g., due to an expired refresh token), the main request **will not\n  proceed**, and an error will be thrown to prevent **redundant** or **unauthorized** actions.\n\n## Making Requests according to the Clean Architecture\n\nUsing the Clean Architecture, you can create a `RemoteDataSource` class that implements an\ninterface, which can be injected into your repository class.\n\n```typescript\n/// src/feature-name/data/datasources/i-auth-remote-datasource.ts\nexport interface IAuthRemoteDataSource {\n  signIn(signInDto: SignInDto): Promise\u003cSignInResponseDto\u003e;\n}\n\n/// src/feature-name/data/datasources/auth-remote-datasource.ts\n@injectable()\nexport class AuthRemoteDataSource implements IAuthRemoteDataSource {\n  constructor(@inject(\"INetworkManager\") private networkManager: INetworkManager) {}\n\n  async signIn(dto: SignInDto): Promise\u003cSignInResponseDto\u003e {\n    return await this.networkManager.request\u003cSignInResponseDto\u003e({\n      method: RequestMethod.POST,\n      url: `/api/auth/sign-in`,\n      data: dto,\n    });\n  }\n}\n```\n\nNow, you can inject the `IAuthRemoteDataSource` into your repository class and use it to make\nnetwork requests.\n\n```typescript\n/// src/feature-name/data/repositories/auth-repository.ts\n@injectable()\nexport class AuthRepository implements IAuthRepository {\n  constructor(\n    @inject(\"IAuthRemoteDataSource\") private remoteDataSource: IAuthRemoteDataSource,\n    @inject(\"IAuthLocalDataSource\") private localDataSource: IAuthLocalDataSource\n  ) {}\n\n  async signIn(dto: SignInDto): Promise\u003cvoid\u003e {\n    try {\n      const response = await this.remoteDataSource.signIn(dto);\n      this.localDataSource.saveUser(response.user);\n    } catch (error) {\n      throw error;\n    }\n  }\n}\n```\n\n[//]: #\n[//]: # \"## Error Handling with ApiException according to the Clean Architecture\"\n[//]: #\n[//]: # \"All errors returned by the network manager will be transformed into `ApiException` instances,\"\n[//]: # \"providing consistent error-handling across your app. Which are caught with a try-catch block.\"\n[//]: #\n[//]: # \"```typescript\"\n[//]: # \"/// AuthController.ts\"\n[//]: # \"@injectable()\"\n[//]: # \"export class AuthController {\"\n[//]: # \"  constructor(@inject(SignIn) private signInUseCase: SignIn) {}\"\n[//]: #\n[//]: # \"  async handleSignIn(dto: SignInDto): Promise\u003cvoid\u003e {\"\n[//]: # \"    try {\"\n[//]: # \"      return await this.signInUseCase.execute(dto);\"\n[//]: # \"    } catch (error) {\"\n[//]: # \"      throw error;\"\n[//]: # \"    }\"\n[//]: # \"  }\"\n[//]: # \"}\"\n[//]: #\n[//]: # \"/// sign-in.tsx\"\n[//]: # \"/// ... other codes\"\n[//]: # \"const signInController = container.get\u003cAuthController\u003e(AuthController);\"\n[//]: #\n[//]: # \"const handleSignIn = async () =\u003e {\"\n[//]: # \"  try {\"\n[//]: # \"    const dto: SignInDto = { email, password };\"\n[//]: # \"    setLoading(true);\"\n[//]: # \"    await signInController.handleSignIn(dto);\"\n[//]: # '    router.push(\"/\");'\n[//]: # \"  } catch (err) {\"\n[//]: # \"    setLoading(false);\"\n[//]: # \"    setError((err as ApiException).message);\"\n[//]: # \"  }\"\n[//]: # \"};\"\n[//]: # \"/// ... other codes\"\n[//]: # \"```\"\n\n## Integration with Inversify for Dependency Injection\n\n`Next-Netkit` works seamlessly with `Inversify` to enable dependency injection. Here’s how you can\nset\nit up:\n\n### Container Module Setup\n\nCreate a module for the network manager using `Inversify`.\n\n```typescript\n// network.container.ts\nimport { ContainerModule, interfaces } from \"inversify\";\nimport { INetworkManager, NetworkManager, NetworkErrorParams } from \"next-netkit\";\n\nconst networkManagerInstance = new NetworkManager({\n  baseUrl: \"https://api.example.com\", // Production base URL\n  devBaseUrl: \"https://dev.example.com\", // Development base URL\n  testMode: isTestMode, // Test mode: false (production), true (development)\n  baseOptions: {}, // Axios config options\n  errorParams: networkErrorParams, // Error parameters\n  withCredentials: true,\n  refreshTokenPath: \"api/auth/refresh-token\",\n});\n\n// Create a network container module\nconst networkContainer = new ContainerModule((bind: interfaces.Bind) =\u003e {\n  bind\u003cINetworkManager\u003e(\"INetworkManager\").toConstantValue(networkManagerInstance);\n});\n\nexport { networkContainer };\n```\n\n### Merging Containers\n\nYou can merge multiple containers, including the network container, like so:\n\n```typescript\n// main.container.ts\nimport { Container } from \"inversify\";\nimport { authContainer } from \"./auth/auth.container\";\nimport { networkContainer } from \"./network.container\";\n\nconst container = new Container();\n\n// Merge containers\ncontainer.load(authContainer);\ncontainer.load(networkContainer);\n\nexport { container };\n```\n\n## License\n\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\n\n### Dependencies\n\n- **axios** - MIT License. See [axios repository](https://github.com/axios/axios) for license details.\n- **inversify** - MIT License. See [inversify repository](https://github.com/inversify/InversifyJS) for license details.\n- **reflect-metadata** - Apache-2.0 License. See [reflect-metadata repository](https://github.com/rbuckton/reflect-metadata) for license details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbehzodfaiziev%2Fnext-netkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbehzodfaiziev%2Fnext-netkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbehzodfaiziev%2Fnext-netkit/lists"}