npm package: @niteen5/api-nexus
Description: A sleek, typed API orchestration layer supporting native fetch (default) or axios.
Author: Niteen Kumar
Latest version: 0.0.2

# @niteen5/api-nexus

> A sleek, fully-typed API orchestration layer for React, Next.js, and any JavaScript/TypeScript project. Supports native **fetch** (default, zero dependencies) or **axios** — your choice.

[![npm version](https://img.shields.io/npm/v/@niteen5/api-nexus)](https://www.npmjs.com/package/@niteen5/api-nexus)
[![license](https://img.shields.io/npm/l/@niteen5/api-nexus)](./LICENSE)

---

## ✨ Features

- 🔁 **Adapter pattern** — swap between `fetch` and `axios` with one config line
- 🔐 **Built-in auth** — pass a `getToken()` function, Bearer headers are handled automatically
- 🧩 **Strongly typed endpoints** — your endpoint map gives you full IntelliSense on every `call()`
- 📦 **Zero mandatory dependencies** — native `fetch` is the default; axios is optional
- 🛤️ **Path params & query strings** — built-in support for `{id}` replacements and query serialization
- 🧼 **Consistent response shape** — every call returns `{ isSuccess, data, error, status, message }`
- ⚡ **CLI scaffold** — run `npx api-nexus` to generate boilerplate in seconds

---

## 📦 Installation

```bash
npm install @niteen5/api-nexus
# or
yarn add @niteen5/api-nexus
# or
pnpm add @niteen5/api-nexus
```

> If you want to use **axios** as the HTTP client, install it too:
> ```bash
> npm install axios
> ```

---

## ⚡ Quickest Start — CLI Scaffold

Run this in your project root right after install and you're done:

```bash
npx api-nexus
```

This generates a ready-to-use `src/api/` folder for you:

```
src/
└── api/
    ├── endpoints.ts   ← your endpoint map (edit this)
    └── index.ts       ← your configured api instance (ready to import)
```

It will ask if you have axios installed and set up the right adapter automatically. That's it — skip the manual steps below if you used the CLI.

---

## 🚀 Manual Setup

### 1. Define your endpoints

> [!IMPORTANT]
> **The key prefix IS the HTTP method — this is mandatory, not a convention.**
>
> `api-nexus` reads the part before the first `_` in your key name to determine how to make the request. There is no separate `method` field — **the key name is your method.**
>
> | Key prefix | HTTP method sent |
> |------------|-----------------|
> | `GET_`     | GET             |
> | `POST_`    | POST            |
> | `PUT_`     | PUT             |
> | `PATCH_`   | PATCH           |
> | `DELETE_`  | DELETE          |
>
> ✅ `GET_USERS` → sends a `GET` request  
> ✅ `POST_LOGIN` → sends a `POST` request  
> ❌ `FETCH_USERS` → **will NOT work** — `fetch` is not a valid HTTP method  
> ❌ `LOAD_DATA` → **will NOT work** — always start your key with a valid HTTP verb

```ts
// src/api/endpoints.ts
export const ENDPOINTS = {
  GET_USERS:    "users",          // → GET  /users
  GET_USER:     "users/{id}",     // → GET  /users/42
  POST_LOGIN:   "auth/login",     // → POST /auth/login
  POST_SIGNUP:  "auth/signup",    // → POST /auth/signup
  PUT_PROFILE:  "profile/{id}",   // → PUT  /profile/1
  DELETE_POST:  "posts/{id}",     // → DELETE /posts/99
} as const;
```

### 2. Create your API instance

```ts
// src/api/index.ts
import { createApiNexus } from "@niteen5/api-nexus";
import { ENDPOINTS } from "./endpoints";

export const api = createApiNexus({
  baseUrl: process.env.NEXT_PUBLIC_API_URL ?? "",
  endpoints: ENDPOINTS,
  httpClient: "fetch",           // "fetch" (default) | "axios"
  getToken: () => localStorage.getItem("token"),  // optional
});
```

### 3. Call your API

```ts
const result = await api.call<User[]>("GET_USERS");

if (result.isSuccess) {
  console.log(result.data); // typed as User[]
} else {
  console.error(result.message, result.status);
}
```

---

## 📖 Usage Examples

### GET with query params

```ts
const result = await api.call<User[]>("GET_USERS", {
  queryParams: { page: 1, limit: 10, search: "john" },
});
```

### GET with path params

```ts
const result = await api.call<User>("GET_USER", {
  params: { id: "42" },
});
// Calls: GET /users/42
```

### POST with a body

```ts
const result = await api.call<{ token: string }>("POST_LOGIN", {
  payload: { email: "user@example.com", password: "secret" },
});
```

### PUT / PATCH / DELETE

The HTTP method is inferred from the **prefix of your endpoint key**:

| Key prefix | HTTP method |
|------------|-------------|
| `GET_`     | GET         |
| `POST_`    | POST        |
| `PUT_`     | PUT         |
| `PATCH_`   | PATCH       |
| `DELETE_`  | DELETE      |

```ts
await api.call("PUT_PROFILE",  { params: { id: "1" }, payload: { name: "Niteen" } });
await api.call("DELETE_POST",  { params: { id: "99" } });
```

### Pass extra fetch/axios config

```ts
const result = await api.call<Blob>("GET_USERS", {
  config: { cache: "no-store" },   // Standard RequestInit options
});
```

---

## ⚙️ Configuration Reference

```ts
createApiNexus({
  baseUrl:    string,                      // Required — your API base URL
  endpoints:  Record<string, string>,      // Required — your endpoint map
  httpClient: "fetch" | "axios",           // Optional — default: "fetch"
  getToken:   () => string | null,         // Optional — for Authorization header
})
```

---

## 📬 Response Shape

Every `api.call()` call returns a consistent, typed `IResponseFormat<T>`:

```ts
interface IResponseFormat<T> {
  isSuccess: boolean;
  data?:     T;
  error?:    unknown;
  message?:  string;
  status?:   number;
}
```

You never need to write a try/catch — errors are caught internally and always returned as `{ isSuccess: false, message, status }`.

---

## 🔌 Using with React

```tsx
// hooks/useUsers.ts
import { useEffect, useState } from "react";
import { api } from "../api";

export function useUsers() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    api.call<User[]>("GET_USERS").then((res) => {
      if (res.isSuccess && res.data) setUsers(res.data);
      setLoading(false);
    });
  }, []);

  return { users, loading };
}
```

---

## ⚡ Using with Next.js

```ts
// app/users/page.tsx  (Next.js App Router — Server Component)
import { createApiNexus } from "@niteen5/api-nexus";
import { ENDPOINTS } from "@/api/endpoints";

const api = createApiNexus({
  baseUrl: process.env.API_URL!,
  endpoints: ENDPOINTS,
  httpClient: "fetch",
  // No getToken needed on the server — pass the token per-request via config:
});

export default async function UsersPage() {
  const result = await api.call<User[]>("GET_USERS", {
    config: { next: { revalidate: 60 } } as RequestInit, // Next.js ISR
  });

  if (!result.isSuccess) return <p>Error: {result.message}</p>;

  return <ul>{result.data?.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
```

---

---

## 🔄 Switching Between fetch and axios

```ts
// Using native fetch (default — no extra install needed)
const api = createApiNexus({ baseUrl, endpoints, httpClient: "fetch" });

// Using axios (run: npm install axios)
const api = createApiNexus({ baseUrl, endpoints, httpClient: "axios" });
```

Both adapters produce the same `IResponseFormat<T>` response, so your application code never changes.

---

## 🧑‍💻 TypeScript Support

The library is written entirely in TypeScript and ships its own type definitions — no `@types/` package needed.

Your endpoint keys are typed automatically:

```ts
api.call("INVALID_KEY"); // ❌ TypeScript error — not in your endpoints map
api.call("GET_USERS");   // ✅
```

---

## 📄 License

MIT © [Niteen Kumar](https://github.com/niteen5)