Skip to content
/ Nixify Public

🌴 A tiny human-friendly JavaScript HTTP client library based on the browser with no dependencies.

License

Notifications You must be signed in to change notification settings

FxOmar/Nixify

Repository files navigation

Nixify HTTP Client Library

Nixify is a lightweight and minimalistic JavaScript HTTP client based on the browser's Fetch API with no dependencies. It's designed for simplicity and ease of use in browser environments, providing a concise API for making HTTP requests to various services.

Installation

Package manager

Using npm:

npm install nixify

Using pnpm:

pnpm add nixify

Using yarn:

yarn add nixify

To import Nixify you have to use ECMAScript

import Nixify from "nixify";

Using jsdelivr:

<script type="module">
  import nixify from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'
</script>

Nixify Features

  • Lightweight: Minimalistic HTTP client designed for simplicity.
  • First-class TypeScript Support: Developed entirely in TypeScript for a robust experience.
  • Fetch API Integration: Built on the browser's Fetch API for HTTP requests.
  • Shortcut Methods: Shorthand methods like Nixify.get().text() for readability.
  • Retry Request: automatic retry of failed requests based on status codes.
  • Configurable Services: Easily configure instances for different services.
  • Header Management: Set headers globally or for specific instances.
  • Dynamic Route Matching with Params: Support for dynamic route matching with parameters.
  • Hooks: Execute functions before/after requests, with a beforeRetry hook.
  • Automatic JSON Handling: Streamlined interaction with JSON responses.
  • Cancel Requests: Efficiently manage ongoing requests.
  • Concise API: Simple and easy-to-use for handling HTTP requests.

Usage

// Create an instance of Nixify with predefined services
const http = Nixify.create({
  github: {
    url: "https://api.github.com",
    headers: {
      "x-API-KEY": "[TOKEN]",
    },
  },
  gitlab: {
    url: "https://gitlab.com/api/v4",
	retryConfig: {
		retries: 4,
		retryOn: [400] // default statusCodes retryOn [408, 413, 429, 500, 502, 503, 504]
		retryDelay: 2000 // ms
	}

  },
});

// Set headers for a specific service instance
http.gitlab.setHeaders({ Authorization: `Bearer ${token}` });

// Set headers globally
http.setHeaders({ Authorization: `Bearer ${token}` });

// Set headers before making a request for a specific service instance
http.gitlab.beforeRequest((request, config) => {
  // Modify request headers or perform other actions
});

// Set headers globally before making a request
http.beforeRequest((request, config) => {
  request.headers.set("Content-type", "application/json");
});

// Will be called right after response
http.gitlab.afterResponse((request, response config) => {});
http.afterResponse((request, response config) => {});

// Will be called right before retry
http.beforeRetry((request, response, attempt, delay) => {});
http.gitlab.beforeRetry((request, response, attempt, delay) => {});


// Retry custom behavior
const { data, status } = await http.gitlab.get("/projects/:id/registry/repositories", {
	params: { id: 5645 } // /projects/5645/registry/repositories
	retry: {
		retryOn(attempt, response) {
			// Should stop retry by returning false
			if (attempt > 3) return false

			// retry on 4xx or 5xx status codes
			if (response.status >= 400) {
				console.log(`retrying, attempt number ${attempt + 1}`);
				return true;
			}
		}
	}
}).json();

// TypeScript Version
interface Repositories {}
const { data } = await http.gitlab.get<Repositories>("/search/repositories").json();

// Javascript Version
const { data, config } = await http
  .get("https://api.github.com/search/repositories", { headers: {} })
  .json();

const { data, status } = await http.github.get("/search/repositories").json();

/**
 * If the body of the HTTP request is an instance of URLSearchParams,
 * The Content-Type is set automatically by `fetch`, 
 * so we didn't have to explicit set it to 'application/x-www-form-urlencoded;charset=utf-8'.
 */
const searchParams = new URLSearchParams();
searchParams.set('food', 'sushi 🍣');
searchParams.set('drink', 'Bubble Tea πŸ§‹');

const { data } = await http.post(path, { body: searchParams })

/**
 * `fetch` automatically sets the Content-Type header,
 *  So we didn't have to explicit set it to 'multipart/form-data; boundary=---....'
 */
const formData = new FormData();
formData.append('username', 'superAdmin');
formData.append('password', 'admin1234');

const { data } = await http.post(path, { body: formData })

/**
 * Cancellation
 */
const controller = new AbortController();
const { signal } = controller;

setTimeout(() => {
	controller.abort();
}, 5000);

try {
	await http.get(url, { signal }).text();
} catch (error) {
	if (error.name === 'AbortError') {
		console.log('Fetch aborted');
	} else {
		console.error('Fetch error:', error);
	}
}

API DOCUMENTATION

Nixify.create(config: { [name: string]: Options }): ServiceReqMethods | RequestMethods

Creates an instance of Nixify with predefined service configurations.

Parameters:
  • config: An object containing service configurations.
Returns:
  • NixifyInstance: An instance of Nixify configured with the provided options.
Example:
const http = Nixify.create({
  github: {
    url: "https://api.github.com",
    headers: {
      "x-API-KEY": "[TOKEN]",
    },
  },
  gitlab: {
    url: "https://gitlab.com/api/v4/",
    headers: {},
  },
});
Nixify.beforeRequest(fn: (request: Request, config: Options) => void)
Nixify.{service}.beforeRequest(fn: (request: Request, config: Options) => void)

Prior to initiating a request for a particular service instance or globally, customize request headers or execute additional actions.

Parameters:
  • fn: A callback function to be invoked right before a request.
    • request: A representation of the Request API, encapsulating HTTP configurations.
    • config: An object with NixifyInstance configurations.
