@mkgabri18/mini-serve 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -69
- package/lib/middlewareRunner.js +4 -4
- package/lib/middlewares/bodyParser.js +2 -2
- package/lib/middlewares/enhancers.js +5 -5
- package/lib/middlewares/errorHandlers.js +1 -1
- package/lib/router.js +4 -4
- package/lib/server.js +14 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
1
|
# mini-serve
|
|
2
2
|
|
|
3
|
-
`mini-serve`
|
|
3
|
+
`mini-serve` is an ultra-lightweight, **zero-dependency** HTTP framework for Node.js. It is designed as a minimal, native alternative to Express for building REST APIs, CRUD services, and web servers while maintaining full control over performance and middleware configuration.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
8
|
-
1. [
|
|
9
|
-
2. [
|
|
10
|
-
3. [
|
|
11
|
-
4. [
|
|
12
|
-
5. [
|
|
7
|
+
## Table of Contents
|
|
8
|
+
1. [Key Features](#key-features)
|
|
9
|
+
2. [Guide: How to Use It in a New Project from Scratch](#guide-how-to-use-it-in-a-new-project-from-scratch)
|
|
10
|
+
3. [API Guide](#api-guide)
|
|
11
|
+
4. [Middleware Management](#middleware-management)
|
|
12
|
+
5. [License](#license)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Key Features
|
|
17
17
|
|
|
18
|
-
* 🚀 **Zero
|
|
19
|
-
* 🛣️ **
|
|
20
|
-
* ⚙️ **
|
|
21
|
-
* 🛡️ **
|
|
18
|
+
* 🚀 **Zero external dependencies**: Built entirely on top of Node.js native modules (e.g. `node:http`, `node:fs`).
|
|
19
|
+
* 🛣️ **Express-like Router**: Native support for HTTP methods (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`) and dynamic path parameters (e.g. `/api/users/:id`).
|
|
20
|
+
* ⚙️ **Opt-In Modularity**: Built-in middlewares (body parser, request/response enhancers, logger) are configurable and can be enabled or disabled at will.
|
|
21
|
+
* 🛡️ **Lightweight & Secure**: Includes a built-in JSON body parser with a default 1MB size limit to prevent potential DOS attacks.
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Guide: How to Use It in a New Project from Scratch
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Follow these steps to create a new project and use `mini-serve`.
|
|
28
28
|
|
|
29
|
-
### Step 1:
|
|
30
|
-
|
|
29
|
+
### Step 1: Initialize the Node.js project
|
|
30
|
+
Create a new directory for your project and initialize it via terminal:
|
|
31
31
|
```bash
|
|
32
|
-
mkdir
|
|
33
|
-
cd
|
|
32
|
+
mkdir my-new-project
|
|
33
|
+
cd my-new-project
|
|
34
34
|
npm init -y
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
### Step 2:
|
|
38
|
-
|
|
37
|
+
### Step 2: Enable ES Modules (ESM)
|
|
38
|
+
Open the newly generated `package.json` file and add the `"type": "module"` property. This step is critical since `mini-serve` uses modern ES6 import syntax:
|
|
39
39
|
```json
|
|
40
40
|
{
|
|
41
|
-
"name": "
|
|
41
|
+
"name": "my-new-project",
|
|
42
42
|
"version": "1.0.0",
|
|
43
43
|
"type": "module",
|
|
44
44
|
"scripts": {
|
|
@@ -47,118 +47,118 @@ Apri il file `package.json` appena generato e aggiungi la riga `"type": "module"
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
### Step 3:
|
|
51
|
-
|
|
50
|
+
### Step 3: Install `@mkgabri18/mini-serve`
|
|
51
|
+
Install the package using npm:
|
|
52
52
|
```bash
|
|
53
|
-
npm install mini-serve
|
|
53
|
+
npm install @mkgabri18/mini-serve
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
### Step 4:
|
|
57
|
-
|
|
56
|
+
### Step 4: Create the Server File (`index.js`)
|
|
57
|
+
Create a file named `index.js` in the root of your project and add the following sample code:
|
|
58
58
|
|
|
59
59
|
```javascript
|
|
60
60
|
import http from 'node:http';
|
|
61
|
-
import { createServer } from 'mini-serve';
|
|
62
|
-
import { notFoundHandler, globalErrorHandler } from 'mini-serve/middlewares';
|
|
61
|
+
import { createServer } from '@mkgabri18/mini-serve';
|
|
62
|
+
import { notFoundHandler, globalErrorHandler } from '@mkgabri18/mini-serve/middlewares';
|
|
63
63
|
|
|
64
|
-
// 1.
|
|
64
|
+
// 1. Initialize the server with the desired options
|
|
65
65
|
const app = createServer({
|
|
66
|
-
useEnhancers: true, //
|
|
67
|
-
useBodyParser: true, //
|
|
68
|
-
useLogger: true //
|
|
66
|
+
useEnhancers: true, // Enables res.json(), res.status(), and req.query
|
|
67
|
+
useBodyParser: true, // Automatically parses JSON body for POST/PUT/PATCH
|
|
68
|
+
useLogger: true // Logs requests to the console
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
// 2.
|
|
71
|
+
// 2. Define your routes
|
|
72
72
|
app.get('/', (req, res) => {
|
|
73
|
-
res.status(200).json({ message: '
|
|
73
|
+
res.status(200).json({ message: 'Welcome to my new agnostic server!' });
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
//
|
|
76
|
+
// Route with dynamic path parameters
|
|
77
77
|
app.get('/items/:id', (req, res) => {
|
|
78
78
|
const { id } = req.params;
|
|
79
79
|
res.status(200).json({ itemId: id, queryString: req.query });
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
//
|
|
82
|
+
// POST route with body parser support
|
|
83
83
|
app.post('/items', (req, res) => {
|
|
84
84
|
const payload = req.body;
|
|
85
85
|
res.status(201).json({ created: payload });
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
// 3.
|
|
88
|
+
// 3. Register fallback and error handling middlewares
|
|
89
89
|
app.use(notFoundHandler);
|
|
90
90
|
app.use(globalErrorHandler);
|
|
91
91
|
|
|
92
|
-
// 4.
|
|
92
|
+
// 4. Start the server using Node.js native http module
|
|
93
93
|
const server = http.createServer(app.handler);
|
|
94
94
|
|
|
95
95
|
server.listen(3000, () => {
|
|
96
|
-
console.log('Server
|
|
96
|
+
console.log('Server successfully started at http://localhost:3000');
|
|
97
97
|
});
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
### Step 5:
|
|
101
|
-
|
|
100
|
+
### Step 5: Start the server
|
|
101
|
+
Run the server using your terminal:
|
|
102
102
|
```bash
|
|
103
103
|
npm start
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
---
|
|
107
107
|
|
|
108
|
-
##
|
|
108
|
+
## API Guide
|
|
109
109
|
|
|
110
110
|
### `createServer(options)`
|
|
111
|
-
|
|
111
|
+
Creates an application instance. Accepts a configuration object for built-in middlewares:
|
|
112
112
|
|
|
113
|
-
|
|
|
113
|
+
| Option | Type | Default | Description |
|
|
114
114
|
|---|---|---|---|
|
|
115
|
-
| `useEnhancers` | `boolean` | `true` |
|
|
116
|
-
| `useBodyParser` | `boolean` | `true` |
|
|
117
|
-
| `useLogger` | `boolean` | `false` |
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
- `app.use(middleware)`:
|
|
121
|
-
- `app.get(path, ...handlers)`:
|
|
122
|
-
- `app.post(path, ...handlers)`:
|
|
123
|
-
- `app.put(path, ...handlers)`:
|
|
124
|
-
- `app.delete(path, ...handlers)`:
|
|
125
|
-
- `app.patch(path, ...handlers)`:
|
|
126
|
-
- `app.handler`:
|
|
115
|
+
| `useEnhancers` | `boolean` | `true` | Injects `req.query`, `req.path`, `res.status(code)`, and `res.json(data)`. |
|
|
116
|
+
| `useBodyParser` | `boolean` | `true` | Automatically parses JSON payloads into `req.body` (max limit: 1MB). Returns `413 Payload Too Large` if exceeded, or `400 Bad Request` if JSON is malformed. |
|
|
117
|
+
| `useLogger` | `boolean` | `false` | Logs incoming requests, status code, and execution time in ms (e.g.: `[GET] /api/users - 200 (12ms)`). |
|
|
118
|
+
|
|
119
|
+
The returned `app` object exposes the following methods:
|
|
120
|
+
- `app.use(middleware)`: Registers a global middleware or an error-handling middleware.
|
|
121
|
+
- `app.get(path, ...handlers)`: Registers a GET route.
|
|
122
|
+
- `app.post(path, ...handlers)`: Registers a POST route.
|
|
123
|
+
- `app.put(path, ...handlers)`: Registers a PUT route.
|
|
124
|
+
- `app.delete(path, ...handlers)`: Registers a DELETE route.
|
|
125
|
+
- `app.patch(path, ...handlers)`: Registers a PATCH route.
|
|
126
|
+
- `app.handler`: The native callback `(req, res)` to pass to `http.createServer()`.
|
|
127
127
|
|
|
128
128
|
---
|
|
129
129
|
|
|
130
|
-
##
|
|
130
|
+
## Middleware Management
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
Middlewares follow the standard `(req, res, next)` pattern:
|
|
133
133
|
|
|
134
134
|
```javascript
|
|
135
135
|
app.use((req, res, next) => {
|
|
136
|
-
console.log('
|
|
137
|
-
next(); //
|
|
136
|
+
console.log('Request received...');
|
|
137
|
+
next(); // Pass control to the next middleware
|
|
138
138
|
});
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
###
|
|
142
|
-
|
|
141
|
+
### Global Error Handling
|
|
142
|
+
If you pass an error to `next(err)`, the middleware runner skips all standard middlewares to execute error-handling middlewares, which are identified by having 4 arguments `(err, req, res, next)`:
|
|
143
143
|
|
|
144
144
|
```javascript
|
|
145
145
|
app.use((err, req, res, next) => {
|
|
146
146
|
console.error(err.stack);
|
|
147
147
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
148
|
-
res.end(JSON.stringify({ error: '
|
|
148
|
+
res.end(JSON.stringify({ error: 'Internal Server Error' }));
|
|
149
149
|
});
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
You can also import and use the pre-built error handlers from their submodule:
|
|
153
153
|
```javascript
|
|
154
|
-
import { notFoundHandler, globalErrorHandler } from 'mini-serve/middlewares';
|
|
154
|
+
import { notFoundHandler, globalErrorHandler } from '@mkgabri18/mini-serve/middlewares';
|
|
155
155
|
|
|
156
|
-
app.use(notFoundHandler); //
|
|
157
|
-
app.use(globalErrorHandler); //
|
|
156
|
+
app.use(notFoundHandler); // Handles 404 for unregistered routes
|
|
157
|
+
app.use(globalErrorHandler); // Safely catches and processes uncaught errors
|
|
158
158
|
```
|
|
159
159
|
|
|
160
160
|
---
|
|
161
161
|
|
|
162
|
-
##
|
|
162
|
+
## License
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
This project is licensed under the [MIT](LICENSE) License.
|
package/lib/middlewareRunner.js
CHANGED
|
@@ -16,19 +16,19 @@ export function runMiddlewares(middlewares, req, res) {
|
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
18
|
if (err) {
|
|
19
|
-
//
|
|
19
|
+
// We are handling an error: invoke error-handling middlewares only (4 arguments)
|
|
20
20
|
if (middleware.length === 4) {
|
|
21
21
|
Promise.resolve(middleware(err, req, res, next)).catch(next);
|
|
22
22
|
} else {
|
|
23
|
-
//
|
|
23
|
+
// Skip normal middleware and continue passing down the error
|
|
24
24
|
dispatch(i + 1, err);
|
|
25
25
|
}
|
|
26
26
|
} else {
|
|
27
|
-
//
|
|
27
|
+
// Normal flow: invoke regular middlewares only (< 4 arguments)
|
|
28
28
|
if (middleware.length < 4) {
|
|
29
29
|
Promise.resolve(middleware(req, res, next)).catch(next);
|
|
30
30
|
} else {
|
|
31
|
-
//
|
|
31
|
+
// Skip error-handling middleware
|
|
32
32
|
dispatch(i + 1, err);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function jsonBodyParser(req, res, next) {
|
|
2
|
-
//
|
|
2
|
+
// Parse the body only for requests that are expected to contain it
|
|
3
3
|
if (["POST", "PUT", "PATCH"].includes(req.method)) {
|
|
4
4
|
let body = "";
|
|
5
5
|
const MAX_SIZE = 1 * 1024 * 1024; // 1 Megabyte
|
|
@@ -46,7 +46,7 @@ export function jsonBodyParser(req, res, next) {
|
|
|
46
46
|
next(err);
|
|
47
47
|
});
|
|
48
48
|
} else {
|
|
49
|
-
//
|
|
49
|
+
// For requests like GET or DELETE, proceed ignoring the body
|
|
50
50
|
next();
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
export function requestResponseEnhancer(req, res, next) {
|
|
2
2
|
// --- ENHANCE REQUEST ---
|
|
3
|
-
//
|
|
3
|
+
// Parse URL including query parameters (e.g. ?sort=asc)
|
|
4
4
|
const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// Save pathname stripped of query strings (e.g. /notes instead of /notes?sort=asc)
|
|
7
7
|
req.path = parsedUrl.pathname;
|
|
8
8
|
|
|
9
|
-
//
|
|
9
|
+
// Save query parameters as an object (e.g. { sort: 'asc' })
|
|
10
10
|
req.query = Object.fromEntries(parsedUrl.searchParams);
|
|
11
11
|
|
|
12
12
|
// --- ENHANCE RESPONSE ---
|
|
13
|
-
//
|
|
13
|
+
// Adds chainable status code shortcut
|
|
14
14
|
res.status = function(code) {
|
|
15
15
|
res.statusCode = code;
|
|
16
16
|
return res;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
//
|
|
19
|
+
// Adds json helper method to send responses with appropriate Headers
|
|
20
20
|
res.json = function(data) {
|
|
21
21
|
if (!res.hasHeader("Content-Type")) {
|
|
22
22
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -7,7 +7,7 @@ export function globalErrorHandler(err, req, res, next) {
|
|
|
7
7
|
console.error("Global Error Caught:", err.message || err);
|
|
8
8
|
|
|
9
9
|
if (res.headersSent) {
|
|
10
|
-
return next(err); //
|
|
10
|
+
return next(err); // Let Node handle the connection termination
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const statusCode = err.status || 500;
|
package/lib/router.js
CHANGED
|
@@ -16,12 +16,12 @@ export function route(method, pathDefinition, ...handlers) {
|
|
|
16
16
|
|
|
17
17
|
return (req, res, next) => {
|
|
18
18
|
if (req.method === method) {
|
|
19
|
-
//
|
|
19
|
+
// Use req.path (cleaned of query params) if available, otherwise sanitized req.url
|
|
20
20
|
const urlToMatch = req.path || req.url.split('?')[0];
|
|
21
21
|
const match = urlToMatch.match(pathRegex);
|
|
22
22
|
|
|
23
23
|
if (match) {
|
|
24
|
-
//
|
|
24
|
+
// Extract dynamic parameters (e.g., /notes/15 -> req.params.id = "15")
|
|
25
25
|
req.params = {};
|
|
26
26
|
paramNames.forEach((name, index) => {
|
|
27
27
|
req.params[name] = match[index + 1];
|
|
@@ -46,11 +46,11 @@ export function route(method, pathDefinition, ...handlers) {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
runNext();
|
|
49
|
-
return; //
|
|
49
|
+
return; // Stop here, route has been matched and handled
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
//
|
|
53
|
+
// If no match is found, proceed to the next middleware in the chain
|
|
54
54
|
next();
|
|
55
55
|
};
|
|
56
56
|
}
|
package/lib/server.js
CHANGED
|
@@ -5,13 +5,13 @@ import { jsonBodyParser } from "./middlewares/bodyParser.js";
|
|
|
5
5
|
import { logger } from "./middlewares/logger.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Factory
|
|
8
|
+
* Factory that creates the mini-serve server instance.
|
|
9
9
|
*
|
|
10
|
-
* @param {Object} [options] -
|
|
11
|
-
* @param {boolean} [options.useEnhancers=true] -
|
|
12
|
-
* @param {boolean} [options.useBodyParser=true] -
|
|
13
|
-
* @param {boolean} [options.useLogger=false] -
|
|
14
|
-
* @returns {Object}
|
|
10
|
+
* @param {Object} [options] - Configuration options for built-in middlewares.
|
|
11
|
+
* @param {boolean} [options.useEnhancers=true] - Whether to register the middleware that enhances req and res.
|
|
12
|
+
* @param {boolean} [options.useBodyParser=true] - Whether to register the JSON body parser for POST/PUT/PATCH.
|
|
13
|
+
* @param {boolean} [options.useLogger=false] - Whether to enable the request logger in console.
|
|
14
|
+
* @returns {Object} Application instance with methods to define routes and register middlewares.
|
|
15
15
|
*/
|
|
16
16
|
export function createServer(options = {}) {
|
|
17
17
|
const {
|
|
@@ -22,7 +22,7 @@ export function createServer(options = {}) {
|
|
|
22
22
|
|
|
23
23
|
const middlewares = [];
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// Register built-in middlewares based on options
|
|
26
26
|
if (useEnhancers) {
|
|
27
27
|
middlewares.push(requestResponseEnhancer);
|
|
28
28
|
}
|
|
@@ -35,49 +35,49 @@ export function createServer(options = {}) {
|
|
|
35
35
|
|
|
36
36
|
const app = {
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Registers a generic middleware in the stack.
|
|
39
39
|
*/
|
|
40
40
|
use(fn) {
|
|
41
41
|
middlewares.push(fn);
|
|
42
42
|
},
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* Registers a GET route.
|
|
46
46
|
*/
|
|
47
47
|
get(path, ...handler) {
|
|
48
48
|
middlewares.push(route("GET", path, ...handler));
|
|
49
49
|
},
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* Registers a POST route.
|
|
53
53
|
*/
|
|
54
54
|
post(path, ...handler) {
|
|
55
55
|
middlewares.push(route("POST", path, ...handler));
|
|
56
56
|
},
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
59
|
+
* Registers a PUT route.
|
|
60
60
|
*/
|
|
61
61
|
put(path, ...handler) {
|
|
62
62
|
middlewares.push(route("PUT", path, ...handler));
|
|
63
63
|
},
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Registers a DELETE route.
|
|
67
67
|
*/
|
|
68
68
|
delete(path, ...handler) {
|
|
69
69
|
middlewares.push(route("DELETE", path, ...handler));
|
|
70
70
|
},
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* Registers a PATCH route.
|
|
74
74
|
*/
|
|
75
75
|
patch(path, ...handler) {
|
|
76
76
|
middlewares.push(route("PATCH", path, ...handler));
|
|
77
77
|
},
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
80
|
+
* The delegate (req, res) to pass to http.createServer().
|
|
81
81
|
*/
|
|
82
82
|
handler(req, res) {
|
|
83
83
|
runMiddlewares(middlewares, req, res);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mkgabri18/mini-serve",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A tiny, zero-dependency HTTP framework for Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -36,4 +36,4 @@
|
|
|
36
36
|
],
|
|
37
37
|
"author": "",
|
|
38
38
|
"license": "MIT"
|
|
39
|
-
}
|
|
39
|
+
}
|