diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml
index f4176095..795de807 100644
--- a/.github/workflows/codecov.yml
+++ b/.github/workflows/codecov.yml
@@ -52,4 +52,5 @@ jobs:
./packages/node-cache/coverage/lcov.info, \
./packages/memoize/coverage/lcov.info, \
./packages/memory/coverage/lcov.info, \
+ ./packages/net/coverage/lcov.info, \
./packages/utils/coverage/lcov.info
diff --git a/packages/net/LICENSE b/packages/net/LICENSE
new file mode 100644
index 00000000..9d6198a9
--- /dev/null
+++ b/packages/net/LICENSE
@@ -0,0 +1,19 @@
+MIT License & © Jared Wray
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/packages/net/README.md b/packages/net/README.md
new file mode 100644
index 00000000..25188d4e
--- /dev/null
+++ b/packages/net/README.md
@@ -0,0 +1,43 @@
+[
](https://github.com/jaredwray/cacheable)
+
+> High Performance Network Caching for Node.js with fetch, request, http 1.1, and http 2 support
+
+[](https://codecov.io/gh/jaredwray/cacheable)
+[](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml)
+[](https://www.npmjs.com/package/@cacheable/net)
+[](https://www.npmjs.com/package/@cacheable/net)
+[](https://github.com/jaredwray/cacheable/blob/main/LICENSE)
+
+
+Features:
+* `fetch` from [undici](https://github.com/nodejs/undici) cache enabled via `cacheable`
+* `fetch` quick helpers such as `get`, `post`, `put`, and `delete` for easier development
+* `request` from [undici](https://github.com/nodejs/undici) cache enabled via `cacheable`
+* HTTP/1.1 and HTTP/2 caching support via Node.js `http` and `https` modules
+* [RFC 7234](http://httpwg.org/specs/rfc7234.html) compliant HTTP caching for native Node.js HTTP/HTTPS requests
+* Drop in replacement for `http` `https`, `fetch` modules with caching enabled
+* DNS caching for `dns.lookup` and `dns.resolve` methods via `cacheable`
+* WHOIS caching for `whois.lookup` method via `cacheable`
+* Advanced key generation via built in hashing and custom key generation functions
+* Benchmarks for performance comparison
+* All the features of [cacheable](https://npmjs.com/package/cacheable) - layered caching, LRU, expiration, hooks, backed by Keyv, and more!
+* Highly Tested and Maintained on a regular basis with a focus on performance and reliability
+
+# Table of Contents
+* [Getting Started](#getting-started)
+* [How to Contribute](#how-to-contribute)
+* [License and Copyright](#license-and-copyright)
+
+# Getting Started
+
+```bash
+npm install @cacheable/net
+```
+
+
+# How to Contribute
+
+You can contribute by forking the repo and submitting a pull request. Please make sure to add tests and update the documentation. To learn more about how to contribute go to our main README [https://github.com/jaredwray/cacheable](https://github.com/jaredwray/cacheable). This will talk about how to `Open a Pull Request`, `Ask a Question`, or `Post an Issue`.
+
+# License and Copyright
+[MIT © Jared Wray](./LICENSE)
diff --git a/packages/net/package.json b/packages/net/package.json
new file mode 100644
index 00000000..09bd9f36
--- /dev/null
+++ b/packages/net/package.json
@@ -0,0 +1,77 @@
+{
+ "name": "@cacheable/net",
+ "version": "1.0.0",
+ "description": "High Performance Network Caching for Node.js with fetch, request, http 1.1, and http 2 support",
+ "type": "module",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "require": "./dist/index.cjs",
+ "import": "./dist/index.js"
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jaredwray/cacheable.git",
+ "directory": "packages/cacheable"
+ },
+ "author": "Jared Wray ",
+ "license": "MIT",
+ "private": false,
+ "scripts": {
+ "build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
+ "prepublish": "pnpm build",
+ "test": "xo --fix && vitest run --coverage",
+ "test:ci": "xo && vitest run --coverage",
+ "clean": "rimraf ./dist ./coverage ./node_modules"
+ },
+ "devDependencies": {
+ "@faker-js/faker": "^9.9.0",
+ "@types/eslint": "^9.6.1",
+ "@types/node": "^24.1.0",
+ "@vitest/coverage-v8": "^3.2.4",
+ "rimraf": "^6.0.1",
+ "tsup": "^8.5.0",
+ "typescript": "^5.8.3",
+ "vitest": "^3.2.4",
+ "xo": "^1.2.1"
+ },
+ "dependencies": {
+ "cacheable": "workspace:^",
+ "hookified": "^1.10.0",
+ "undici": "^7.13.0"
+ },
+ "keywords": [
+ "cacheable",
+ "http caching",
+ "fetch caching",
+ "request caching",
+ "http 1.1 caching",
+ "http 2 caching",
+ "dns caching",
+ "whois caching",
+ "high performance",
+ "layer 1 caching",
+ "layer 2 caching",
+ "distributed caching",
+ "keyv",
+ "expiration",
+ "CacheableMemory",
+ "distributed sync",
+ "secondary store",
+ "primary store",
+ "cache statistics",
+ "layered caching",
+ "fault tolerant",
+ "in-memory cache",
+ "distributed cache",
+ "lru",
+ "multi-tier cache"
+ ],
+ "files": [
+ "dist",
+ "LICENSE"
+ ]
+}
diff --git a/packages/net/src/fetch.ts b/packages/net/src/fetch.ts
new file mode 100644
index 00000000..d6c157bb
--- /dev/null
+++ b/packages/net/src/fetch.ts
@@ -0,0 +1,38 @@
+import {type Cacheable, type CacheableOptions} from 'cacheable';
+import {fetch as undiciFetch, type RequestInit, type Response as UndiciResponse} from 'undici';
+
+export type FetchOptions = Omit & {
+ cache: Cacheable;
+};
+
+/**
+ * Fetch data from a URL with optional request options.
+ * @param {string} url The URL to fetch.
+ * @param {FetchOptions} options Optional request options. The `cacheable` property is required and should be an
+ * instance of `Cacheable` or a `CacheableOptions` object.
+ * @returns {Promise} The response from the fetch.
+ */
+export async function fetch(url: string, options: FetchOptions): Promise {
+ if (!options.cache) {
+ throw new Error('Fetch options must include a cache instance or options.');
+ }
+
+ const fetchOptions: RequestInit = {
+ ...options,
+ cache: 'no-cache',
+ };
+
+ return options.cache.getOrSet(url, async () => {
+ // Perform the fetch operation
+ const response = await undiciFetch(url, fetchOptions);
+ /* c8 ignore next 3 */
+ if (!response.ok) {
+ throw new Error(`Fetch failed with status ${response.status}`);
+ }
+
+ return response;
+ }) as Promise;
+}
+
+export type Response = UndiciResponse;
+export type {RequestInit as FetchRequestInit} from 'undici';
diff --git a/packages/net/src/index.ts b/packages/net/src/index.ts
new file mode 100644
index 00000000..6b18973f
--- /dev/null
+++ b/packages/net/src/index.ts
@@ -0,0 +1,50 @@
+import {Hookified, type HookifiedOptions} from 'hookified';
+import {Cacheable, type CacheableOptions} from 'cacheable';
+import {
+ fetch, type FetchOptions, type Response as FetchResponse, type FetchRequestInit,
+} from './fetch.js';
+
+export type CacheableNetOptions = {
+ cache?: Cacheable | CacheableOptions;
+} & HookifiedOptions;
+
+export class CacheableNet extends Hookified {
+ private _cache: Cacheable = new Cacheable();
+
+ constructor(options?: CacheableNetOptions) {
+ super(options);
+
+ if (options?.cache) {
+ this._cache = options.cache instanceof Cacheable ? options.cache : new Cacheable(options.cache);
+ }
+ }
+
+ public get cache(): Cacheable {
+ return this._cache;
+ }
+
+ public set cache(value: Cacheable) {
+ this._cache = value;
+ }
+
+ /**
+ * Fetch data from a URL with optional request options. Will use the cache that is already set in the instance.
+ * @param {string} url The URL to fetch.
+ * @param {FetchRequestInit} options Optional request options.
+ * @returns {Promise} The response from the fetch.
+ */
+ public async fetch(url: string, options?: FetchRequestInit): Promise {
+ const fetchOptions: FetchOptions = {
+ ...options,
+ cache: this._cache,
+ };
+
+ return fetch(url, fetchOptions);
+ }
+}
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+export const Net = CacheableNet;
+export {
+ fetch, type FetchOptions, type Response as FetchResponse, type FetchRequestInit,
+} from './fetch.js';
diff --git a/packages/net/test/fetch.test.ts b/packages/net/test/fetch.test.ts
new file mode 100644
index 00000000..87313258
--- /dev/null
+++ b/packages/net/test/fetch.test.ts
@@ -0,0 +1,40 @@
+import process from 'node:process';
+import {describe, test, expect} from 'vitest';
+import {faker} from '@faker-js/faker';
+import {Cacheable} from 'cacheable';
+import {fetch, type FetchOptions} from '../src/fetch.js';
+
+const testUrl = process.env.TEST_URL ?? 'https://mockhttp.org';
+const testTimeout = 10_000; // 10 seconds
+
+describe('Fetch', () => {
+ test('should fetch data successfully', async () => {
+ const url = `${testUrl}/get`;
+ const options: FetchOptions = {
+ method: 'GET',
+ cache: new Cacheable(),
+ };
+ const response = await fetch(url, options);
+ expect(response).toBeDefined();
+ }, testTimeout);
+
+ test('should fetch data successfully from cache', async () => {
+ const cache = new Cacheable({stats: true});
+ const url = `${testUrl}/get`;
+ const options: FetchOptions = {
+ method: 'GET',
+ cache,
+ };
+ const response = await fetch(url, options);
+ expect(response).toBeDefined();
+ }, testTimeout);
+
+ test('should throw an error if cache is not provided', async () => {
+ const url = `${testUrl}/get`;
+ const options: FetchOptions = {
+ method: 'GET',
+ cache: undefined as unknown as Cacheable, // Force error
+ };
+ await expect(fetch(url, options)).rejects.toThrow('Fetch options must include a cache instance or options.');
+ }, testTimeout);
+});
diff --git a/packages/net/test/index.test.ts b/packages/net/test/index.test.ts
new file mode 100644
index 00000000..3c301c28
--- /dev/null
+++ b/packages/net/test/index.test.ts
@@ -0,0 +1,75 @@
+
+import process from 'node:process';
+import {describe, test, expect} from 'vitest';
+import {faker} from '@faker-js/faker';
+import {Cacheable} from 'cacheable';
+import {
+ CacheableNet, fetch, Net, type CacheableNetOptions, type FetchOptions,
+} from '../src/index.js';
+
+const testUrl = process.env.TEST_URL ?? 'https://mockhttp.org';
+const testTimeout = 10_000; // 10 seconds
+
+describe('Cacheable Net', () => {
+ test('should create an instance of CacheableNet', () => {
+ const net = new CacheableNet();
+ expect(net).toBeInstanceOf(CacheableNet);
+ });
+
+ test('should create an instance of Net', () => {
+ const net = new Net();
+ expect(net).toBeInstanceOf(CacheableNet);
+ });
+
+ test('should create an instance with cache instance', async () => {
+ const cacheOptions: CacheableNetOptions = {
+ cache: new Cacheable({ttl: '1h'}),
+ };
+ const net = new CacheableNet(cacheOptions);
+ expect(net.cache).toBeInstanceOf(Cacheable);
+ expect(net.cache.ttl).toBe('1h');
+
+ // Do a quick test to ensure the cache is working
+ const data = {key: faker.string.uuid(), value: faker.string.alpha(10)};
+ await net.cache.set(data.key, data.value);
+ const cachedValue = await net.cache.get(data.key);
+ expect(cachedValue).toBe(data.value);
+ });
+
+ test('should create an instance with custom cache options', () => {
+ const cacheOptions: CacheableNetOptions = {
+ cache: {
+ ttl: '2h',
+ },
+ };
+ const net = new Net(cacheOptions);
+ expect(net.cache).toBeInstanceOf(Cacheable);
+ expect(net.cache.ttl).toBe('2h');
+
+ // Set a new cache instance
+ const newCache = new Cacheable({ttl: '3h'});
+ net.cache = newCache;
+ expect(net.cache).toBe(newCache);
+ });
+
+ test('should fetch data using fetch method', async () => {
+ const cache = new Cacheable();
+ const url = `${testUrl}/get`;
+ const options: FetchOptions = {
+ method: 'GET',
+ cache,
+ };
+ const response = await fetch(url, options);
+ expect(response).toBeDefined();
+ });
+
+ test('should fetch data using CacheableNet fetch method', async () => {
+ const net = new Net();
+ const url = `${testUrl}/get`;
+ const options = {
+ method: 'GET',
+ };
+ const response = await net.fetch(url, options);
+ expect(response).toBeDefined();
+ }, testTimeout);
+});
diff --git a/packages/net/tsconfig.json b/packages/net/tsconfig.json
new file mode 100644
index 00000000..d6359664
--- /dev/null
+++ b/packages/net/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
+ "baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */
+
+ /* Emit */
+ "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ "outDir": "./dist", /* Specify an output folder for all emitted files. */
+
+ /* Interop Constraints */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+
+ /* Completeness */
+ "skipLibCheck": true, /* Skip type checking all .d.ts files. */
+ "lib": [
+ "ESNext", "DOM"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/net/vitest.config.ts b/packages/net/vitest.config.ts
new file mode 100644
index 00000000..155743cb
--- /dev/null
+++ b/packages/net/vitest.config.ts
@@ -0,0 +1,17 @@
+import {defineConfig} from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ slowTestThreshold: 750,
+ coverage: {
+ reporter: ['json', 'text', 'lcov'],
+ exclude: [
+ 'test',
+ 'src/cacheable-item-types.ts',
+ 'vitest.config.ts',
+ 'dist',
+ 'node_modules',
+ ],
+ },
+ },
+});