Example:
// Set headers before making a request for a specific service instance
http.gitlab.beforeRequest((request, config) => {
  // Modify request headers or perform other actions
});

// Set headers globally before making a request
http.beforeRequest((request, config) => {
  request.headers.set("Content-type", "application/json");
});
Nixify.afterResponse(fn: (request: Request, response: Response, config: Options) => void)
Nixify.{service}.afterResponse(fn: (request: Request, response: Response config: Options) => void)

Still under development.

Parameters:
  • fn: A callback function to be invoked right after a response.
    • request: A representation of the Request API, encapsulating HTTP configurations.
    • response: A representation of the Response API.
    • config: An object with NixifyInstance configurations.
Example:
http.gitlab.afterResponse((request, config) => {});
http.afterResponse((request, config) => {});
Nixify.beforeRetry(fn: (request: Request, response: Response, attempt: number, delay: number) => void)
Nixify.{service}.beforeRetry(fn: (request: Request, response: Response, attempt: number, delay: number) => void)

Registers a function to be executed before a fetch retry attempt within the Nixify service.

Parameters:

  • fn: A callback function to be invoked before a retry attempt.
    • request: A representation of the Request API, encapsulating HTTP configurations.
    • response: A representation of the Response API.
    • attempt: The number of the retry attempt.
    • delay: The delay before the next retry attempt.

Example:

http.beforeRetry((request, response, attempt, delay) => {
  if(response.status === 401) {
	const { data } = await http.get("/refresh-token").json()

	request.headers.set("X-API-KEY", data.token)
  }
});

http.{service}.beforeRetry((request, response, attempt, delay) => {
  // Your logic here
});

This method allows you to register a callback function that will be called before each retry attempt within the Nixify service. The callback function receives information about the request, response, the current attempt number, and the delay before the next retry.

Nixify.setHeaders(headers: { [key: string]: string })
Nixify.{service}.setHeaders(headers: { [key: string]: string })

Before making a request for a specific service instance or globally, modify request headers.

Parameters:
  • headers: An object containing headers.
Example:
// Set headers for a specific service instance
http.gitlab.setHeaders({ Authorization: `Bearer ${token}` });

// Set headers globally
http.setHeaders({ Authorization: `Bearer ${token}` });

Request method aliases

// We provided supported for all request methods.
http.get<T>(url | path, options?) // Returns an Object of callable type-setters methods.
// instead of `responseType`.
  json() // By default
  text()
  blob()
  arrayBuffer()
  formData()
http.delete(url | path, options?)

http.head(url | path, options?)

http.options(url | path, options?)

http.post(url[, body or json])

http.put(url[, body or json])

http.patch(url[, body or json])
Request Config
// These are the available `options?` for making requests. Only the url is required.
interface Options {
  url: string
  headers?: { [key: string]: string }
  hooks?: {
	beforeRequest: (request: Request) => void
	afterResponse: (request: Request, response: Response, config: any) => void
	beforeRetry: (request: Request, response: Response, attempt: number, delay: number) => void
  }
  qs?: {
	readonly strict?: boolean
	readonly encode?: boolean
	readonly arrayFormat?:
		| "bracket"
		| "index"
		| "comma"
		| "separator"
		| "bracket-separator"
		| "colon-list-separator"
		| "none"
	readonly arrayFormatSeparator?: string
	readonly sort?: ((itemLeft: string, itemRight: string) => number) | false
	readonly skipNull?: boolean
	readonly skipEmptyString?: boolean
  }
  timeout?: number | false
  retryConfig?: {
	retries?: number | boolean
	retryDelay?: number | (attempt: number, response: Response | null) => number
	retryOn?: number[] | (attempt: number, response: Response | null) => boolean | Promise<boolean> 
  }
}

// https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options
interface MethodConfig extends Omit<RequestInit, "method"> {
  // URL parameters to be sent with the request
  // (e.g http.get(path, { qs: { name: "Joe" } }) = path?name=Joe )
  qs?: { [name: string]: string | URLSearchParams | Record<string, string> | string[][] }
  // "/groups/:id/registry/repositories" - { id: 4873 }
  // "/groups/4873/registry/repositories"
  params?: { [key: string]: string | number }
  // `headers` are custom headers to be sent.
  headers?: Object;
  // `json` to send body as Content-Type JSON.
  json?: Object;
  //  `body` to send data under one of these types -
  body?:
    | Blob
    | BufferSource
    | FormData
    | URLSearchParams
    | USVString
    | ReadableStream;
  // `responseType` indicates the type of data that the server will respond with.
  responseType?: "json" | "text" | "blob" | "arrayBuffer" | "formData";
  timeout?: number | false
  retry?: {
	retries?: number | boolean
	retryDelay?: number | (attempt: number, response: Response | null) => number
	retryOn?: number[] | (attempt: number, response: Response | null) => boolean | Promise<boolean> 
  }
  // To cancel request using AbortController
  signal?: AbortController;
}

Response Schema

The response for a request contains the following information.

interface ResponseInterface<T> {
  // `data` is the response that was provided by the server
  data: T;
  // `headers` the HTTP headers that the server responded with
  // All header names are lower cased and can be accessed using the bracket notation.
  // Example: `response.headers['content-type']`
  headers: Headers;
  // `status` is the HTTP status code from the server response
  status: number;
  // `statusText` is the HTTP status message from the server response
  statusText: string;
  // `config` is the config that was provided to the request
  config: Request;
}

Contributing

We welcome contributions! Feel free to open issues, submit pull requests, or provide feedback. Make sure to follow our contribution guidelines.

Authors

License

This library is licensed under the MIT License.

About

🌴 A tiny human-friendly JavaScript HTTP client library based on the browser with no dependencies.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published