cutefetch
v1.2.0
Published
A sleek and minimalistic HTTP client for modern web and Node.js applications, designed with simplicity and elegance in mind.
Downloads
26
Maintainers
Readme
What is CuteFetch ?
CuteFetch is a HTTP client, inspired by Axios, thats offers a sleek and minimalistic solution for modern web and Node.js applications
🧊 Tiny & Efficient: Only ~66.4 kB unpacked — perfect for modern apps.
Table of Content
Transformer
CuteFetch - Lightweight & Customizable HTTP Client
| Feature | Description |
| ----------------------------- | ---------------------------------------------------------------------------- |
| 🌟 Lightweight | A very lightweight library |
| ⚡ Fast & Simple | Easy to use |
| 🔄 Auto Parsing | Automatically parses JSON, text, blobs |
| 🎯 Customizable | Can be customized and extended as desired |
| 🔥 Modern Fetch-Based | Built based on the modern Fetch API |
| 🛠 Supports Extra Requests | cf.extra() instance method for complete control |
| ⏳ Timeout Support | Add timeout globally or to specific instance methods |
| 🔄 Transformer Properties | Allows customization of data parsing and transformation in response handling |
How to use?
npm install cutefetch # Or yarn add cutefetchIncludes in Project
/*For ESM*/
import CuteFetch from "cutefetch";
/*For CJS*/
const CuteFetch = require("cutefetch");Create CuteFetch instance
const cf = new CuteFetch({
baseURL: "https://typecode-api.vercel.app",
timeout: 12000,
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
mode: "cors",
cache: "force-cache",
credentials: "include",
});⚙️ CuteFetch Config Props
| Property | Description | Default | Optional |
| ------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | :------: |
| methods | Array of HTTP request methods you plan to use (GET, POST, etc.). Must be explicitly defined. | [] | ❌ |
| baseURL | Base URL to prepend to all requests | nothing | ✅ |
| resultProps | Key name where the final response data will be available in the result object | data | ✅ |
| credentials | Sets the credentials option for all HTTP requests | same-origin | ✅ |
| cache | Sets the cache behavior for requests | default | ✅ |
| mode | Sets the mode for the request | cors | ✅ |
| timeout | Global timeout duration (in milliseconds) for all requests unless overridden per-request | 5000 | ✅ |
| headers | Object of headers to be sent with every request (default headers can be overridden). | Default browser headers are included, such as User-Agent, Accept, Accept-Encoding, etc. | ✅ |
CuteFetch instance methods (6)
- cf.get()
Syntax: cf.get('/path-to-endpoint', { Request options }) - cf.post()
Syntax: cf.post('/path-to-endpoint', { Request options }) - cf.put()
Syntax: cf.put('/path-to-endpoint', { Request options }) - cf.patch()
Syntax: cf.patch('/path-to-endpoint', { Request options }) - cf.delete()
Syntax: cf.delete('/path-to-endpoint', { Request options }) - cf.extra()
Syntax: cf.extra('/path-to-endpoint', { Request options })
Request options properties:
🧰 CuteFetch - Request Properties
| Property | Default | Optional | Replaceable |
| ------------------------ | --------------------------------------------------------- | :------: | :------------: |
| body | undefined | ✅ | ✅ |
| query | undefined | ✅ | ✅ |
| headers | From CuteFetch config or default | ✅ | ✅ |
| timeout | From CuteFetch config or default | ✅ | ✅ |
| baseURL | From CuteFetch config or undefined | ✅ | ✅ |
| credentials | From CuteFetch config or default | ✅ | ✅ |
| mode | From CuteFetch config or default | ✅ | ✅ |
| cache | From CuteFetch config or default | ✅ | ✅ |
| method | Defined by CuteFetch method used (GET, POST, etc.) | ✅ | ❌ |
| transformResponse | User-defined transformer for successful responses | ✅ | not applicable |
| transformErrorResponse | User-defined transformer for error responses | ✅ | not applicable |
| inspect | Custom debugger/logging function (undefined by default) | ✅ | not applicable |
Example Request
Create a Basic Instance
const cf = new CuteFetch({
baseURL: "https://typecode-api.vercel.app",
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
timeout: 5000,
headers: {
"Content-Type": "application/json",
},
});1. GET
Example Success Response handling:
const { data, error } = await cf.get("/api/posts", {
timeout: 8000,
});
if (data) {
console.log(data);
} else {
console.log(error);
}// Console Output
{
success: true,
status: 200,
message: 'All Posts here',
data: [
{
userId: 1,
id: 1,
title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
body: 'quia et suscipit\n' +
'suscipit recusandae consequuntur expedita et cum\n' +
'reprehenderit molestiae ut ut quas totam\n' +
'nostrum rerum est autem sunt rem eveniet architecto'
},
{
userId: 1,
id: 2,
title: 'qui est esse',
body: 'est rerum tempore vitae\n' +
'sequi sint nihil reprehenderit dolor beatae ea dolores neque\n' +
'fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\n' +
'qui aperiam non debitis possimus qui neque nisi nulla'
},
...more
]
}Example Failed Response handling:
const { data, error } = await cf.get("/api/postss"); // Invalid endpoint
if (data) {
console.log(data);
} else {
console.log(error); // Error output in the console
}// Console Output
{
"success": false,
"status": 404,
"message": "Requested data will not be found"
}2. POST
const post = {
title: "তবে গল্পটা যদি আরও কিছুটা দুরে যেতো",
body: "মনে আছে? একদিন আমরা আঁকাশের তারা গুনছিলাম। তুমি বোকার মত বলেছিলে হ্যাঁ। আমি তোমাকে একটা খোঁচা দিলাম। অমনি তোমার হুঁশ ফিরলো আর বললে, ধুর দিনের বেলা কে তাঁরা গুনলো?",
userId: 1,
};
const { data, error } = await cf.post("/api/posts", {
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(post),
});
if (data) {
console.log(data);
} else {
console.log(error); // No Error
}// Console Output
{
success: true,
status: 201,
message: 'Post Successfully created',
data: {
title: 'তবে গল্পটা যদি আরও কিছুটা দুরে যেতো',
body: 'মনে আছে? একদিন আমরা আঁকাশের তারা গুনছিলাম। তুমি বোকার মত বলেছিলে হ্যাঁ। আমি তোমাকে একটা খোঁচা দিলাম। অমনি তোমার হুঁশ ফিরলো আর বললে, ধুর দিনের বেলা কে তাঁরা গুনলো?',
userId: 1,
id: 101
}
}3. PUT
const { data, error } = await cf.put("/api/posts/1", {
body: JSON.stringify({
title: "foo",
body: "bar",
userId: 1,
}),
});
if (data) {
console.log(data);
} else {
console.log(error); // No Error
}// Console Output
{
success: true,
status: 202,
message: 'Post updated successfully',
data: { title: 'foo', body: 'bar', userId: 1, id: 1 }
}4. PATCH
const { data, error } = await cf.patch("/api/posts/1", {
body: JSON.stringify({
title: "তুমি আমায় ডেকেছিলে এক মেঘে ঢাকা দিনে।",
}),
});
if (data) {
console.log(data);
} else {
console.log(error); // No Error
}// Console Output
{
success: true,
status: 202,
message: 'Post partially updated!',
data: {
userId: 1,
id: 1,
title: 'তুমি আমায় ডেকেছিলে এক মেঘে ঢাকা দিনে।',
body: 'quia et suscipit\n' +
'suscipit recusandae consequuntur expedita et cum\n' +
'reprehenderit molestiae ut ut quas totam\n' +
'nostrum rerum est autem sunt rem eveniet architecto'
}
}5. DELETE
const { data, error } = await cf.delete("/api/posts/5");
if (data) {
console.log(data);
} else {
console.log(error); // No Error
}// Console Output
{ success: true, status: 200, message: 'Post deleted id: 5' }EXTRA - Methods
This method can handle any HTTP Request. Additionally, it gives the Developer more control. However, the extra method returns a Promise Response, so if you use it, you will be responsible for handling the Promise Response yourself
It's worth noting that HEAD and OPTIONS requests can also be used via the extra method.
GET request with extra method:
const response = await cf.extra("/api/posts", {
method: "GET",
query: {
_limit: "1",
_start: "2",
},
});
const { status, statusText, ok /*... and more*/ } = response;
if (!ok) {
// Some Error handling Logic
console.log(await response.json());
} else {
// manipulate respone data using response method e.g. response.text(),response.json()
console.log(await response.json());
}// Console Output
{
success: true,
status: 200,
message: 'All Posts here',
data: [
{
userId: 1,
id: 3,
title: 'ea molestias quasi exercitationem repellat qui ipsa sit aut',
body: 'et iusto sed quo iure\n' +
'voluptatem occaecati omnis eligendi aut ad\n' +
'voluptatem doloribus vel accusantium quis pariatur\n' +
'molestiae porro eius odio et labore et velit aut'
}
]
}Similarly, other request methods can be applied through this extra method.
inspect Property in CuteFetch Request
The inspect property is an optional feature that allows you to inspect and log detailed information about a request and its components before it's sent. You can use this feature to debug, log, or track specific aspects of a request's execution. The property is an object containing two parts:
name_space: The name for the specific inspection process, which helps in identifying logs or outputs related to this request.rule: This is an object where you can specify which parts of the request to inspect. It includes options to inspect various properties of the request, such as:methods: Whether to inspect the HTTP methods used.method: The specific HTTP method (e.g.,GET,POST).timeout: The timeout value for the request.credentials: The credentials used in the request.mode: The mode for the request (cors,no-cors, etc.).cors: Whether Cross-Origin Resource Sharing (CORS) settings are enabled.cache: The cache settings for the request.baseURL: The base URL used for the request.full_url: The complete URL formed by combiningbaseURLandqueryparameters.headers: The headers set for the request.query: The query parameters included in the request.body: The body content of the request.
callback: A function that gets called once the inspection is done. This function receives the result of the inspection, allowing you to extract and manipulate information before or after the request is made. You can use this callback to log the inspected data or perform any additional tasks.
Example
const { error, data, status, statusText } = await cf.put("/posts/1", {
body: JSON.stringify({
title: "It's Ababil Nefren",
description: "A programmer whose brain is not static!",
}),
query: {
param_a: "i am a",
param_b: "i am b",
},
inspect: () => ({
name_space: "post data fetch",
rule: {
// methods: true,
// method: true,
// timeout: true,
// credentials: true,
// mode: true,
// cors: true,
// cache: true,
baseURL: true,
full_url: true,
// headers: true,
query: true,
body: true,
},
callback: (result) => {
// Way 1: Logs the whole inspected object (the full result)
console.log(result["post data fetch"]);
// Way 2: Correct way to extract data using the extract method
const { body, query } = result.extract();
if (body) {
console.log("Body content: ", JSON.parse(body)); // Log the body content (if available)
}
console.log("Query parameters: ", query); // Log query parameters
},
}),
});How it works:
name_space: This helps you identify logs specific to this request.rule: By setting this totrue, you specify which request parts you want to inspect. In the example above, we're inspecting thebaseURL,full_url,query, andbodyproperties.callback: Thecallbackfunction is executed with the result of the inspection. You can then extract specific properties (e.g.,bodyandquery) and perform actions like logging them or manipulating the data.
Notes:
- Use the
inspectfeature to keep track of request parameters and details for debugging or logging purposes. - The callback function gives you flexibility to log, process, or store any information you need from the request.
This feature is powerful for debugging or understanding how requests are made under the hood.
Transformer: transformResponse and transformErrorResponse
Overview
transformResponse: This is a function that allows you to modify or transform the response data before it's made available to your code. It's called only when the request succeeds.transformErrorResponse: This is a function that lets you modify or transform the error response before it's made available to your code. It is called only in the case of an error (i.e., when the request fails).
Properties
transformResponse
- Purpose: Allows you to transform the response data after the request has succeeded.
- Arguments: The function receives the
data(the successful response) as its argument. - Return Value: You should return the modified data from this function. The returned data will be passed to the
dataproperty in the response object.
Example:
const { data, error } = await cf.get("/some-endpoint", {#
transformResponse: (data) => {
// Example: Convert response data to uppercase before returning
return data.toUpperCase();
},
});In this example, any data returned from /some-endpoint will be transformed to uppercase before it's made available to your code.
transformErrorResponse
- Purpose: Allows you to transform the error response in case of a failed request.
- Arguments: The function receives the
error(the failed response) as its argument. - Return Value: You should return the modified error from this function. The returned error will be passed to the
errorproperty in the response object.
Example:
const { data, error } = await cf.get("/some-endpoint", {
transformErrorResponse: (error) => {
// Example: Add custom message to error
return {
...error,
message: "Something went wrong while fetching data",
};
},
});In this example, if the request fails, the error will be transformed by adding a custom message to it before it's made available in the error property.
Full Example with Both transformResponse and transformErrorResponse:
const { error, data, status, statusText } = await cf.put("/posts/1", {
body: JSON.stringify({
title: "It's Ababil Nefren",
description: "A programmer whose brain is not static!",
}),
query: {
param_a: "i am a",
param_b: "i am b",
},
transformResponse: (data) => {
// Modify the response data here, e.g., logging it or altering it
console.log("Original Data:", data);
return {
...data,
transformed: true, // Add a custom property to the response
};
},
transformErrorResponse: (error) => {
// Modify the error response here, e.g., adding custom properties
console.log("Error Data:", error);
return {
...error,
customError: "There was an issue with your request.",
};
},
});
if (data) {
log.info("Transformed Data:", data);
} else {
log.info("Transformed Error:", error);
}Explanation:
transformResponseis used to modify the successful response data. In the example above, the original data is logged, and a new propertytransformed: trueis added to the response.transformErrorResponseis used to modify the error response. Here, the original error is logged, and a new custom error message is added.
When to Use:
- Use
transformResponseif you need to format or modify the data before your application uses it. - Use
transformErrorResponseto adjust error handling or add custom messages, logging, or other properties to errors.
Custom Transformers for Response and Error Handling
Overview
transformResponseandtransformErrorResponseare not part of the global configuration. Instead, they are optional features that developers can implement as needed.- Developers have the flexibility to create their custom higher-order functions for transforming responses and errors at the application level.
- By allowing developers to define their own transformers, they can tailor the response and error handling to their specific needs, without a top-level enforced configuration.
How to Implement Custom Transformers
You can create custom functions for transforming both responses and errors within your application by defining them at the request level.
1. transformResponse (Custom Response Transformer)
This function lets you manipulate the successful response data. It can be implemented at the request level to customize how the response data is processed before it reaches your application.
Example:
const { data, error } = await cf.get("/some-endpoint", {
transformResponse: (data) => {
// Custom transformation logic
// Example: Log the original data and modify it
console.log("Original Data:", data);
return {
...data,
modified: true, // Add custom properties or modifications
};
},
});In this example, the developer is free to log or transform the data returned from the endpoint as needed.
2. transformErrorResponse (Custom Error Transformer)
Similarly, you can define how to handle errors when a request fails by using transformErrorResponse. This function gives you full control over the error response data.
Example:
const { data, error } = await cf.get("/some-endpoint", {
transformErrorResponse: (error) => {
// Custom error transformation
// Example: Add additional info to the error
console.log("Error Data:", error);
return {
...error,
customErrorMessage: "An error occurred, please try again later.",
};
},
});In this case, if the request fails, the developer can modify the error response to include a custom message or perform additional actions like logging.
Flexibility to Create Higher-Order Transformers
Since the transformResponse and transformErrorResponse functions are not provided by default in the global config, developers can create higher-order functions to use as transformers across their application.
Example: Custom Higher-Order Function for Transformer
Here’s how you might create a custom higher-order function that wraps the transformer logic for reuse:
function createResponseTransformer(transformer) {
return function (data) {
// Apply additional transformations or logic if needed
return transformer(data);
};
}
function createErrorTransformer(errorTransformer) {
return function (error) {
// Apply additional error handling logic if needed
return errorTransformer(error);
};
}
// Usage:
const customResponseTransformer = createResponseTransformer((data) => {
console.log("Custom Response:", data);
return { ...data, custom: true };
});
const customErrorTransformer = createErrorTransformer((error) => {
console.error("Custom Error:", error);
return { ...error, errorMessage: "A custom error occurred" };
});
const { data, error } = await cf.get("/some-endpoint", {
transformResponse: customResponseTransformer,
transformErrorResponse: customErrorTransformer,
});In this approach:
- Developers define their own transformer logic.
- Custom transformers can be easily reused in various requests.
When to Implement Custom Transformers
- Use
transformResponsewhen you need to modify the data received from a successful request (e.g., to format or enrich the data before it's used in your application). - Use
transformErrorResponseto adjust error handling (e.g., to add additional context, log errors, or format error messages in a specific way). - Developers can implement custom logic by creating higher-order functions, allowing flexible handling of both responses and errors.
This setup gives developers full control over the transformation process, allowing them to create higher-order functions or individual transformers as needed while avoiding enforcing global configurations.
Error and Exceptions
You need to add the corresponding HTTP method in the CuteFetch Configuration for each CuteFetch instance method you intend to use.
For example: If you want to use the cf.get() method, you need to add it to the CuteFetch Configuration.
const cf = new CuteFetch({
methods: ["GET"],
...others,
});If you call the cf.get() method without adding "GET" to the methods array in the CuteFetch configuration, you will see the following error:
Similarly, you need to add all the HTTP Request methods you intend to use to the methods Array in the CuteFetch Configuration.
If you mistakenly change the HTTP Request method, you will see the following error.
For example:
const result = await cf.get("/posts", {
method: "POST",
});Note: It is pointless to try to modify the Request method of these five instance methods: cf.get(), cf.post(), cf.put(), cf.patch(), cf.delete().
If you use the cf.extra() method, you must include the Request Method within the options.
For example:
const response = await cf.extra("/posts", {
method: "GET",
});
const { status, statusText, ok /*... and more*/ } = response;
if (!ok) {
// Some Error handling Logic
} else {
// manipulate respone data using response method e.g. response.text(),response.json()
}If you don't do this, you will see the following error:
For example:
const response = await cf.extra("/posts", {});Bodyless HTTP request for CuteFetch:
GET HEAD OPTIONS DELETEIf you include a body property in the options for the mentioned Bodyless HTTP requests, you will observe the following error.
For example:
(async () => {
try {
const response = await cf.extra("/posts/1", {
method: "HEAD",
body: JSON.stringify({ name: "Ababil", age: undefined }),
});
if (response.ok) {
console.log(await response.headers);
}
} catch (error) {
console.log(error);
}
})();How to use it in plain HTML
app.js
// Import CuteFetch from unpkg (ESM)
import CuteFetch from "https://www.unpkg.com/[email protected]/dist/index.mjs";
// create CuteFetch instance
const cf = new CuteFetch({
baseURL: "https://typecode-api.vercel.app",
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
timeout: 12000,
headers: {
"Content-Type": "application/json",
},
});
// use intance method
cf.get("/api/posts", {
query: {
_page: "1",
_limit: "3",
},
})
.then(console.log)
.catch(console.log);index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- linkup app.js in html -->
<script type="module" src="./app.js"></script>
</body>
</html>Inside browser console
License ⚖️
Feel free to use, modify, and distribute under the terms of this license. 💻
💡 Permission:
- Commercial use allowed 💼
- Modification allowed 🔧
- Distribution allowed 📦
🚫 Conditions:
- Must include this license notice in all copies or substantial portions of the software 📜.
- Provide attribution to the original author 🙏.
This project is licensed under the MIT License 📝.
