mikroserve 1.0.0 → 1.1.0

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 CHANGED
@@ -14,10 +14,14 @@
14
14
 
15
15
  - Native Node.js [http](https://nodejs.org/api/http.html)/[https](https://nodejs.org/api/https.html)/[http2](https://nodejs.org/api/http2.html) implementation, meaning maximum performance
16
16
  - [Hono](https://hono.dev)-style API semantics for GET, POST, PATCH, PUT, DELETE operations
17
+ - Binary file upload/download support (images, PDFs, videos, etc.)
18
+ - Multipart form-data parsing for HTML file uploads
17
19
  - Supports being exposed over HTTP, HTTPS, and HTTP2
18
20
  - Supports custom middlewares
19
21
  - Out-of-the-box CORS support
20
22
  - Built-in customizable rate limiter
23
+ - Configurable request timeout protection against slow clients
24
+ - Configurable body size limits
21
25
  - Tiny (~5kb gzipped)
22
26
  - Only a single dependency: [MikroConf](https://github.com/mikaelvesavuori/mikroconf)
23
27
 
@@ -189,6 +193,150 @@ api.get('/error', () => {
189
193
  api.start();
190
194
  ```
191
195
 
196
+ ### Binary File Uploads
197
+
198
+ MikroServe automatically handles binary file uploads by preserving the raw Buffer for binary content types.
199
+
200
+ ```typescript
201
+ import { MikroServe, type Context } from 'mikroserve';
202
+ import { writeFile } from 'node:fs/promises';
203
+
204
+ const api = new MikroServe();
205
+
206
+ // Handle binary file upload (e.g., image, PDF, video)
207
+ api.post('/upload', async (c: Context) => {
208
+ const buffer = c.body as Buffer;
209
+
210
+ // Save to disk
211
+ await writeFile('/uploads/file.bin', buffer);
212
+
213
+ return c.json({
214
+ success: true,
215
+ size: buffer.length,
216
+ message: 'File uploaded successfully'
217
+ });
218
+ });
219
+
220
+ // Handle image upload with binary response
221
+ api.post('/process-image', async (c: Context) => {
222
+ const imageBuffer = c.body as Buffer;
223
+
224
+ // Process the image (resize, compress, etc.)
225
+ const processedImage = await processImage(imageBuffer);
226
+
227
+ // Return binary image
228
+ return c.binary(processedImage, 'image/jpeg');
229
+ });
230
+
231
+ api.start();
232
+ ```
233
+
234
+ **Supported binary content types:**
235
+
236
+ - Images: `image/*` (PNG, JPEG, GIF, WebP, etc.)
237
+ - Videos: `video/*` (MP4, WebM, etc.)
238
+ - Audio: `audio/*` (MP3, WAV, etc.)
239
+ - Documents: `application/pdf`, Office documents, etc.
240
+ - Archives: `application/zip`, `application/gzip`, etc.
241
+ - Generic: `application/octet-stream`
242
+
243
+ ### Multipart Form-Data (File Uploads from HTML Forms)
244
+
245
+ Handle file uploads from HTML forms with automatic multipart parsing.
246
+
247
+ ```typescript
248
+ import { MikroServe, type Context, type MultipartFormData } from 'mikroserve';
249
+
250
+ const api = new MikroServe();
251
+
252
+ // Handle HTML form with file upload
253
+ api.post('/submit-form', async (c: Context) => {
254
+ const { fields, files } = c.body as MultipartFormData;
255
+
256
+ // Access form fields
257
+ console.log(fields.username); // "john_doe"
258
+ console.log(fields.email); // "john@example.com"
259
+
260
+ // Access uploaded file
261
+ const uploadedFile = files.avatar;
262
+ console.log(uploadedFile.filename); // "profile.jpg"
263
+ console.log(uploadedFile.contentType); // "image/jpeg"
264
+ console.log(uploadedFile.size); // 45234
265
+
266
+ // uploadedFile.data is a Buffer
267
+ await saveFile(`/uploads/${uploadedFile.filename}`, uploadedFile.data);
268
+
269
+ return c.json({
270
+ success: true,
271
+ user: fields.username,
272
+ file: uploadedFile.filename
273
+ });
274
+ });
275
+
276
+ // Handle multiple file uploads
277
+ api.post('/upload-multiple', async (c: Context) => {
278
+ const { fields, files } = c.body as MultipartFormData;
279
+
280
+ // Multiple files with same field name become an array
281
+ const uploadedFiles = files.documents as Array<MultipartFile>;
282
+
283
+ for (const file of uploadedFiles) {
284
+ await saveFile(`/uploads/${file.filename}`, file.data);
285
+ }
286
+
287
+ return c.json({
288
+ success: true,
289
+ count: uploadedFiles.length
290
+ });
291
+ });
292
+
293
+ api.start();
294
+ ```
295
+
296
+ **Example HTML form:**
297
+
298
+ ```html
299
+ <form action="http://localhost:3000/submit-form" method="POST" enctype="multipart/form-data">
300
+ <input type="text" name="username" value="john_doe">
301
+ <input type="email" name="email" value="john@example.com">
302
+ <input type="file" name="avatar">
303
+ <button type="submit">Upload</button>
304
+ </form>
305
+ ```
306
+
307
+ ### Custom Configuration
308
+
309
+ Configure body size limits, request timeouts, and more.
310
+
311
+ ```typescript
312
+ import { MikroServe } from 'mikroserve';
313
+
314
+ const api = new MikroServe({
315
+ port: 3000,
316
+ host: '0.0.0.0',
317
+
318
+ // Set maximum request body size (default: 1MB)
319
+ maxBodySize: 50 * 1024 * 1024, // 50MB for large file uploads
320
+
321
+ // Set request timeout to prevent slow clients (default: 30 seconds)
322
+ requestTimeout: 60000, // 60 seconds, set to 0 to disable
323
+
324
+ // Rate limiting
325
+ rateLimit: {
326
+ enabled: true,
327
+ requestsPerMinute: 100
328
+ },
329
+
330
+ // CORS configuration
331
+ allowedDomains: ['https://yourdomain.com', 'https://app.yourdomain.com'],
332
+
333
+ // Enable debug logging
334
+ debug: false
335
+ });
336
+
337
+ api.start();
338
+ ```
339
+
192
340
  ## Configuration
193
341
 
194
342
  All of the settings already presented in the above examples can be provided in multiple ways.
@@ -203,6 +351,24 @@ All of the settings already presented in the above examples can be provided in m
203
351
 
204
352
  ### Options
205
353
 
354
+ | Option | Type | Default | Description |
355
+ |------------------------|-----------|--------------|-------------------------------------------------------|
356
+ | `port` | `number` | `3000` | Port to listen on |
357
+ | `host` | `string` | `'0.0.0.0'` | Host address to bind to |
358
+ | `useHttps` | `boolean` | `false` | Enable HTTPS |
359
+ | `useHttp2` | `boolean` | `false` | Enable HTTP/2 |
360
+ | `sslCert` | `string` | `''` | Path to SSL certificate file |
361
+ | `sslKey` | `string` | `''` | Path to SSL key file |
362
+ | `sslCa` | `string` | `''` | Path to SSL CA file |
363
+ | `debug` | `boolean` | `false` | Enable debug logging |
364
+ | `maxBodySize` | `number` | `1048576` | Maximum request body size in bytes (1MB default) |
365
+ | `requestTimeout` | `number` | `30000` | Request timeout in milliseconds (30s default, 0 = disabled) |
366
+ | `rateLimit.enabled` | `boolean` | `true` | Enable rate limiting |
367
+ | `rateLimit.requestsPerMinute` | `number` | `100` | Maximum requests per minute per IP |
368
+ | `allowedDomains` | `string[]`| `['*']` | CORS allowed domains (wildcard `*` allows all) |
369
+
370
+ ### CLI Arguments
371
+
206
372
  | CLI argument | CLI value | JSON (config file) value | Environment variable |
207
373
  |--------------|-----------------------------|-----------------------------|----------------------|
208
374
  | --port | `<number>` | port | PORT |
@@ -217,7 +383,7 @@ All of the settings already presented in the above examples can be provided in m
217
383
  | --allowed | `<comma-separated strings>` | allowedDomains | |
218
384
  | --debug | none (is flag) | debug | DEBUG |
219
385
 
220
- ### Order of application
386
+ ### Order of Application
221
387
 
222
388
  As per [MikroConf](https://github.com/mikaelvesavuori/mikroconf) behavior, the configuration sources are applied in this order:
223
389
 
@@ -236,6 +402,73 @@ openssl req -x509 -newkey rsa:2048 -keyout local-key.pem -out local-cert.pem -da
236
402
 
237
403
  Feel free to change the key and cert names as you wish.
238
404
 
405
+ ## Response Helpers
406
+
407
+ MikroServe provides convenient response helpers on the context object:
408
+
409
+ ```typescript
410
+ // Text response
411
+ c.text('Plain text content', 200);
412
+
413
+ // JSON response
414
+ c.json({ message: 'Success' }, 200);
415
+
416
+ // HTML response
417
+ c.html('<h1>Hello World</h1>', 200);
418
+
419
+ // Binary response (for files, images, etc.)
420
+ c.binary(buffer, 'image/png', 200);
421
+
422
+ // Form-urlencoded response
423
+ c.form({ key: 'value' }, 200);
424
+
425
+ // Redirect
426
+ c.redirect('/new-location', 302);
427
+
428
+ // Custom status code
429
+ c.status(404).json({ error: 'Not Found' });
430
+
431
+ // Access raw response object
432
+ const res = c.raw();
433
+ ```
434
+
435
+ ## TypeScript Support
436
+
437
+ MikroServe is written in TypeScript and exports all necessary types:
438
+
439
+ ```typescript
440
+ import {
441
+ MikroServe,
442
+ type Context,
443
+ type MultipartFile,
444
+ type MultipartFormData
445
+ } from 'mikroserve';
446
+
447
+ // Context provides full type information for request/response
448
+ api.post('/upload', async (c: Context) => {
449
+ // c.body, c.params, c.query, c.headers are all typed
450
+ const data = c.body as MultipartFormData;
451
+ // ...
452
+ });
453
+ ```
454
+
455
+ ## Performance Tips
456
+
457
+ 1. **Adjust body size limits** based on your use case:
458
+ - For JSON APIs: Keep default 1MB or lower
459
+ - For file uploads: Increase to 50-100MB or as needed
460
+
461
+ 2. **Enable request timeouts** to prevent resource exhaustion:
462
+ - Default 30 seconds is good for most APIs
463
+ - Increase for large file uploads
464
+ - Set to 0 only if you have other timeout mechanisms
465
+
466
+ 3. **Use rate limiting** to protect against abuse:
467
+ - Default 100 requests/minute per IP
468
+ - Adjust based on your traffic patterns
469
+
470
+ 4. **Binary responses**: Use `c.binary()` for files to avoid JSON serialization overhead
471
+
239
472
  ## License
240
473
 
241
474
  MIT. See the `LICENSE` file.
@@ -10,6 +10,7 @@ declare class MikroServe {
10
10
  private config;
11
11
  private rateLimiter;
12
12
  private router;
13
+ private shutdownHandlers;
13
14
  /**
14
15
  * @description Creates a new MikroServe instance.
15
16
  */
@@ -38,6 +39,10 @@ declare class MikroServe {
38
39
  * @description Register a PATCH route.
39
40
  */
40
41
  patch(path: string, ...handlers: (RouteHandler | Middleware)[]): this;
42
+ /**
43
+ * @description Register a route that responds to any HTTP method.
44
+ */
45
+ any(path: string, ...handlers: (RouteHandler | Middleware)[]): this;
41
46
  /**
42
47
  * @description Register an OPTIONS route.
43
48
  */
@@ -66,6 +71,10 @@ declare class MikroServe {
66
71
  * @description Parses the request body based on content type.
67
72
  */
68
73
  private parseBody;
74
+ /**
75
+ * @description Checks if a content type is binary.
76
+ */
77
+ private isBinaryContentType;
69
78
  /**
70
79
  * @description CORS middleware.
71
80
  */
@@ -82,6 +91,10 @@ declare class MikroServe {
82
91
  * @description Sets up graceful shutdown handlers for a server.
83
92
  */
84
93
  setupGracefulShutdown(server: ServerType): void;
94
+ /**
95
+ * @description Cleans up shutdown event listeners to prevent memory leaks.
96
+ */
97
+ private cleanupShutdownHandlers;
85
98
  }
86
99
 
87
100
  export { MikroServe };
@@ -10,6 +10,7 @@ declare class MikroServe {
10
10
  private config;
11
11
  private rateLimiter;
12
12
  private router;
13
+ private shutdownHandlers;
13
14
  /**
14
15
  * @description Creates a new MikroServe instance.
15
16
  */
@@ -38,6 +39,10 @@ declare class MikroServe {
38
39
  * @description Register a PATCH route.
39
40
  */
40
41
  patch(path: string, ...handlers: (RouteHandler | Middleware)[]): this;
42
+ /**
43
+ * @description Register a route that responds to any HTTP method.
44
+ */
45
+ any(path: string, ...handlers: (RouteHandler | Middleware)[]): this;
41
46
  /**
42
47
  * @description Register an OPTIONS route.
43
48
  */
@@ -66,6 +71,10 @@ declare class MikroServe {
66
71
  * @description Parses the request body based on content type.
67
72
  */
68
73
  private parseBody;
74
+ /**
75
+ * @description Checks if a content type is binary.
76
+ */
77
+ private isBinaryContentType;
69
78
  /**
70
79
  * @description CORS middleware.
71
80
  */
@@ -82,6 +91,10 @@ declare class MikroServe {
82
91
  * @description Sets up graceful shutdown handlers for a server.
83
92
  */
84
93
  setupGracefulShutdown(server: ServerType): void;
94
+ /**
95
+ * @description Cleans up shutdown event listeners to prevent memory leaks.
96
+ */
97
+ private cleanupShutdownHandlers;
85
98
  }
86
99
 
87
100
  export { MikroServe };