rkk-next 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
@@ -1,218 +1,465 @@
1
- # ๐Ÿš€ rkk-next
1
+ <div align="center">
2
2
 
3
- > **SEO, Performance & Routing SDK for Next.js**
3
+ <img src="./assets/logo.svg" alt="rkk-next logo" width="120" height="120" />
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/rkk-next.svg)](https://www.npmjs.com/package/rkk-next)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
7
- [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
5
+ # rkk-next
8
6
 
9
- rkk-next is an opinionated Next.js SDK that helps you build **SEO-optimized**, **blazing fast**, and **scalable** applications with better routing, caching, and performance defaults.
7
+ **Production-ready SEO, Performance & Routing SDK for Next.js**
10
8
 
11
- โœจ **Built for Next.js Pages Router & App Router**
12
- ๐ŸŽฏ **Ideal for:** Startups, Landing Pages, Web3 Dashboards, SaaS, Hackathons
9
+ [![npm version](https://img.shields.io/npm/v/rkk-next.svg?style=flat-square)](https://www.npmjs.com/package/rkk-next)
10
+ [![npm downloads](https://img.shields.io/npm/dm/rkk-next.svg?style=flat-square)](https://www.npmjs.com/package/rkk-next)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](./LICENSE)
12
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-3178c6.svg?style=flat-square&logo=typescript)](https://www.typescriptlang.org/)
13
13
 
14
- ## โœจ Features
14
+ **Enterprise-grade toolkit for building SEO-optimized, lightning-fast Next.js applications**
15
15
 
16
- ### ๐Ÿ” SEO Optimization
17
- - โœ… Centralized meta management (OpenGraph, Twitter Cards)
18
- - โœ… JSON-LD structured data (Schema.org)
19
- - โœ… Canonical URL handling
20
- - โœ… SEO-safe defaults & best practices
16
+ [Get Started](#-quick-start) ยท [Documentation](./docs/DOCS.md) ยท [Examples](./examples/) ยท [Report Bug](https://github.com/ROHIT8759/rkk-next/issues)
21
17
 
22
- ### โšก Routing Optimization
23
- - โœ… Intelligent route prefetching (hover-based)
24
- - โœ… Network-aware prefetching
25
- - โœ… Route change observer with performance metrics
26
- - โœ… Analytics-ready routing events
18
+ </div>
27
19
 
28
- ### ๐Ÿš€ Performance Boost
29
- - โœ… Lazy loading for heavy components
30
- - โœ… Optimized image wrapper (SEO + performance)
31
- - โœ… Cache & CDN header presets
32
- - โœ… Edge-friendly caching strategies
33
- - โœ… Security headers included
20
+ ---
34
21
 
35
- ### ๐Ÿ“Š Analytics
36
- - โœ… Web Vitals tracking (LCP, FID, CLS, etc.)
37
- - โœ… Route navigation analytics
38
- - โœ… Performance monitoring
22
+ ## ๐ŸŽฏ Why rkk-next?
39
23
 
40
- ๐Ÿ“ฆ Installation
41
- npm install rkk-next
24
+ Building performant, SEO-optimized Next.js applications requires juggling multiple concerns: meta tags, structured data, route prefetching, image optimization, and caching strategies. **rkk-next** provides production-tested solutions out of the box.
25
+
26
+ **Perfect for:**
27
+
28
+ - ๐Ÿš€ Startups needing rapid development
29
+ - ๐Ÿ’ผ Enterprise applications requiring SEO excellence
30
+ - ๐ŸŽจ Marketing websites and landing pages
31
+ - ๐ŸŒ Web3 dashboards and SaaS platforms
32
+ - โšก Performance-critical applications
33
+
34
+ ## โœจ Key Features
35
+
36
+ <table>
37
+ <tr>
38
+ <td width="50%">
39
+
40
+ ### ๐Ÿ” **SEO Excellence**
41
+
42
+ - Comprehensive meta tag management
43
+ - OpenGraph & Twitter Cards
44
+ - JSON-LD structured data (Schema.org)
45
+ - Automatic canonical URLs
46
+ - Server-side rendering optimized
47
+
48
+ </td>
49
+ <td width="50%">
50
+
51
+ ### โšก **Performance First**
52
+
53
+ - Intelligent route prefetching
54
+ - Network-aware optimizations
55
+ - Lazy loading for heavy components
56
+ - CDN & edge caching strategies
57
+ - Built-in security headers
58
+
59
+ </td>
60
+ </tr>
61
+ <tr>
62
+ <td width="50%">
63
+
64
+ ### ๐Ÿ“Š **Analytics Ready**
65
+
66
+ - Core Web Vitals tracking
67
+ - Route navigation metrics
68
+ - Performance monitoring
69
+ - Custom event tracking
70
+ - Production-ready insights
71
+
72
+ </td>
73
+ <td width="50%">
74
+
75
+ ### โšก **Backend Utilities**
76
+
77
+ - Express-like middleware
78
+ - API route optimization
79
+ - Rate limiting & CORS
80
+ - Response caching
81
+ - Request validation
82
+
83
+ </td>
84
+ </tr>
85
+ <tr>
86
+ <td width="50%">
87
+
88
+ ### ๐ŸŽจ **Developer Experience**
89
+
90
+ - Full TypeScript support
91
+ - Zero configuration needed
92
+ - Pages Router & App Router
93
+ - Comprehensive documentation
94
+ - Active maintenance
95
+
96
+ </td>
97
+ </tr>
98
+ </table>
99
+
100
+ ---
101
+
102
+ ## ๐Ÿš€ Quick Start
42
103
 
104
+ ### Create New Project (Recommended)
43
105
 
44
- or
106
+ Get started instantly with our CLI tool:
45
107
 
108
+ ```bash
109
+ npx create-next-rkk@latest my-app
110
+ cd my-app
111
+ npm run dev
112
+ ```
113
+
114
+ ### Add to Existing Project
115
+
116
+ Install into your existing Next.js application:
117
+
118
+ ```bash
119
+ npm install rkk-next
120
+ # or
46
121
  yarn add rkk-next
122
+ # or
123
+ pnpm add rkk-next
124
+ ```
125
+
126
+ ---
47
127
 
48
- ๐Ÿง  Basic Usage
49
- 1๏ธโƒฃ SEO Meta Manager
128
+ ## ๐Ÿ“– Usage Examples
129
+
130
+ ### SEO Meta Management
131
+
132
+ Centralize your SEO configuration with type-safe components:
133
+
134
+ ```tsx
50
135
  import { MetaManager } from "rkk-next";
51
136
 
52
- export default function Home() {
137
+ export default function HomePage() {
53
138
  return (
54
139
  <>
55
140
  <MetaManager
56
141
  title="Home | My App"
57
- description="SEO optimized Next.js app using rkk-next"
58
- keywords="Next.js, SEO, Performance"
59
- image="/og.png"
142
+ description="Production-ready Next.js application with enterprise SEO"
143
+ keywords="Next.js, React, SEO, Performance"
144
+ image="https://myapp.com/og-image.png"
60
145
  siteName="My App"
146
+ twitterHandle="myhandle"
61
147
  />
62
148
 
63
- <h1>Hello World</h1>
149
+ <main>{/* Your content */}</main>
64
150
  </>
65
151
  );
66
152
  }
153
+ ```
67
154
 
68
- 2๏ธโƒฃ JSON-LD (Schema.org)
69
- import { JsonLd } from "rkk-next";
155
+ ### Structured Data (JSON-LD)
70
156
 
71
- <JsonLd
72
- type="WebSite"
73
- data={{
74
- name: "My App",
75
- url: "https://myapp.com",
76
- }}
77
- />
157
+ Improve search engine understanding with structured data:
78
158
 
79
- ๐Ÿ”— Smart Routing
80
- SmartLink (Enhanced next/link)
81
- import { SmartLink } from "rkk-next";
159
+ ```tsx
160
+ import { JsonLd } from "rkk-next";
82
161
 
83
- <SmartLink href="/dashboard">
84
- Go to Dashboard
85
- </SmartLink>
162
+ export default function ArticlePage() {
163
+ return (
164
+ <>
165
+ <JsonLd
166
+ type="Article"
167
+ data={{
168
+ headline: "Advanced Next.js SEO Techniques",
169
+ image: "https://myapp.com/article.jpg",
170
+ datePublished: "2025-12-18T08:00:00.000Z",
171
+ author: {
172
+ "@type": "Person",
173
+ name: "John Doe",
174
+ },
175
+ }}
176
+ />
86
177
 
178
+ <article>{/* Article content */}</article>
179
+ </>
180
+ );
181
+ }
182
+ ```
87
183
 
88
- โœ” Prefetch on hover
89
- โœ” Network-aware
90
- โœ” SEO-safe <a> tag
184
+ ### Smart Routing & Prefetching
91
185
 
92
- Route Observer
93
- import { observeRoutes } from "rkk-next";
186
+ Enhance navigation performance with intelligent prefetching:
94
187
 
95
- observeRoutes((event) => {
96
- console.log(event.url, event.duration);
97
- });
188
+ ```tsx
189
+ import { SmartLink, observeRoutes } from "rkk-next";
190
+ import { useEffect } from "react";
98
191
 
99
- ๐Ÿ–ผ๏ธ Image Optimization
100
- import { OptimizedImage } from "rkk-next";
192
+ export default function Navigation() {
193
+ useEffect(() => {
194
+ // Track route changes for analytics
195
+ const unsubscribe = observeRoutes((event) => {
196
+ analytics.track("page_view", {
197
+ url: event.url,
198
+ duration: event.duration,
199
+ });
200
+ });
101
201
 
102
- <OptimizedImage
103
- src="/hero.png"
104
- alt="Landing page hero image"
105
- width={1200}
106
- height={630}
107
- priority
108
- />
202
+ return unsubscribe;
203
+ }, []);
109
204
 
205
+ return (
206
+ <nav>
207
+ <SmartLink href="/products" prefetchOnHover>
208
+ Products
209
+ </SmartLink>
210
+ </nav>
211
+ );
212
+ }
213
+ ```
110
214
 
111
- โœ” SEO-safe alt enforcement
112
- โœ” Responsive sizes
113
- โœ” LCP optimized
215
+ ### Optimized Images
114
216
 
115
- ๐Ÿ’ค Lazy Loading
116
- import { lazyImport } from "rkk-next";
217
+ Ensure SEO-compliant and performant images:
117
218
 
118
- const Chart = lazyImport(() => import("./Chart"));
219
+ ```tsx
220
+ import { OptimizedImage } from "rkk-next";
119
221
 
120
- export default function Dashboard() {
121
- return <Chart />;
222
+ export default function Hero() {
223
+ return (
224
+ <OptimizedImage
225
+ src="/hero-banner.jpg"
226
+ alt="Professional hero banner showcasing our product"
227
+ width={1920}
228
+ height={1080}
229
+ priority // For above-the-fold images
230
+ quality={85}
231
+ />
232
+ );
122
233
  }
234
+ ```
235
+
236
+ ### Code Splitting & Lazy Loading
123
237
 
124
- ๐Ÿง  Intelligent Prefetching
125
- import { prefetchRoute, isFastConnection } from "rkk-next";
238
+ Reduce initial bundle size with intelligent lazy loading:
126
239
 
127
- prefetchRoute("/dashboard", {
128
- condition: isFastConnection,
240
+ ```tsx
241
+ import { lazyImport, DefaultLoader } from "rkk-next";
242
+
243
+ // Heavy component loaded on-demand
244
+ const AnalyticsDashboard = lazyImport(() => import("./AnalyticsDashboard"), {
245
+ loading: DefaultLoader,
246
+ ssr: false,
247
+ delay: 100,
129
248
  });
130
249
 
131
- ๐Ÿ—„๏ธ Cache Headers
250
+ export default function Dashboard() {
251
+ return (
252
+ <main>
253
+ <h1>Dashboard</h1>
254
+ <AnalyticsDashboard />
255
+ </main>
256
+ );
257
+ }
258
+ ```
259
+
260
+ ### Performance-Optimized Caching
132
261
 
133
- Use directly inside next.config.js:
262
+ Configure production-grade caching in `next.config.js`:
134
263
 
264
+ ```javascript
135
265
  const {
136
266
  LONG_TERM_CACHE,
137
267
  EDGE_CACHE,
138
268
  NO_CACHE,
269
+ SECURITY_HEADERS,
139
270
  applyCache,
140
- } = require("rkk-next");
271
+ } = require("rkk-next/performance/cacheHeaders");
141
272
 
142
273
  module.exports = {
143
274
  async headers() {
144
275
  return [
276
+ // Static assets: aggressive caching
145
277
  applyCache("/_next/static/:path*", LONG_TERM_CACHE),
278
+ applyCache("/images/:path*", LONG_TERM_CACHE),
279
+
280
+ // API routes: edge caching
146
281
  applyCache("/api/public/:path*", EDGE_CACHE),
282
+
283
+ // User-specific pages: no cache
147
284
  applyCache("/dashboard/:path*", NO_CACHE),
285
+
286
+ // Security headers for all routes
287
+ {
288
+ source: "/:path*",
289
+ headers: SECURITY_HEADERS,
290
+ },
148
291
  ];
149
292
  },
150
293
  };
294
+ ```
295
+
296
+ ### Backend API Utilities
297
+
298
+ Build robust Next.js API routes with Express-like middleware:
299
+
300
+ ```typescript
301
+ // pages/api/users/[id].ts
302
+ import { NextApiRequest, NextApiResponse } from "next";
303
+ import {
304
+ composeMiddleware,
305
+ cors,
306
+ rateLimit,
307
+ validateRequest,
308
+ logger,
309
+ errorHandler,
310
+ cacheResponse,
311
+ jsonResponse,
312
+ allowMethods,
313
+ } from "rkk-next";
314
+
315
+ // Compose middleware chain
316
+ const handler = composeMiddleware(
317
+ cors({ origin: "https://yourdomain.com" }),
318
+ rateLimit({ maxRequests: 100, windowMs: 60000 }),
319
+ logger(),
320
+ allowMethods(["GET", "PUT", "DELETE"]),
321
+ cacheResponse({ ttl: 300 }), // Cache for 5 minutes
322
+ validateRequest((req) => {
323
+ if (req.method === "PUT" && !req.body.name) {
324
+ return "Name is required";
325
+ }
326
+ }),
327
+ errorHandler()
328
+ )(async (req: NextApiRequest, res: NextApiResponse) => {
329
+ const { id } = req.query;
330
+
331
+ // Your API logic
332
+ const user = await getUserById(id as string);
333
+
334
+ return jsonResponse(res, {
335
+ success: true,
336
+ data: user,
337
+ });
338
+ });
339
+
340
+ export default handler;
341
+ ```
151
342
 
152
- ## ๐Ÿงฉ Supported Next.js Versions
343
+ **Server-side caching with automatic TTL:**
153
344
 
154
- | Feature | Pages Router | App Router |
155
- |-------------------|--------------|------------|
156
- | MetaManager | โœ… | โœ… (via generateAppMetadata) |
157
- | JsonLd | โœ… | โœ… |
158
- | SmartLink | โœ… | โš ๏ธ (use for internal links only) |
159
- | Routing Observer | โœ… | โš ๏ธ (Pages Router recommended) |
160
- | OptimizedImage | โœ… | โœ… |
161
- | Lazy Loading | โœ… | โœ… |
162
- | Cache Headers | โœ… | โœ… |
163
- | Web Vitals | โœ… | โœ… |
345
+ ```typescript
346
+ import { cache, memoize } from "rkk-next";
164
347
 
165
- **Minimum Requirements:**
166
- - Next.js >= 12.0.0
167
- - React >= 17.0.0
168
- - TypeScript >= 4.5.0 (optional but recommended)
169
- ๐Ÿ› ๏ธ Best Practices
348
+ // Cache expensive operations
349
+ const expensiveQuery = memoize(
350
+ async (userId: string) => {
351
+ return await database.query(/* ... */);
352
+ },
353
+ { ttl: 600 } // 10 minutes
354
+ );
170
355
 
171
- Use MetaManager on every page
356
+ // Manual cache control
357
+ cache.set("user:123", userData, 300);
358
+ const cachedUser = cache.get("user:123");
359
+ ```
172
360
 
173
- Avoid lazy loading LCP elements
361
+ See [Backend Utilities Documentation](docs/BACKEND.md) for complete API reference.
174
362
 
175
- Use SmartLink for internal navigation
363
+ ---
176
364
 
177
- Enable cache headers for static assets
365
+ ## ๐Ÿงฉ Compatibility Matrix
178
366
 
179
- Always provide alt text for images
367
+ | Feature | Pages Router | App Router | Notes |
368
+ | -------------- | :----------: | :--------: | ------------------------------------- |
369
+ | MetaManager | โœ… | โœ… | App Router uses `generateAppMetadata` |
370
+ | JsonLd | โœ… | โœ… | Works with both routers |
371
+ | SmartLink | โœ… | โš ๏ธ | Recommended for Pages Router |
372
+ | RouteObserver | โœ… | โš ๏ธ | Pages Router only |
373
+ | OptimizedImage | โœ… | โœ… | Full support both routers |
374
+ | Lazy Loading | โœ… | โœ… | Dynamic imports supported |
375
+ | Cache Headers | โœ… | โœ… | Universal support |
376
+ | Web Vitals | โœ… | โœ… | Analytics integration |
377
+ | Backend Utils | โœ… | โœ… | API routes middleware |
180
378
 
181
- ## ๐Ÿง‘โ€๐Ÿ’ป Author
379
+ **System Requirements:**
182
380
 
183
- **Rohit Kumar Kundu**
184
- ๐ŸŽ“ B.Tech CSE | Web3 & Next.js Developer
185
- ๐Ÿ”— [GitHub](https://github.com/ROHIT8759) | [LinkedIn](https://linkedin.com/in/rohit-kumar-kundu)
381
+ - Next.js `>= 12.0.0`
382
+ - React `>= 17.0.0`
383
+ - Node.js `>= 16.0.0`
384
+ - TypeScript `>= 4.5.0` (optional but recommended)
186
385
 
187
- ## ๐Ÿ“š Documentation
386
+ ---
387
+
388
+ ## ๐ŸŽ“ Learn More
389
+
390
+ ### ๐Ÿ“š Documentation
188
391
 
189
- - ๐Ÿ“– [Full Documentation](./docs/DOCS.md)
190
- - ๐Ÿš€ [Quick Start Guide](./docs/QUICKSTART.md)
191
- - ๐Ÿ“ [API Reference](./docs/DOCS.md#api-reference)
192
- - ๐Ÿ’ก [Examples](./examples/)
392
+ - [Complete Documentation](./docs/DOCS.md) - Comprehensive API reference
393
+ - [Quick Start Guide](./docs/QUICKSTART.md) - Get running in 5 minutes
394
+ - [Migration Guide](./docs/DOCS.md) - Upgrade from other solutions
395
+ - [Best Practices](./docs/DOCS.md#best-practices) - Production tips
193
396
 
194
397
  ## ๐Ÿค Contributing
195
398
 
196
- We welcome contributions! See [CONTRIBUTING.md](./docs/CONTRIBUTING.md) for guidelines.
399
+ We welcome contributions from the community! Whether it's:
400
+
401
+ - ๐Ÿ› Bug reports and fixes
402
+ - โœจ New features and enhancements
403
+ - ๐Ÿ“– Documentation improvements
404
+ - ๐Ÿ’ก Feature suggestions
405
+
406
+ **Getting Started:**
197
407
 
198
408
  1. Fork the repository
199
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
200
- 3. Commit your changes (`git commit -m 'Add amazing feature'`)
201
- 4. Push to the branch (`git push origin feature/amazing-feature`)
202
- 5. Open a Pull Request
409
+ 2. Create a feature branch: `git checkout -b feature/amazing-feature`
410
+ 3. Make your changes with clear commit messages
411
+ 4. Write or update tests as needed
412
+ 5. Submit a pull request
203
413
 
204
- ## ๐Ÿš€ Next Steps
414
+ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md) for detailed guidelines.
205
415
 
206
- - [ ] Add App Router `generateMetadata` helper
207
- - [ ] Expand Web Vitals analytics integration
208
- - [ ] Create demo app showcase
209
- - [ ] Add Lighthouse CI integration
210
- - [ ] Video tutorials & guides
416
+ ---
211
417
 
212
418
  ## ๐Ÿ“„ License
213
419
 
214
420
  MIT License ยฉ 2025 [Rohit Kumar Kundu](https://github.com/ROHIT8759)
215
421
 
422
+ Free for commercial and personal use. See [LICENSE](./LICENSE) for details.
423
+
424
+ ---
425
+
426
+ ## ๐Ÿ™ Support & Community
427
+
428
+ ### Get Help
429
+
430
+ - ๐Ÿ“– [Documentation](./docs/DOCS.md)
431
+ - ๐Ÿ’ฌ [GitHub Discussions](https://github.com/ROHIT8759/rkk-next/discussions)
432
+ - ๐Ÿ› [Issue Tracker](https://github.com/ROHIT8759/rkk-next/issues)
433
+
434
+ ### Show Your Support
435
+
436
+ If rkk-next helps your project:
437
+
438
+ - โญ Star the repository
439
+ - ๐Ÿฆ Share on social media
440
+ - ๐Ÿ“ Write about your experience
441
+ - ๐Ÿค Contribute back to the project
442
+
443
+ ---
444
+
445
+ ## ๐Ÿง‘โ€๐Ÿ’ป Author
446
+
447
+ **Rohit Kumar Kundu**
448
+ Full-Stack Developer | Next.js & Web3 Specialist
449
+
450
+ ๐Ÿ”— [GitHub](https://github.com/ROHIT8759) ยท [LinkedIn](https://linkedin.com/in/rohit-kumar-kundu) ยท [Portfolio](https://rohitkundu.dev)
451
+
452
+ ---
453
+
454
+ <div align="center">
455
+
456
+ **Built with โค๏ธ for the Next.js community**
457
+
458
+ [Get Started](./docs/QUICKSTART.md) ยท [Documentation](./docs/DOCS.md) ยท [Examples](./examples/) ยท [Changelog](./CHANGELOG.md)
459
+
460
+ </div>
461
+ MIT License ยฉ 2025 [Rohit Kumar Kundu](https://github.com/ROHIT8759)
462
+
216
463
  Free to use, modify, and distribute. See [LICENSE](./LICENSE) for details.
217
464
 
218
465
  ## โญ Support the Project
@@ -245,4 +492,4 @@ If you want, I can now:
245
492
 
246
493
  โœ” Final SDK audit before release
247
494
 
248
- Just tell me ๐Ÿ‘
495
+ Just tell me ๐Ÿ‘
@@ -0,0 +1,49 @@
1
+ import { NextApiRequest, NextApiResponse } from 'next';
2
+ export interface CacheOptions {
3
+ /** Time to live in seconds */
4
+ ttl?: number;
5
+ /** Cache key generator function */
6
+ keyGenerator?: (req: NextApiRequest) => string;
7
+ /** Conditional caching based on request */
8
+ shouldCache?: (req: NextApiRequest) => boolean;
9
+ }
10
+ /**
11
+ * In-memory cache store for API responses
12
+ */
13
+ declare class MemoryCache {
14
+ private cache;
15
+ set<T = any>(key: string, data: T, ttl: number): void;
16
+ get<T = any>(key: string): T | null;
17
+ has(key: string): boolean;
18
+ delete(key: string): void;
19
+ clear(): void;
20
+ size(): number;
21
+ /**
22
+ * Clean up expired entries
23
+ */
24
+ cleanup(): void;
25
+ }
26
+ export declare const cache: MemoryCache;
27
+ /**
28
+ * Cache API response middleware
29
+ */
30
+ export declare function cacheResponse(options?: CacheOptions): (req: NextApiRequest, res: NextApiResponse, next: () => void | Promise<void>) => Promise<void>;
31
+ /**
32
+ * Invalidate cache by pattern or specific key
33
+ */
34
+ export declare function invalidateCache(pattern?: string | RegExp): void;
35
+ /**
36
+ * Get cache statistics
37
+ */
38
+ export declare function getCacheStats(): {
39
+ size: number;
40
+ timestamp: string;
41
+ };
42
+ /**
43
+ * Memoize function results with TTL
44
+ */
45
+ export declare function memoize<T extends (...args: any[]) => any>(fn: T, options?: {
46
+ ttl?: number;
47
+ keyGenerator?: (...args: Parameters<T>) => string;
48
+ }): T;
49
+ export {};
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cache = void 0;
4
+ exports.cacheResponse = cacheResponse;
5
+ exports.invalidateCache = invalidateCache;
6
+ exports.getCacheStats = getCacheStats;
7
+ exports.memoize = memoize;
8
+ /**
9
+ * In-memory cache store for API responses
10
+ */
11
+ class MemoryCache {
12
+ constructor() {
13
+ this.cache = new Map();
14
+ }
15
+ set(key, data, ttl) {
16
+ this.cache.set(key, {
17
+ data,
18
+ timestamp: Date.now(),
19
+ ttl: ttl * 1000, // Convert to milliseconds
20
+ });
21
+ }
22
+ get(key) {
23
+ const entry = this.cache.get(key);
24
+ if (!entry) {
25
+ return null;
26
+ }
27
+ const isExpired = Date.now() - entry.timestamp > entry.ttl;
28
+ if (isExpired) {
29
+ this.cache.delete(key);
30
+ return null;
31
+ }
32
+ return entry.data;
33
+ }
34
+ has(key) {
35
+ return this.get(key) !== null;
36
+ }
37
+ delete(key) {
38
+ this.cache.delete(key);
39
+ }
40
+ clear() {
41
+ this.cache.clear();
42
+ }
43
+ size() {
44
+ return this.cache.size;
45
+ }
46
+ /**
47
+ * Clean up expired entries
48
+ */
49
+ cleanup() {
50
+ const now = Date.now();
51
+ for (const [key, entry] of this.cache.entries()) {
52
+ if (now - entry.timestamp > entry.ttl) {
53
+ this.cache.delete(key);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ exports.cache = new MemoryCache();
59
+ // Periodic cleanup every 5 minutes
60
+ if (typeof setInterval !== 'undefined') {
61
+ setInterval(() => exports.cache.cleanup(), 5 * 60 * 1000);
62
+ }
63
+ /**
64
+ * Default cache key generator
65
+ */
66
+ function defaultKeyGenerator(req) {
67
+ const method = req.method || 'GET';
68
+ const url = req.url || '';
69
+ const query = JSON.stringify(req.query || {});
70
+ return `${method}:${url}:${query}`;
71
+ }
72
+ /**
73
+ * Cache API response middleware
74
+ */
75
+ function cacheResponse(options = {}) {
76
+ const { ttl = 60, // Default 60 seconds
77
+ keyGenerator = defaultKeyGenerator, shouldCache = (req) => req.method === 'GET', } = options;
78
+ return async (req, res, next) => {
79
+ // Skip caching if condition not met
80
+ if (!shouldCache(req)) {
81
+ await next();
82
+ return;
83
+ }
84
+ const cacheKey = keyGenerator(req);
85
+ // Check if cached response exists
86
+ const cachedData = exports.cache.get(cacheKey);
87
+ if (cachedData) {
88
+ res.setHeader('X-Cache', 'HIT');
89
+ res.setHeader('Cache-Control', `public, max-age=${ttl}`);
90
+ res.status(200).json(cachedData);
91
+ return;
92
+ }
93
+ // Intercept response to cache it
94
+ const originalJson = res.json;
95
+ const originalSend = res.send;
96
+ let responseData;
97
+ res.json = function (data) {
98
+ responseData = data;
99
+ return originalJson.call(this, data);
100
+ };
101
+ res.send = function (data) {
102
+ responseData = data;
103
+ return originalSend.call(this, data);
104
+ };
105
+ const originalEnd = res.end;
106
+ res.end = function (...args) {
107
+ // Only cache successful responses
108
+ if (res.statusCode >= 200 && res.statusCode < 300 && responseData) {
109
+ exports.cache.set(cacheKey, responseData, ttl);
110
+ res.setHeader('X-Cache', 'MISS');
111
+ res.setHeader('Cache-Control', `public, max-age=${ttl}`);
112
+ }
113
+ return originalEnd.apply(this, args);
114
+ };
115
+ await next();
116
+ };
117
+ }
118
+ /**
119
+ * Invalidate cache by pattern or specific key
120
+ */
121
+ function invalidateCache(pattern) {
122
+ if (!pattern) {
123
+ exports.cache.clear();
124
+ return;
125
+ }
126
+ const regex = typeof pattern === 'string'
127
+ ? new RegExp(pattern)
128
+ : pattern;
129
+ // This is a simplified implementation
130
+ // In production, you'd want a more efficient pattern matching
131
+ exports.cache.clear(); // For now, clear all
132
+ }
133
+ /**
134
+ * Get cache statistics
135
+ */
136
+ function getCacheStats() {
137
+ return {
138
+ size: exports.cache.size(),
139
+ timestamp: new Date().toISOString(),
140
+ };
141
+ }
142
+ /**
143
+ * Memoize function results with TTL
144
+ */
145
+ function memoize(fn, options = {}) {
146
+ const { ttl = 60, keyGenerator = (...args) => JSON.stringify(args), } = options;
147
+ const memoCache = new Map();
148
+ return ((...args) => {
149
+ const key = keyGenerator(...args);
150
+ const cached = memoCache.get(key);
151
+ if (cached) {
152
+ const isExpired = Date.now() - cached.timestamp > cached.ttl;
153
+ if (!isExpired) {
154
+ return cached.data;
155
+ }
156
+ memoCache.delete(key);
157
+ }
158
+ const result = fn(...args);
159
+ memoCache.set(key, {
160
+ data: result,
161
+ timestamp: Date.now(),
162
+ ttl: ttl * 1000,
163
+ });
164
+ return result;
165
+ });
166
+ }
@@ -0,0 +1,43 @@
1
+ import { NextApiRequest, NextApiResponse } from 'next';
2
+ export type NextApiHandler = (req: NextApiRequest, res: NextApiResponse) => Promise<void> | void;
3
+ export type Middleware = (req: NextApiRequest, res: NextApiResponse, next: () => void | Promise<void>) => void | Promise<void>;
4
+ /**
5
+ * Compose multiple middleware functions into a single handler
6
+ */
7
+ export declare function composeMiddleware(...middlewares: Middleware[]): (handler: NextApiHandler) => NextApiHandler;
8
+ /**
9
+ * CORS middleware for Next.js API routes
10
+ */
11
+ export declare function cors(options?: {
12
+ origin?: string | string[];
13
+ methods?: string[];
14
+ credentials?: boolean;
15
+ allowedHeaders?: string[];
16
+ }): Middleware;
17
+ /**
18
+ * Rate limiting middleware
19
+ */
20
+ export declare function rateLimit(options?: {
21
+ windowMs?: number;
22
+ max?: number;
23
+ message?: string;
24
+ }): Middleware;
25
+ /**
26
+ * Request validation middleware
27
+ */
28
+ export declare function validateRequest(schema: {
29
+ body?: (data: any) => boolean;
30
+ query?: (data: any) => boolean;
31
+ headers?: (data: any) => boolean;
32
+ }): Middleware;
33
+ /**
34
+ * Request logging middleware
35
+ */
36
+ export declare function logger(options?: {
37
+ verbose?: boolean;
38
+ includeBody?: boolean;
39
+ }): Middleware;
40
+ /**
41
+ * Error handling middleware
42
+ */
43
+ export declare function errorHandler(): Middleware;
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.composeMiddleware = composeMiddleware;
4
+ exports.cors = cors;
5
+ exports.rateLimit = rateLimit;
6
+ exports.validateRequest = validateRequest;
7
+ exports.logger = logger;
8
+ exports.errorHandler = errorHandler;
9
+ /**
10
+ * Compose multiple middleware functions into a single handler
11
+ */
12
+ function composeMiddleware(...middlewares) {
13
+ return (handler) => {
14
+ return async (req, res) => {
15
+ let index = 0;
16
+ const next = async () => {
17
+ if (index < middlewares.length) {
18
+ const middleware = middlewares[index++];
19
+ await middleware(req, res, next);
20
+ }
21
+ else {
22
+ await handler(req, res);
23
+ }
24
+ };
25
+ await next();
26
+ };
27
+ };
28
+ }
29
+ /**
30
+ * CORS middleware for Next.js API routes
31
+ */
32
+ function cors(options = {}) {
33
+ const { origin = '*', methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], credentials = true, allowedHeaders = ['Content-Type', 'Authorization'], } = options;
34
+ return (req, res, next) => {
35
+ const requestOrigin = req.headers.origin || '*';
36
+ if (Array.isArray(origin)) {
37
+ if (origin.includes(requestOrigin)) {
38
+ res.setHeader('Access-Control-Allow-Origin', requestOrigin);
39
+ }
40
+ }
41
+ else if (origin === '*' || origin === requestOrigin) {
42
+ res.setHeader('Access-Control-Allow-Origin', origin);
43
+ }
44
+ res.setHeader('Access-Control-Allow-Methods', methods.join(', '));
45
+ res.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));
46
+ if (credentials) {
47
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
48
+ }
49
+ // Handle preflight requests
50
+ if (req.method === 'OPTIONS') {
51
+ res.status(200).end();
52
+ return;
53
+ }
54
+ next();
55
+ };
56
+ }
57
+ /**
58
+ * Rate limiting middleware
59
+ */
60
+ function rateLimit(options = {}) {
61
+ const { windowMs = 60000, // 1 minute
62
+ max = 60, // 60 requests per minute
63
+ message = 'Too many requests, please try again later.', } = options;
64
+ const requests = new Map();
65
+ return (req, res, next) => {
66
+ const identifier = req.headers['x-forwarded-for'] ||
67
+ req.socket.remoteAddress ||
68
+ 'unknown';
69
+ const now = Date.now();
70
+ const windowStart = now - windowMs;
71
+ // Get existing requests for this identifier
72
+ let userRequests = requests.get(identifier) || [];
73
+ // Filter out requests outside the time window
74
+ userRequests = userRequests.filter(time => time > windowStart);
75
+ if (userRequests.length >= max) {
76
+ res.status(429).json({ error: message });
77
+ return;
78
+ }
79
+ // Add current request
80
+ userRequests.push(now);
81
+ requests.set(identifier, userRequests);
82
+ // Cleanup old entries periodically
83
+ if (Math.random() < 0.01) {
84
+ for (const [key, times] of requests.entries()) {
85
+ const filteredTimes = times.filter(time => time > windowStart);
86
+ if (filteredTimes.length === 0) {
87
+ requests.delete(key);
88
+ }
89
+ else {
90
+ requests.set(key, filteredTimes);
91
+ }
92
+ }
93
+ }
94
+ next();
95
+ };
96
+ }
97
+ /**
98
+ * Request validation middleware
99
+ */
100
+ function validateRequest(schema) {
101
+ return (req, res, next) => {
102
+ try {
103
+ if (schema.body && !schema.body(req.body)) {
104
+ res.status(400).json({ error: 'Invalid request body' });
105
+ return;
106
+ }
107
+ if (schema.query && !schema.query(req.query)) {
108
+ res.status(400).json({ error: 'Invalid query parameters' });
109
+ return;
110
+ }
111
+ if (schema.headers && !schema.headers(req.headers)) {
112
+ res.status(400).json({ error: 'Invalid headers' });
113
+ return;
114
+ }
115
+ next();
116
+ }
117
+ catch (error) {
118
+ res.status(400).json({ error: 'Validation error' });
119
+ }
120
+ };
121
+ }
122
+ /**
123
+ * Request logging middleware
124
+ */
125
+ function logger(options = {}) {
126
+ const { verbose = false, includeBody = false } = options;
127
+ return (req, res, next) => {
128
+ const start = Date.now();
129
+ const originalJson = res.json;
130
+ const originalSend = res.send;
131
+ const originalEnd = res.end;
132
+ let responseBody;
133
+ res.json = function (body) {
134
+ responseBody = body;
135
+ return originalJson.call(this, body);
136
+ };
137
+ res.send = function (body) {
138
+ responseBody = body;
139
+ return originalSend.call(this, body);
140
+ };
141
+ res.end = function (...args) {
142
+ const duration = Date.now() - start;
143
+ const logData = {
144
+ method: req.method,
145
+ url: req.url,
146
+ status: res.statusCode,
147
+ duration: `${duration}ms`,
148
+ };
149
+ if (verbose) {
150
+ logData.headers = req.headers;
151
+ logData.query = req.query;
152
+ }
153
+ if (includeBody && req.body) {
154
+ logData.requestBody = req.body;
155
+ }
156
+ console.log(`[API] ${req.method} ${req.url} - ${res.statusCode} (${duration}ms)`);
157
+ if (verbose) {
158
+ console.log(JSON.stringify(logData, null, 2));
159
+ }
160
+ return originalEnd.apply(this, args);
161
+ };
162
+ next();
163
+ };
164
+ }
165
+ /**
166
+ * Error handling middleware
167
+ */
168
+ function errorHandler() {
169
+ return async (req, res, next) => {
170
+ try {
171
+ await next();
172
+ }
173
+ catch (error) {
174
+ console.error('[API Error]', error);
175
+ const isDev = process.env.NODE_ENV === 'development';
176
+ res.status(500).json({
177
+ error: 'Internal server error',
178
+ ...(isDev && {
179
+ message: error instanceof Error ? error.message : 'Unknown error',
180
+ stack: error instanceof Error ? error.stack : undefined
181
+ }),
182
+ });
183
+ }
184
+ };
185
+ }
@@ -0,0 +1,63 @@
1
+ import { NextApiRequest, NextApiResponse } from 'next';
2
+ import { Middleware } from './middleware';
3
+ /**
4
+ * Compression middleware for API responses
5
+ */
6
+ export declare function compress(options?: {
7
+ threshold?: number;
8
+ level?: number;
9
+ }): Middleware;
10
+ /**
11
+ * Response time tracking
12
+ */
13
+ export declare function responseTime(): Middleware;
14
+ /**
15
+ * Pagination helper for API responses
16
+ */
17
+ export interface PaginationOptions {
18
+ page?: number;
19
+ limit?: number;
20
+ maxLimit?: number;
21
+ }
22
+ export interface PaginatedResponse<T> {
23
+ data: T[];
24
+ pagination: {
25
+ page: number;
26
+ limit: number;
27
+ total: number;
28
+ totalPages: number;
29
+ hasNext: boolean;
30
+ hasPrev: boolean;
31
+ };
32
+ }
33
+ export declare function paginate<T>(data: T[], options?: PaginationOptions): PaginatedResponse<T>;
34
+ /**
35
+ * Parse pagination params from request
36
+ */
37
+ export declare function getPaginationParams(req: NextApiRequest): PaginationOptions;
38
+ /**
39
+ * JSON response helper with consistent format
40
+ */
41
+ export declare function jsonResponse<T = any>(res: NextApiResponse, options: {
42
+ status?: number;
43
+ data?: T;
44
+ error?: string;
45
+ message?: string;
46
+ meta?: Record<string, any>;
47
+ }): void;
48
+ /**
49
+ * API response formatter middleware
50
+ */
51
+ export declare function formatResponse(): Middleware;
52
+ /**
53
+ * Request timeout middleware
54
+ */
55
+ export declare function timeout(ms: number): Middleware;
56
+ /**
57
+ * Body size limiter
58
+ */
59
+ export declare function bodyLimit(maxSize: number): Middleware;
60
+ /**
61
+ * Method filter middleware
62
+ */
63
+ export declare function allowMethods(methods: string[]): Middleware;
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compress = compress;
4
+ exports.responseTime = responseTime;
5
+ exports.paginate = paginate;
6
+ exports.getPaginationParams = getPaginationParams;
7
+ exports.jsonResponse = jsonResponse;
8
+ exports.formatResponse = formatResponse;
9
+ exports.timeout = timeout;
10
+ exports.bodyLimit = bodyLimit;
11
+ exports.allowMethods = allowMethods;
12
+ /**
13
+ * Compression middleware for API responses
14
+ */
15
+ function compress(options = {}) {
16
+ const { threshold = 1024 } = options; // 1KB default threshold
17
+ return (req, res, next) => {
18
+ const acceptEncoding = req.headers['accept-encoding'] || '';
19
+ if (!acceptEncoding.includes('gzip')) {
20
+ next();
21
+ return;
22
+ }
23
+ const originalJson = res.json;
24
+ const originalSend = res.send;
25
+ res.json = function (data) {
26
+ const jsonString = JSON.stringify(data);
27
+ if (jsonString.length > threshold) {
28
+ res.setHeader('Content-Encoding', 'gzip');
29
+ res.setHeader('Vary', 'Accept-Encoding');
30
+ }
31
+ return originalJson.call(this, data);
32
+ };
33
+ res.send = function (data) {
34
+ if (typeof data === 'string' && data.length > threshold) {
35
+ res.setHeader('Content-Encoding', 'gzip');
36
+ res.setHeader('Vary', 'Accept-Encoding');
37
+ }
38
+ return originalSend.call(this, data);
39
+ };
40
+ next();
41
+ };
42
+ }
43
+ /**
44
+ * Response time tracking
45
+ */
46
+ function responseTime() {
47
+ return (req, res, next) => {
48
+ const start = process.hrtime();
49
+ const originalEnd = res.end;
50
+ res.end = function (...args) {
51
+ const diff = process.hrtime(start);
52
+ const time = (diff[0] * 1e9 + diff[1]) / 1e6; // Convert to milliseconds
53
+ res.setHeader('X-Response-Time', `${time.toFixed(2)}ms`);
54
+ return originalEnd.apply(this, args);
55
+ };
56
+ next();
57
+ };
58
+ }
59
+ function paginate(data, options = {}) {
60
+ const { page = 1, limit = 10, maxLimit = 100, } = options;
61
+ const actualLimit = Math.min(limit, maxLimit);
62
+ const actualPage = Math.max(1, page);
63
+ const startIndex = (actualPage - 1) * actualLimit;
64
+ const endIndex = startIndex + actualLimit;
65
+ const paginatedData = data.slice(startIndex, endIndex);
66
+ const total = data.length;
67
+ const totalPages = Math.ceil(total / actualLimit);
68
+ return {
69
+ data: paginatedData,
70
+ pagination: {
71
+ page: actualPage,
72
+ limit: actualLimit,
73
+ total,
74
+ totalPages,
75
+ hasNext: actualPage < totalPages,
76
+ hasPrev: actualPage > 1,
77
+ },
78
+ };
79
+ }
80
+ /**
81
+ * Parse pagination params from request
82
+ */
83
+ function getPaginationParams(req) {
84
+ const page = parseInt(req.query.page) || 1;
85
+ const limit = parseInt(req.query.limit) || 10;
86
+ return { page, limit };
87
+ }
88
+ /**
89
+ * JSON response helper with consistent format
90
+ */
91
+ function jsonResponse(res, options) {
92
+ const { status = 200, data, error, message, meta } = options;
93
+ const response = {};
94
+ if (data !== undefined) {
95
+ response.data = data;
96
+ }
97
+ if (message) {
98
+ response.message = message;
99
+ }
100
+ if (error) {
101
+ response.error = error;
102
+ }
103
+ if (meta) {
104
+ response.meta = meta;
105
+ }
106
+ return res.status(status).json(response);
107
+ }
108
+ /**
109
+ * API response formatter middleware
110
+ */
111
+ function formatResponse() {
112
+ return (req, res, next) => {
113
+ const originalJson = res.json;
114
+ res.json = function (data) {
115
+ const formattedResponse = {
116
+ success: res.statusCode >= 200 && res.statusCode < 300,
117
+ statusCode: res.statusCode,
118
+ data,
119
+ timestamp: new Date().toISOString(),
120
+ };
121
+ return originalJson.call(this, formattedResponse);
122
+ };
123
+ next();
124
+ };
125
+ }
126
+ /**
127
+ * Request timeout middleware
128
+ */
129
+ function timeout(ms) {
130
+ return async (req, res, next) => {
131
+ const timeoutPromise = new Promise((_, reject) => {
132
+ setTimeout(() => {
133
+ reject(new Error(`Request timeout after ${ms}ms`));
134
+ }, ms);
135
+ });
136
+ try {
137
+ await Promise.race([
138
+ next(),
139
+ timeoutPromise,
140
+ ]);
141
+ }
142
+ catch (error) {
143
+ if (error instanceof Error && error.message.includes('timeout')) {
144
+ res.status(408).json({
145
+ error: 'Request timeout',
146
+ message: error.message,
147
+ });
148
+ }
149
+ else {
150
+ throw error;
151
+ }
152
+ }
153
+ };
154
+ }
155
+ /**
156
+ * Body size limiter
157
+ */
158
+ function bodyLimit(maxSize) {
159
+ return (req, res, next) => {
160
+ const contentLength = parseInt(req.headers['content-length'] || '0');
161
+ if (contentLength > maxSize) {
162
+ res.status(413).json({
163
+ error: 'Payload too large',
164
+ maxSize: `${maxSize} bytes`,
165
+ });
166
+ return;
167
+ }
168
+ next();
169
+ };
170
+ }
171
+ /**
172
+ * Method filter middleware
173
+ */
174
+ function allowMethods(methods) {
175
+ const allowedMethods = methods.map(m => m.toUpperCase());
176
+ return (req, res, next) => {
177
+ if (!req.method || !allowedMethods.includes(req.method.toUpperCase())) {
178
+ res.status(405).json({
179
+ error: 'Method not allowed',
180
+ allowed: allowedMethods,
181
+ });
182
+ return;
183
+ }
184
+ next();
185
+ };
186
+ }
package/dist/index.d.ts CHANGED
@@ -10,3 +10,6 @@ export * from "./performance/Lazy";
10
10
  export * from "./performance/cacheHeaders";
11
11
  export * from "./performance/securityHeaders";
12
12
  export * from "./analytics/webVitals";
13
+ export * from "./backend/middleware";
14
+ export * from "./backend/cache";
15
+ export * from "./backend/optimization";
package/dist/index.js CHANGED
@@ -30,3 +30,7 @@ __exportStar(require("./performance/cacheHeaders"), exports);
30
30
  __exportStar(require("./performance/securityHeaders"), exports);
31
31
  // Analytics
32
32
  __exportStar(require("./analytics/webVitals"), exports);
33
+ // Backend
34
+ __exportStar(require("./backend/middleware"), exports);
35
+ __exportStar(require("./backend/cache"), exports);
36
+ __exportStar(require("./backend/optimization"), exports);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rkk-next",
3
- "version": "1.0.0",
4
- "description": "SEO, routing and performance optimization SDK for Next.js",
3
+ "version": "1.1.0",
4
+ "description": "SEO, routing, performance optimization and backend utilities SDK for Next.js",
5
5
  "author": "Rohit Kumar Kundu",
6
6
  "license": "MIT",
7
7
  "main": "dist/index.js",
@@ -20,8 +20,10 @@
20
20
  "scripts": {
21
21
  "build": "tsc",
22
22
  "dev": "tsc --watch",
23
- "prepublishOnly": "npm run build",
24
- "test": "echo \"Tests coming soon\" && exit 0"
23
+ "test": "jest",
24
+ "test:watch": "jest --watch",
25
+ "test:coverage": "jest --coverage",
26
+ "prepublishOnly": "npm run build"
25
27
  },
26
28
  "peerDependencies": {
27
29
  "next": ">=12",
@@ -29,9 +31,15 @@
29
31
  "react-dom": ">=17"
30
32
  },
31
33
  "devDependencies": {
34
+ "@testing-library/jest-dom": "^6.1.5",
35
+ "@testing-library/react": "^14.1.2",
36
+ "@types/jest": "^29.5.11",
32
37
  "@types/node": "^20.10.0",
33
38
  "@types/react": "^18.2.45",
34
39
  "@types/react-dom": "^18.2.18",
40
+ "jest": "^29.7.0",
41
+ "jest-environment-jsdom": "^29.7.0",
42
+ "ts-jest": "^29.1.1",
35
43
  "typescript": "^5.3.3"
36
44
  },
37
45
  "keywords": [
@@ -46,6 +54,13 @@
46
54
  "react",
47
55
  "meta-tags",
48
56
  "json-ld",
49
- "prefetch"
57
+ "prefetch",
58
+ "middleware",
59
+ "api-routes",
60
+ "backend",
61
+ "caching",
62
+ "rate-limiting",
63
+ "cors",
64
+ "express"
50
65
  ]
51
66
  }