@syntay/fastay 0.2.5 → 0.2.7
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 +1223 -210
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +1 -0
- package/dist/utils/cookies.d.ts +20 -2
- package/dist/utils/cookies.d.ts.map +1 -1
- package/dist/utils/cookies.js +38 -17
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -1,68 +1,120 @@
|
|
|
1
|
+
# Fastay Documentation
|
|
1
2
|
<p align="center">
|
|
2
3
|
<img src="./fastay.png" width="200" />
|
|
3
4
|
</p>
|
|
5
|
+
Fastay is a modern backend framework built on Express.js, designed to create APIs quickly, predictably, and in a developer-friendly way.
|
|
6
|
+
|
|
7
|
+
It is **TypeScript-first**, file-based, auto-discovers routes and middlewares, and provides a clean development experience.
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
## Quick Navigation
|
|
6
10
|
|
|
7
|
-
Fastay
|
|
11
|
+
- [Fastay Philosophy](#fastay-philosophy)
|
|
12
|
+
- [Quick Start](#quick-start)
|
|
13
|
+
- [Project Structure](#project-structure)
|
|
14
|
+
- [Main Configuration](#main-configuration)
|
|
15
|
+
- [Routing System](#routing-system)
|
|
16
|
+
- [Middleware System](#middleware-system)
|
|
17
|
+
- [Comparison with Other Frameworks](#comparison-with-other-frameworks)
|
|
8
18
|
|
|
9
|
-
|
|
19
|
+
## Fastay Philosophy
|
|
10
20
|
|
|
11
|
-
|
|
21
|
+
### The Art of Intentional Simplicity
|
|
12
22
|
|
|
13
|
-
|
|
23
|
+
Fastay is born from an obsession with simplicity and speed, representing a minimalist approach to modern backend development. Our philosophy is based on principles that value efficiency without sacrificing power.
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
### Fundamental Principles
|
|
16
26
|
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- **Tratamento de erros** — mensagens claras de erro em runtime e boot no modo dev e production.
|
|
22
|
-
- **Extensível** — fácil de adicionar autenticação, logging ou qualquer biblioteca do Express.
|
|
27
|
+
#### 1. Less is More
|
|
28
|
+
- We eliminate unnecessary layers that don't add real value
|
|
29
|
+
- Focus on what's essential to build robust APIs
|
|
30
|
+
- Zero architectural bureaucracy that drains time and energy
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
#### 2. Freedom with Structure
|
|
33
|
+
- We provide a solid foundation without imposing limitations
|
|
34
|
+
- You maintain full control over how your project evolves
|
|
35
|
+
- Flexibility to scale according to your specific needs
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
#### 3. Fluid Development
|
|
38
|
+
- Intuitive and frictionless development experience
|
|
39
|
+
- Minimal configuration, maximum results
|
|
40
|
+
- Focus on business logic, not complex configurations
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
### Who Fastay Was Created For
|
|
29
43
|
|
|
30
|
-
|
|
44
|
+
**Ideal for:**
|
|
45
|
+
- Developers who value simplicity and efficiency
|
|
46
|
+
- Teams that need development speed
|
|
47
|
+
- Projects requiring long-term maintainability
|
|
48
|
+
- Those who prefer explicit code over complex magic
|
|
49
|
+
|
|
50
|
+
**Perfect Use Cases:**
|
|
51
|
+
- Small to medium-sized RESTful APIs
|
|
52
|
+
- Quick prototypes and MVPs
|
|
53
|
+
- Lightweight microservices
|
|
54
|
+
- Projects where development speed is crucial
|
|
55
|
+
|
|
56
|
+
### The Perfect Balance
|
|
57
|
+
|
|
58
|
+
Fastay finds the sweet spot between:
|
|
59
|
+
|
|
60
|
+
**Express.js (too minimalist) ← FASTAY → NestJS (too structured)**
|
|
61
|
+
|
|
62
|
+
**Developer freedom ← FASTAY → Smart conventions**
|
|
63
|
+
|
|
64
|
+
**Total flexibility ← FASTAY → Maximum productivity**
|
|
65
|
+
|
|
66
|
+
### Technical Manifesto
|
|
67
|
+
|
|
68
|
+
"We believe that frameworks should facilitate and not complicate. That complexity should be added by choice, not imposed by default. That developers deserve tools that respect their time and intelligence."
|
|
69
|
+
|
|
70
|
+
Fastay is not just a framework - it's a statement of principles: that it's possible to have power without complexity, structure without rigidity, and conventions without dictatorship.
|
|
71
|
+
|
|
72
|
+
[⬆ Back to Top](#fastay-documentation)
|
|
73
|
+
|
|
74
|
+
## Quick Start
|
|
75
|
+
|
|
76
|
+
### 1. Create a New Project
|
|
31
77
|
|
|
32
78
|
```bash
|
|
33
|
-
npx fastay create-app
|
|
79
|
+
npx fastay create-app my-app
|
|
34
80
|
```
|
|
35
81
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
✔
|
|
41
|
-
|
|
42
|
-
|
|
82
|
+
Example CLI interaction:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Fastay — Create a modern backend project
|
|
86
|
+
✔ Use TypeScript? › Yes
|
|
87
|
+
✔ Choose an ORM: › None
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Navigate to the Project
|
|
91
|
+
|
|
43
92
|
```bash
|
|
44
|
-
cd
|
|
93
|
+
cd my-app
|
|
45
94
|
```
|
|
46
|
-
|
|
95
|
+
|
|
96
|
+
### Start Development Server
|
|
97
|
+
|
|
47
98
|
```bash
|
|
48
|
-
npm run dev
|
|
99
|
+
npm run dev
|
|
49
100
|
```
|
|
50
|
-
|
|
101
|
+
|
|
102
|
+
### Watch Mode with Hot Reload
|
|
103
|
+
|
|
51
104
|
```bash
|
|
52
|
-
npm run dev:watch
|
|
105
|
+
npm run dev:watch
|
|
53
106
|
```
|
|
54
107
|
|
|
108
|
+
[⬆ Back to Top](#fastay-documentation)
|
|
55
109
|
|
|
56
|
-
|
|
110
|
+
## Project Structure
|
|
57
111
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
minha-app/
|
|
61
|
-
│
|
|
62
|
-
├── dist/ # Código compilado para produção
|
|
112
|
+
```
|
|
113
|
+
my-app/
|
|
63
114
|
│
|
|
115
|
+
├── dist/ # Compiled production code
|
|
64
116
|
├── src/
|
|
65
|
-
│ ├── api/
|
|
117
|
+
│ ├── api/ # API routes (auto-loaded)
|
|
66
118
|
│ │ ├── hello/
|
|
67
119
|
│ │ │ └── route.ts
|
|
68
120
|
│ │ ├── users/
|
|
@@ -70,373 +122,1334 @@ minha-app/
|
|
|
70
122
|
│ │ └── products/
|
|
71
123
|
│ │ └── route.ts
|
|
72
124
|
│ │
|
|
73
|
-
│ ├── middlewares/
|
|
125
|
+
│ ├── middlewares/ # Fastay middlewares
|
|
74
126
|
│ │ ├── auth.ts
|
|
75
127
|
│ │ ├── logger.ts
|
|
76
128
|
│ │ └── middleware.ts
|
|
77
129
|
│ │
|
|
78
|
-
│ ├── services/
|
|
130
|
+
│ ├── services/ # Business logic (recommended)
|
|
79
131
|
│ │ ├── user-service.ts
|
|
80
132
|
│ │ └── product-service.ts
|
|
81
133
|
│ │
|
|
82
|
-
│ ├── utils/
|
|
134
|
+
│ ├── utils/ # Helper functions
|
|
83
135
|
│ │ └── formatters.ts
|
|
84
136
|
│ │
|
|
85
|
-
│ └── index.ts
|
|
137
|
+
│ └── index.ts # Application entry point
|
|
86
138
|
│
|
|
87
|
-
├── fastay.config.json
|
|
139
|
+
├── fastay.config.json # Global framework configuration
|
|
88
140
|
├── package.json
|
|
89
141
|
├── tsconfig.json
|
|
90
142
|
└── eslint.config.mjs
|
|
91
143
|
```
|
|
92
144
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
`src/api` — Cada pasta representa um grupo de rotas. Todo route.ts dentro é registrado automaticamente.
|
|
145
|
+
### Main Directories Description
|
|
96
146
|
|
|
97
|
-
|
|
147
|
+
- **src/api** - Each folder represents a route group. Every route.ts is automatically registered
|
|
148
|
+
- **src/middlewares** - Custom middlewares, automatically loaded
|
|
149
|
+
- **src/services** - Keeps business logic separate from routes
|
|
150
|
+
- **src/utils** - Helpers and utility functions
|
|
151
|
+
- **src/index.ts** - Main application bootstrap
|
|
152
|
+
- **dist/** - Compiled production code
|
|
153
|
+
- **fastay.config.json** - Build and compiler configuration
|
|
98
154
|
|
|
99
|
-
|
|
155
|
+
[⬆ Back to Top](#fastay-documentation)
|
|
100
156
|
|
|
101
|
-
|
|
157
|
+
## Main Configuration
|
|
102
158
|
|
|
103
|
-
|
|
159
|
+
### src/index.ts File
|
|
104
160
|
|
|
105
|
-
|
|
161
|
+
```typescript
|
|
162
|
+
import { createApp } from '@syntay/fastay';
|
|
106
163
|
|
|
107
|
-
|
|
164
|
+
const port = 5555;
|
|
108
165
|
|
|
166
|
+
void (async () => {
|
|
167
|
+
await createApp({
|
|
168
|
+
apiDir: './src/api',
|
|
169
|
+
baseRoute: '/api',
|
|
170
|
+
port: port
|
|
171
|
+
});
|
|
172
|
+
})();
|
|
173
|
+
```
|
|
109
174
|
|
|
175
|
+
### createApp Configuration
|
|
110
176
|
|
|
111
|
-
|
|
177
|
+
The createApp method is the heart of Fastay, responsible for initializing and configuring the entire application. It accepts a flexible configuration object that allows customization from routes to global middlewares.
|
|
112
178
|
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
import { createApp } from '@syntay/fastay';
|
|
179
|
+
### Configuration Parameters
|
|
116
180
|
|
|
117
|
-
|
|
181
|
+
#### Basic Parameters
|
|
118
182
|
|
|
183
|
+
```typescript
|
|
119
184
|
void (async () => {
|
|
120
185
|
await createApp({
|
|
186
|
+
// Server port (optional) - default: 5000
|
|
187
|
+
port: 5000,
|
|
188
|
+
// Routes directory (optional) - default: './src/api'
|
|
121
189
|
apiDir: './src/api',
|
|
190
|
+
// Base route (optional) - default: '/api'
|
|
122
191
|
baseRoute: '/api',
|
|
123
|
-
|
|
192
|
+
// Fastay middlewares (optional)
|
|
193
|
+
middlewares: {
|
|
194
|
+
'/api/hello': [home]
|
|
195
|
+
}
|
|
124
196
|
});
|
|
125
197
|
})();
|
|
126
198
|
```
|
|
127
199
|
|
|
128
|
-
|
|
200
|
+
#### Complete Practical Example
|
|
129
201
|
|
|
130
|
-
|
|
202
|
+
```typescript
|
|
203
|
+
import { createApp } from '@syntay/fastay';
|
|
204
|
+
import { home } from './middlewares/home';
|
|
131
205
|
|
|
132
|
-
|
|
206
|
+
void (async () => {
|
|
207
|
+
await createApp({
|
|
208
|
+
port: 5000,
|
|
209
|
+
apiDir: './src/api',
|
|
210
|
+
baseRoute: '/api',
|
|
211
|
+
middlewares: {
|
|
212
|
+
'/api/hello': [home] // Middleware applied only to /api/hello route
|
|
213
|
+
},
|
|
214
|
+
expressOptions: {
|
|
215
|
+
// Express configurations...
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
})();
|
|
219
|
+
```
|
|
133
220
|
|
|
134
|
-
|
|
135
|
-
apiDir?: string; // Diretório contendo as rotas da API
|
|
136
|
-
(default: 'src/api')
|
|
137
|
-
baseRoute?: string; // Caminho base para todas as rotas (default: '/api')
|
|
138
|
-
port?: number; // Porta do servidor (default: 5000)
|
|
221
|
+
### Express Configurations (expressOptions)
|
|
139
222
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
223
|
+
Since Fastay is built on Express, you can leverage all Express functionalities through the expressOptions object.
|
|
224
|
+
|
|
225
|
+
#### Global Middlewares
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
expressOptions: {
|
|
229
|
+
middlewares: [
|
|
230
|
+
cors(),
|
|
231
|
+
helmet(),
|
|
232
|
+
(req, res, next) => {
|
|
233
|
+
res.setHeader('X-Powered-By', 'Fastay.js');
|
|
234
|
+
console.log("Global middleware executed");
|
|
235
|
+
next();
|
|
236
|
+
},
|
|
237
|
+
],
|
|
150
238
|
}
|
|
151
239
|
```
|
|
152
240
|
|
|
153
|
-
|
|
241
|
+
#### Body Parsers Configuration
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
expressOptions: {
|
|
245
|
+
jsonOptions: {
|
|
246
|
+
limit: '10mb', // Size limit for JSON
|
|
247
|
+
strict: true // Only objects and arrays
|
|
248
|
+
},
|
|
249
|
+
urlencodedOptions: {
|
|
250
|
+
extended: true, // Allows complex objects
|
|
251
|
+
limit: '10mb' // Size limit
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
```
|
|
154
255
|
|
|
155
|
-
|
|
256
|
+
#### Serve Static Files
|
|
156
257
|
|
|
157
|
-
```
|
|
258
|
+
```typescript
|
|
158
259
|
expressOptions: {
|
|
159
|
-
|
|
260
|
+
static: {
|
|
261
|
+
path: "public", // Static files directory
|
|
262
|
+
options: {
|
|
263
|
+
maxAge: "1d", // 1 day cache
|
|
264
|
+
etag: true // Enable ETag
|
|
265
|
+
}
|
|
266
|
+
},
|
|
160
267
|
}
|
|
161
268
|
```
|
|
162
269
|
|
|
163
|
-
|
|
270
|
+
#### Template Engine Configuration
|
|
164
271
|
|
|
165
|
-
```
|
|
272
|
+
```typescript
|
|
166
273
|
expressOptions: {
|
|
167
|
-
|
|
168
|
-
|
|
274
|
+
views: {
|
|
275
|
+
engine: "pug", // Template engine (Pug, EJS, etc.)
|
|
276
|
+
dir: "views" // Views directory
|
|
277
|
+
},
|
|
169
278
|
}
|
|
170
279
|
```
|
|
171
280
|
|
|
172
|
-
|
|
281
|
+
#### Global Local Variables
|
|
173
282
|
|
|
174
|
-
```
|
|
283
|
+
```typescript
|
|
175
284
|
expressOptions: {
|
|
176
|
-
|
|
285
|
+
locals: {
|
|
286
|
+
appName: "My Fastay App",
|
|
287
|
+
version: "1.0.0",
|
|
288
|
+
author: "Your Team"
|
|
289
|
+
},
|
|
177
290
|
}
|
|
178
291
|
```
|
|
179
292
|
|
|
180
|
-
|
|
293
|
+
#### Reverse Proxy Configuration
|
|
181
294
|
|
|
182
|
-
```
|
|
295
|
+
```typescript
|
|
183
296
|
expressOptions: {
|
|
184
|
-
|
|
185
|
-
locals: { siteName: 'Fastay' }
|
|
297
|
+
trustProxy: true, // Important for Nginx, Cloudflare, etc.
|
|
186
298
|
}
|
|
187
299
|
```
|
|
188
300
|
|
|
189
|
-
|
|
301
|
+
#### Custom Error Handler
|
|
190
302
|
|
|
191
|
-
```
|
|
303
|
+
```typescript
|
|
192
304
|
expressOptions: {
|
|
193
305
|
errorHandler: (err, req, res, next) => {
|
|
194
|
-
console.error(err);
|
|
195
|
-
|
|
196
|
-
|
|
306
|
+
console.error('Error captured:', err);
|
|
307
|
+
if (err.statusCode) {
|
|
308
|
+
res.status(err.statusCode).json({
|
|
309
|
+
error: err.message,
|
|
310
|
+
code: err.code
|
|
311
|
+
});
|
|
312
|
+
} else {
|
|
313
|
+
res.status(500).json({
|
|
314
|
+
error: 'Internal server error',
|
|
315
|
+
code: 'INTERNAL_ERROR'
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
},
|
|
197
319
|
}
|
|
198
320
|
```
|
|
199
321
|
|
|
322
|
+
### CORS Configuration
|
|
200
323
|
|
|
201
|
-
|
|
324
|
+
Fastay offers a simplified and powerful CORS configuration:
|
|
202
325
|
|
|
203
|
-
|
|
326
|
+
#### Complete CORS Example
|
|
204
327
|
|
|
205
|
-
```
|
|
206
|
-
{
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
328
|
+
```typescript
|
|
329
|
+
expressOptions: {
|
|
330
|
+
enableCors: {
|
|
331
|
+
// Allow requests from any origin (be careful in production)
|
|
332
|
+
allowAnyOrigin: true,
|
|
333
|
+
// Specific URLs that can send cookies
|
|
334
|
+
cookieOrigins: [
|
|
335
|
+
'https://mysite.com',
|
|
336
|
+
'https://app.mysite.com',
|
|
337
|
+
'http://localhost:3000'
|
|
338
|
+
],
|
|
339
|
+
// Enable cross-origin cookie sending
|
|
340
|
+
credentials: true,
|
|
341
|
+
// Allowed HTTP methods
|
|
342
|
+
methods: 'GET,POST,PUT,DELETE,OPTIONS,PATCH,HEAD',
|
|
343
|
+
// Allowed request headers
|
|
344
|
+
headers: 'Content-Type, Authorization, X-Requested-With, X-Custom-Header',
|
|
345
|
+
// Headers exposed to client
|
|
346
|
+
exposedHeaders: 'X-Custom-Header, X-Total-Count',
|
|
347
|
+
// Preflight request cache time (24 hours)
|
|
348
|
+
maxAge: 86400,
|
|
349
|
+
},
|
|
214
350
|
}
|
|
215
351
|
```
|
|
216
352
|
|
|
217
|
-
|
|
353
|
+
#### Secure Production Configuration
|
|
218
354
|
|
|
219
|
-
|
|
355
|
+
```typescript
|
|
356
|
+
expressOptions: {
|
|
357
|
+
enableCors: {
|
|
358
|
+
allowAnyOrigin: false,
|
|
359
|
+
cookieOrigins: [
|
|
360
|
+
'https://mydomain.com',
|
|
361
|
+
'https://api.mydomain.com'
|
|
362
|
+
],
|
|
363
|
+
credentials: true,
|
|
364
|
+
methods: 'GET,POST,PUT,DELETE',
|
|
365
|
+
headers: 'Content-Type, Authorization',
|
|
366
|
+
maxAge: 3600, // 1 hour
|
|
367
|
+
},
|
|
368
|
+
}
|
|
369
|
+
```
|
|
220
370
|
|
|
221
|
-
|
|
371
|
+
#### Development Configuration
|
|
222
372
|
|
|
223
|
-
|
|
373
|
+
```typescript
|
|
374
|
+
expressOptions: {
|
|
375
|
+
enableCors: {
|
|
376
|
+
allowAnyOrigin: true, // Allow any origin in development
|
|
377
|
+
credentials: true,
|
|
378
|
+
methods: 'GET,POST,PUT,DELETE,OPTIONS,PATCH',
|
|
379
|
+
headers: '*', // Allow all headers
|
|
380
|
+
maxAge: 86400,
|
|
381
|
+
},
|
|
382
|
+
}
|
|
383
|
+
```
|
|
224
384
|
|
|
385
|
+
### Complete Configuration Example
|
|
225
386
|
|
|
387
|
+
```typescript
|
|
388
|
+
import { createApp } from '@syntay/fastay';
|
|
389
|
+
import cors from 'cors';
|
|
390
|
+
import helmet from 'helmet';
|
|
391
|
+
import { authMiddleware } from './middlewares/auth';
|
|
392
|
+
import { loggerMiddleware } from './middlewares/logger';
|
|
393
|
+
|
|
394
|
+
void (async () => {
|
|
395
|
+
await createApp({
|
|
396
|
+
// Basic configurations
|
|
397
|
+
port: process.env.PORT || 5000,
|
|
398
|
+
apiDir: './src/api',
|
|
399
|
+
baseRoute: '/api/v1',
|
|
400
|
+
|
|
401
|
+
// Fastay middlewares
|
|
402
|
+
middlewares: {
|
|
403
|
+
'/api/v1/admin': [authMiddleware, loggerMiddleware],
|
|
404
|
+
'/api/v1/users': [authMiddleware],
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
// Express configurations
|
|
408
|
+
expressOptions: {
|
|
409
|
+
// Global middlewares
|
|
410
|
+
middlewares: [
|
|
411
|
+
helmet(),
|
|
412
|
+
(req, res, next) => {
|
|
413
|
+
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
|
|
414
|
+
next();
|
|
415
|
+
}
|
|
416
|
+
],
|
|
417
|
+
|
|
418
|
+
// CORS configuration
|
|
419
|
+
enableCors: {
|
|
420
|
+
allowAnyOrigin: process.env.NODE_ENV === 'development',
|
|
421
|
+
cookieOrigins: [
|
|
422
|
+
'https://mysite.com',
|
|
423
|
+
'https://app.mysite.com'
|
|
424
|
+
],
|
|
425
|
+
credentials: true,
|
|
426
|
+
methods: 'GET,POST,PUT,DELETE,OPTIONS',
|
|
427
|
+
headers: 'Content-Type, Authorization, X-API-Key',
|
|
428
|
+
maxAge: 86400,
|
|
429
|
+
},
|
|
430
|
+
|
|
431
|
+
// Body parsers
|
|
432
|
+
jsonOptions: {
|
|
433
|
+
limit: '10mb'
|
|
434
|
+
},
|
|
435
|
+
urlencodedOptions: {
|
|
436
|
+
extended: true
|
|
437
|
+
},
|
|
438
|
+
|
|
439
|
+
// Static files
|
|
440
|
+
static: {
|
|
441
|
+
path: "public",
|
|
442
|
+
options: {
|
|
443
|
+
maxAge: 3600000
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
|
|
447
|
+
// Template engine
|
|
448
|
+
views: {
|
|
449
|
+
engine: "ejs",
|
|
450
|
+
dir: "src/views"
|
|
451
|
+
},
|
|
452
|
+
|
|
453
|
+
// Local variables
|
|
454
|
+
locals: {
|
|
455
|
+
appName: "My API",
|
|
456
|
+
environment: process.env.NODE_ENV || 'development'
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
// Reverse proxy
|
|
460
|
+
trustProxy: true,
|
|
461
|
+
|
|
462
|
+
// Custom error handler
|
|
463
|
+
errorHandler: (err, req, res, next) => {
|
|
464
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
465
|
+
res.status(err.status || 500).json({
|
|
466
|
+
error: isProduction ? 'Something went wrong' : err.message,
|
|
467
|
+
...(!isProduction && { stack: err.stack })
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
})();
|
|
473
|
+
```
|
|
226
474
|
|
|
227
|
-
|
|
475
|
+
### Important Tips
|
|
228
476
|
|
|
229
|
-
|
|
477
|
+
#### Middleware Order
|
|
230
478
|
|
|
231
|
-
|
|
479
|
+
```typescript
|
|
480
|
+
// Order matters! Following the flow:
|
|
481
|
+
expressOptions: {
|
|
482
|
+
middlewares: [
|
|
483
|
+
helmet(), // 1. Security first
|
|
484
|
+
cors(), // 2. CORS before body parsers
|
|
485
|
+
express.json(), // 3. Body parsers
|
|
486
|
+
express.urlencoded(),
|
|
487
|
+
logger, // 4. Logging
|
|
488
|
+
auth // 5. Authentication
|
|
489
|
+
],
|
|
490
|
+
}
|
|
491
|
+
```
|
|
232
492
|
|
|
233
|
-
|
|
493
|
+
#### Environment-based Configuration
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
497
|
+
|
|
498
|
+
await createApp({
|
|
499
|
+
port: process.env.PORT || 5000,
|
|
500
|
+
expressOptions: {
|
|
501
|
+
enableCors: {
|
|
502
|
+
allowAnyOrigin: isDevelopment, // Only allowed in development
|
|
503
|
+
credentials: !isDevelopment, // Cookies only in production
|
|
504
|
+
},
|
|
505
|
+
trustProxy: !isDevelopment, // Proxy only in production
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
#### Specific vs Global Middlewares
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
await createApp({
|
|
514
|
+
// Specific route middlewares (Fastay)
|
|
515
|
+
middlewares: {
|
|
516
|
+
'/api/admin': [adminAuth, adminLogger], // Only for /api/admin
|
|
517
|
+
'/api/public': [rateLimit], // Only for /api/public
|
|
518
|
+
},
|
|
519
|
+
expressOptions: {
|
|
520
|
+
// Global middlewares (Express)
|
|
521
|
+
middlewares: [
|
|
522
|
+
cors(), // For all routes
|
|
523
|
+
helmet(), // For all routes
|
|
524
|
+
],
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Security Considerations
|
|
530
|
+
|
|
531
|
+
- **CORS in Production**: Never use `allowAnyOrigin: true` in production
|
|
532
|
+
- **Body Parser Limits**: Set reasonable limits to prevent attacks
|
|
533
|
+
- **Helmet**: Always include Helmet for basic security
|
|
534
|
+
- **Trust Proxy**: Configure correctly to avoid IP issues
|
|
535
|
+
|
|
536
|
+
[⬆ Back to Top](#fastay-documentation)
|
|
537
|
+
|
|
538
|
+
## Routing System
|
|
539
|
+
|
|
540
|
+
Fastay uses a file-based routing system that combines simplicity with power. Routes are self-discoverable and intuitively organized.
|
|
541
|
+
|
|
542
|
+
### API Folder Structure
|
|
543
|
+
|
|
544
|
+
In Fastay.js, API routes are placed inside the directory defined in apiDir (default: './src/api'). Each subfolder represents an API endpoint.
|
|
545
|
+
|
|
546
|
+
```
|
|
547
|
+
src/
|
|
548
|
+
├── api/
|
|
549
|
+
│ ├── hello/
|
|
550
|
+
│ │ └── route.ts # → /api/hello
|
|
551
|
+
│ ├── users/
|
|
552
|
+
│ │ └── route.ts # → /api/users
|
|
553
|
+
│ └── products/
|
|
554
|
+
│ └── route.ts # → /api/products
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Basic Route Definition
|
|
558
|
+
|
|
559
|
+
#### Clean and Intuitive Syntax
|
|
560
|
+
|
|
561
|
+
```typescript
|
|
234
562
|
import { Request } from '@syntay/fastay';
|
|
235
563
|
|
|
564
|
+
// GET /api/hello
|
|
236
565
|
export async function GET() {
|
|
237
566
|
return "Hello World";
|
|
238
567
|
}
|
|
239
568
|
|
|
569
|
+
// POST /api/hello
|
|
240
570
|
export async function POST(req: Request) {
|
|
241
571
|
return { message: 'Hello World' };
|
|
242
572
|
}
|
|
243
573
|
```
|
|
244
574
|
|
|
245
|
-
|
|
575
|
+
**Routing System Characteristics:**
|
|
576
|
+
- ✅ Each HTTP method is exported as a function
|
|
577
|
+
- ✅ Automatically registered by Fastay
|
|
578
|
+
- ✅ Fully typed and TypeScript compatible
|
|
579
|
+
- ✅ Supports native Express middlewares
|
|
246
580
|
|
|
247
|
-
|
|
581
|
+
### Supported HTTP Methods
|
|
248
582
|
|
|
249
|
-
|
|
583
|
+
You can handle all main HTTP methods in the same route file:
|
|
250
584
|
|
|
251
|
-
|
|
585
|
+
```typescript
|
|
586
|
+
// api/users/route.ts
|
|
587
|
+
import { Request } from '@syntay/fastay';
|
|
252
588
|
|
|
253
|
-
|
|
589
|
+
// GET /api/users
|
|
590
|
+
export async function GET() {
|
|
591
|
+
const users = [
|
|
592
|
+
{ id: 1, name: 'John' },
|
|
593
|
+
{ id: 2, name: 'Mary' }
|
|
594
|
+
];
|
|
595
|
+
return users;
|
|
596
|
+
}
|
|
254
597
|
|
|
255
|
-
|
|
598
|
+
// POST /api/users
|
|
599
|
+
export async function POST(req: Request) {
|
|
600
|
+
const userData = await req.body;
|
|
601
|
+
// Save user to database
|
|
602
|
+
return { message: 'User created successfully', user: userData };
|
|
603
|
+
}
|
|
256
604
|
|
|
257
|
-
|
|
605
|
+
// PUT /api/users
|
|
606
|
+
export async function PUT(req: Request) {
|
|
607
|
+
const userData = await req.body;
|
|
608
|
+
// Update user
|
|
609
|
+
return { message: 'User updated', user: userData };
|
|
610
|
+
}
|
|
258
611
|
|
|
259
|
-
|
|
612
|
+
// DELETE /api/users
|
|
613
|
+
export async function DELETE(req: Request) {
|
|
614
|
+
// Delete user
|
|
615
|
+
return { message: 'User deleted' };
|
|
616
|
+
}
|
|
260
617
|
|
|
261
|
-
|
|
618
|
+
// PATCH /api/users
|
|
619
|
+
export async function PATCH(req: Request) {
|
|
620
|
+
const updates = await req.body;
|
|
621
|
+
// Partial update
|
|
622
|
+
return { message: 'User partially updated', updates };
|
|
623
|
+
}
|
|
624
|
+
```
|
|
262
625
|
|
|
626
|
+
### Advanced Response System
|
|
263
627
|
|
|
264
|
-
|
|
628
|
+
Fastay offers a flexible system for building HTTP responses with different content types.
|
|
265
629
|
|
|
266
|
-
|
|
267
|
-
import express from 'express';
|
|
630
|
+
#### JSON Response (Default)
|
|
268
631
|
|
|
269
|
-
|
|
632
|
+
```typescript
|
|
633
|
+
export async function GET() {
|
|
634
|
+
return {
|
|
635
|
+
success: true,
|
|
636
|
+
data: { id: 1, name: 'John' }
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
```
|
|
270
640
|
|
|
271
|
-
|
|
272
|
-
app.get('/api/hello', (req, res) => {
|
|
273
|
-
res.json({ message: 'Hello World' });
|
|
274
|
-
});
|
|
641
|
+
#### STRING Response
|
|
275
642
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
643
|
+
```typescript
|
|
644
|
+
export async function GET() {
|
|
645
|
+
return 'John Doe'
|
|
646
|
+
}
|
|
647
|
+
```
|
|
280
648
|
|
|
281
|
-
|
|
282
|
-
app.use('/api/auth', (req, res, next) => {
|
|
283
|
-
console.log('Auth middleware');
|
|
284
|
-
next();
|
|
285
|
-
});
|
|
649
|
+
#### NUMBER Response
|
|
286
650
|
|
|
287
|
-
|
|
651
|
+
```typescript
|
|
652
|
+
export async function GET() {
|
|
653
|
+
return 1975
|
|
654
|
+
}
|
|
288
655
|
```
|
|
289
656
|
|
|
290
|
-
|
|
657
|
+
#### Response with Custom Status Code
|
|
291
658
|
|
|
292
|
-
|
|
659
|
+
```typescript
|
|
660
|
+
export async function POST(req: Request) {
|
|
661
|
+
const data = await req.body;
|
|
662
|
+
return {
|
|
663
|
+
status: 201, // Created
|
|
664
|
+
body: {
|
|
665
|
+
message: 'Resource created successfully',
|
|
666
|
+
data
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
```
|
|
293
671
|
|
|
294
|
-
|
|
672
|
+
#### Response with Cookies
|
|
295
673
|
|
|
296
|
-
|
|
674
|
+
```typescript
|
|
675
|
+
export async function POST(req: Request) {
|
|
676
|
+
const token = 'jwt_token_here';
|
|
677
|
+
const cookies = {
|
|
678
|
+
user_token: {
|
|
679
|
+
value: token,
|
|
680
|
+
options: {
|
|
681
|
+
httpOnly: true,
|
|
682
|
+
secure: process.env.NODE_ENV === 'production',
|
|
683
|
+
path: '/',
|
|
684
|
+
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
|
|
685
|
+
domain: process.env.NODE_ENV === 'production' ? 'yoursite.com' : 'localhost',
|
|
686
|
+
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
return {
|
|
692
|
+
cookies,
|
|
693
|
+
status: 200,
|
|
694
|
+
body: {
|
|
695
|
+
message: "User registered successfully"
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
```
|
|
297
700
|
|
|
701
|
+
#### Response with Custom Headers
|
|
298
702
|
|
|
703
|
+
```typescript
|
|
704
|
+
export async function GET() {
|
|
705
|
+
return {
|
|
706
|
+
headers: {
|
|
707
|
+
'Content-Type': 'application/json',
|
|
708
|
+
'X-Custom-Header': 'custom-value',
|
|
709
|
+
'Cache-Control': 'no-cache'
|
|
710
|
+
},
|
|
711
|
+
body: {
|
|
712
|
+
data: 'content'
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
```
|
|
299
717
|
|
|
300
|
-
|
|
718
|
+
#### Redirection
|
|
301
719
|
|
|
302
|
-
|
|
720
|
+
```typescript
|
|
721
|
+
export async function GET() {
|
|
722
|
+
// Temporary redirection (302)
|
|
723
|
+
return {
|
|
724
|
+
redirect: '/new-route',
|
|
725
|
+
status: 302
|
|
726
|
+
};
|
|
727
|
+
}
|
|
303
728
|
|
|
304
|
-
|
|
305
|
-
|
|
729
|
+
export async function POST() {
|
|
730
|
+
// Permanent redirection (301)
|
|
731
|
+
return {
|
|
732
|
+
redirect: 'https://example.com',
|
|
733
|
+
status: 301
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
```
|
|
306
737
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
738
|
+
#### File Download
|
|
739
|
+
|
|
740
|
+
```typescript
|
|
741
|
+
export async function GET() {
|
|
742
|
+
return {
|
|
743
|
+
file: {
|
|
744
|
+
path: '/path/to/report.pdf',
|
|
745
|
+
downloadName: 'monthly-report.pdf'
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### Data Stream
|
|
752
|
+
|
|
753
|
+
```typescript
|
|
754
|
+
import fs from 'fs';
|
|
755
|
+
|
|
756
|
+
export async function GET() {
|
|
757
|
+
return {
|
|
758
|
+
stream: fs.createReadStream('/videos/movie.mp4'),
|
|
759
|
+
headers: {
|
|
760
|
+
'Content-Type': 'video/mp4'
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
#### Raw Response (Buffer/String)
|
|
767
|
+
|
|
768
|
+
```typescript
|
|
769
|
+
export async function GET() {
|
|
770
|
+
return {
|
|
771
|
+
raw: Buffer.from('Hello World in plain text'),
|
|
772
|
+
headers: {
|
|
773
|
+
'Content-Type': 'text/plain'
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Dynamic Routes
|
|
780
|
+
|
|
781
|
+
#### URL Parameters
|
|
782
|
+
|
|
783
|
+
```typescript
|
|
784
|
+
// api/users/[id]/route.ts
|
|
785
|
+
import { Request } from '@syntay/fastay';
|
|
786
|
+
|
|
787
|
+
export async function GET(req: Request) {
|
|
788
|
+
const { id } = req.params;
|
|
789
|
+
// Find user by ID
|
|
790
|
+
return {
|
|
791
|
+
message: `User details with ID: ${id}`,
|
|
792
|
+
user: { id, name: `User ${id}` }
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
**Access:** `GET /api/users/123` → `{ id: '123' }`
|
|
798
|
+
|
|
799
|
+
#### Query Parameters
|
|
800
|
+
|
|
801
|
+
```typescript
|
|
802
|
+
// api/users/route.ts
|
|
803
|
+
import { Request } from '@syntay/fastay';
|
|
804
|
+
|
|
805
|
+
interface UserQuery {
|
|
806
|
+
name?: string;
|
|
807
|
+
email?: string;
|
|
808
|
+
page?: number;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
export async function GET(req: Request) {
|
|
812
|
+
const query: UserQuery = req.query;
|
|
813
|
+
const { name, email, page = 1 } = query;
|
|
814
|
+
|
|
815
|
+
// Find users with filters
|
|
816
|
+
return {
|
|
817
|
+
users: [
|
|
818
|
+
{ id: 1, name, email },
|
|
819
|
+
{ id: 2, name: 'Mary', email: 'mary@email.com' }
|
|
820
|
+
],
|
|
821
|
+
pagination: {
|
|
822
|
+
page,
|
|
823
|
+
totalPages: 5
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
**Access:** `GET /api/users?name=John&email=john@email.com&page=2`
|
|
830
|
+
|
|
831
|
+
#### Combining Parameters and Query
|
|
832
|
+
|
|
833
|
+
```typescript
|
|
834
|
+
// api/users/[id]/posts/route.ts
|
|
835
|
+
import { Request } from '@syntay/fastay';
|
|
836
|
+
|
|
837
|
+
export async function GET(req: Request) {
|
|
838
|
+
const { id } = req.params; // User ID
|
|
839
|
+
const { category, limit = 10 } = req.query; // Filters
|
|
840
|
+
|
|
841
|
+
return {
|
|
842
|
+
userId: id,
|
|
843
|
+
posts: [
|
|
844
|
+
{ id: 1, title: 'Post 1', category },
|
|
845
|
+
{ id: 2, title: 'Post 2', category }
|
|
846
|
+
],
|
|
847
|
+
filters: { category, limit }
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
### Working with FormData
|
|
853
|
+
|
|
854
|
+
#### File Uploads and Forms
|
|
855
|
+
|
|
856
|
+
```typescript
|
|
857
|
+
// api/upload/route.ts
|
|
858
|
+
import { Request } from '@syntay/fastay';
|
|
859
|
+
|
|
860
|
+
export async function POST(req: Request) {
|
|
861
|
+
const formData = await req.formData();
|
|
862
|
+
const id = formData.get('id') as string;
|
|
863
|
+
const name = formData.get('name') as string;
|
|
864
|
+
const image = formData.get('image') as File;
|
|
865
|
+
|
|
866
|
+
// Process image upload
|
|
867
|
+
console.log('File received:', image.name, image.size);
|
|
868
|
+
|
|
869
|
+
return {
|
|
870
|
+
message: 'Upload completed successfully',
|
|
871
|
+
data: { id, name, fileName: image.name }
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### Working with Cookies
|
|
877
|
+
|
|
878
|
+
#### Reading Cookies
|
|
879
|
+
|
|
880
|
+
```typescript
|
|
881
|
+
export async function GET(req: Request) {
|
|
882
|
+
// Check if cookie exists
|
|
883
|
+
if (req.cookies.has('user_token')) {
|
|
884
|
+
// Get cookie value
|
|
885
|
+
const token = req.cookies.get('user_token');
|
|
886
|
+
return {
|
|
887
|
+
authenticated: true,
|
|
888
|
+
user: { token }
|
|
889
|
+
};
|
|
312
890
|
}
|
|
313
891
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
892
|
+
return { authenticated: false };
|
|
893
|
+
}
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
#### Available Cookie Methods
|
|
897
|
+
|
|
898
|
+
```typescript
|
|
899
|
+
export async function GET(req: Request) {
|
|
900
|
+
// Check existence
|
|
901
|
+
const hasToken = req.cookies.has('user_token');
|
|
902
|
+
// Get value
|
|
903
|
+
const token = req.cookies.get('user_token');
|
|
904
|
+
// Get all cookies
|
|
905
|
+
const allCookies = req.cookies.all();
|
|
906
|
+
|
|
907
|
+
return {
|
|
908
|
+
cookieInfo: {
|
|
909
|
+
hasToken,
|
|
910
|
+
token,
|
|
911
|
+
allCookies
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
### Error Handling
|
|
918
|
+
|
|
919
|
+
#### Try/Catch Block
|
|
920
|
+
|
|
921
|
+
```typescript
|
|
922
|
+
export async function GET() {
|
|
923
|
+
try {
|
|
924
|
+
const data = await fetchExternalData();
|
|
925
|
+
return { data };
|
|
926
|
+
} catch (error) {
|
|
927
|
+
return {
|
|
928
|
+
status: 500,
|
|
929
|
+
body: {
|
|
930
|
+
error: 'Internal server error',
|
|
931
|
+
message: error.message
|
|
932
|
+
}
|
|
933
|
+
};
|
|
317
934
|
}
|
|
318
935
|
}
|
|
319
936
|
```
|
|
320
937
|
|
|
321
|
-
|
|
938
|
+
#### Errors with Specific Status Codes
|
|
322
939
|
|
|
323
|
-
|
|
940
|
+
```typescript
|
|
941
|
+
export async function GET(req: Request) {
|
|
942
|
+
const { id } = req.params;
|
|
943
|
+
const user = await findUserById(id);
|
|
324
944
|
|
|
325
|
-
|
|
945
|
+
if (!user) {
|
|
946
|
+
return {
|
|
947
|
+
status: 404,
|
|
948
|
+
body: { error: 'User not found' }
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (!user.active) {
|
|
953
|
+
return {
|
|
954
|
+
status: 403,
|
|
955
|
+
body: { error: 'User inactive' }
|
|
956
|
+
};
|
|
957
|
+
}
|
|
326
958
|
|
|
327
|
-
|
|
959
|
+
return { user };
|
|
960
|
+
}
|
|
961
|
+
```
|
|
328
962
|
|
|
329
|
-
|
|
963
|
+
#### Data Validation
|
|
330
964
|
|
|
965
|
+
```typescript
|
|
966
|
+
export async function POST(req: Request) {
|
|
967
|
+
const userData = await req.body;
|
|
968
|
+
|
|
969
|
+
// Simple validation
|
|
970
|
+
if (!userData.name || !userData.email) {
|
|
971
|
+
return {
|
|
972
|
+
status: 400,
|
|
973
|
+
body: {
|
|
974
|
+
error: 'Invalid data',
|
|
975
|
+
required: ['name', 'email']
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
}
|
|
331
979
|
|
|
980
|
+
// Process valid data
|
|
981
|
+
return {
|
|
982
|
+
status: 201,
|
|
983
|
+
body: {
|
|
984
|
+
message: 'User created',
|
|
985
|
+
user: userData
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
```
|
|
332
990
|
|
|
333
|
-
|
|
991
|
+
### Complete Practical Examples
|
|
334
992
|
|
|
335
|
-
|
|
993
|
+
#### Complete Blog API
|
|
336
994
|
|
|
337
|
-
```
|
|
995
|
+
```typescript
|
|
996
|
+
// api/posts/route.ts
|
|
338
997
|
import { Request } from '@syntay/fastay';
|
|
339
998
|
|
|
340
|
-
// GET /api/
|
|
341
|
-
export async function GET() {
|
|
342
|
-
|
|
999
|
+
// GET /api/posts - List posts with pagination
|
|
1000
|
+
export async function GET(req: Request) {
|
|
1001
|
+
const { page = 1, limit = 10, category } = req.query;
|
|
1002
|
+
const posts = await findPosts({
|
|
1003
|
+
page: parseInt(page),
|
|
1004
|
+
limit: parseInt(limit),
|
|
1005
|
+
category
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
return {
|
|
1009
|
+
posts,
|
|
1010
|
+
pagination: {
|
|
1011
|
+
page,
|
|
1012
|
+
limit,
|
|
1013
|
+
total: posts.length
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
343
1016
|
}
|
|
344
1017
|
|
|
345
|
-
// POST /api/
|
|
1018
|
+
// POST /api/posts - Create new post
|
|
346
1019
|
export async function POST(req: Request) {
|
|
347
|
-
|
|
1020
|
+
const postData = await req.body;
|
|
1021
|
+
|
|
1022
|
+
// Validation
|
|
1023
|
+
if (!postData.title || !postData.content) {
|
|
1024
|
+
return {
|
|
1025
|
+
status: 400,
|
|
1026
|
+
body: { error: 'Title and content are required' }
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
const newPost = await createPost(postData);
|
|
1031
|
+
return {
|
|
1032
|
+
status: 201,
|
|
1033
|
+
body: {
|
|
1034
|
+
message: 'Post created successfully',
|
|
1035
|
+
post: newPost
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
348
1038
|
}
|
|
349
1039
|
```
|
|
350
1040
|
|
|
351
|
-
|
|
1041
|
+
#### Authentication API
|
|
1042
|
+
|
|
1043
|
+
```typescript
|
|
1044
|
+
// api/auth/login/route.ts
|
|
1045
|
+
import { Request } from '@syntay/fastay';
|
|
352
1046
|
|
|
353
|
-
|
|
1047
|
+
export async function POST(req: Request) {
|
|
1048
|
+
const { email, password } = await req.body;
|
|
1049
|
+
|
|
1050
|
+
// Verify credentials
|
|
1051
|
+
const user = await verifyCredentials(email, password);
|
|
1052
|
+
if (!user) {
|
|
1053
|
+
return {
|
|
1054
|
+
status: 401,
|
|
1055
|
+
body: { error: 'Invalid credentials' }
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
354
1058
|
|
|
355
|
-
|
|
1059
|
+
// Generate token
|
|
1060
|
+
const token = generateJWTToken(user);
|
|
1061
|
+
|
|
1062
|
+
const cookies = {
|
|
1063
|
+
auth_token: {
|
|
1064
|
+
value: token,
|
|
1065
|
+
options: {
|
|
1066
|
+
httpOnly: true,
|
|
1067
|
+
secure: process.env.NODE_ENV === 'production',
|
|
1068
|
+
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
|
1069
|
+
path: '/'
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
return {
|
|
1075
|
+
cookies,
|
|
1076
|
+
body: {
|
|
1077
|
+
message: 'Login successful',
|
|
1078
|
+
user: {
|
|
1079
|
+
id: user.id,
|
|
1080
|
+
name: user.name
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
```
|
|
356
1086
|
|
|
357
|
-
|
|
1087
|
+
### Best Practices Tips
|
|
358
1088
|
|
|
359
|
-
|
|
1089
|
+
#### Route Organization
|
|
360
1090
|
|
|
361
|
-
|
|
1091
|
+
```
|
|
1092
|
+
src/api/
|
|
1093
|
+
├── users/
|
|
1094
|
+
│ ├── route.ts # Basic operations
|
|
1095
|
+
│ ├── [id]/
|
|
1096
|
+
│ │ └── route.ts # Operations by ID
|
|
1097
|
+
│ └── auth/
|
|
1098
|
+
│ └── route.ts # Authentication
|
|
1099
|
+
├── posts/
|
|
1100
|
+
│ ├── route.ts
|
|
1101
|
+
│ └── [id]/
|
|
1102
|
+
│ └── comments/
|
|
1103
|
+
│ └── route.ts # Post comments
|
|
1104
|
+
```
|
|
362
1105
|
|
|
1106
|
+
#### Consistent Validation
|
|
363
1107
|
|
|
1108
|
+
```typescript
|
|
1109
|
+
// utils/validation.ts
|
|
1110
|
+
export function validateUser(data: any) {
|
|
1111
|
+
const errors = [];
|
|
1112
|
+
if (!data.name) errors.push('Name is required');
|
|
1113
|
+
if (!data.email) errors.push('Email is required');
|
|
1114
|
+
if (!validateEmail(data.email)) errors.push('Invalid email');
|
|
1115
|
+
return errors;
|
|
1116
|
+
}
|
|
364
1117
|
|
|
365
|
-
|
|
1118
|
+
// api/users/route.ts
|
|
1119
|
+
export async function POST(req: Request) {
|
|
1120
|
+
const userData = await req.body;
|
|
1121
|
+
const errors = validateUser(userData);
|
|
1122
|
+
|
|
1123
|
+
if (errors.length > 0) {
|
|
1124
|
+
return {
|
|
1125
|
+
status: 400,
|
|
1126
|
+
body: { errors: errors }
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
366
1129
|
|
|
1130
|
+
// Process valid data...
|
|
1131
|
+
}
|
|
1132
|
+
```
|
|
367
1133
|
|
|
1134
|
+
#### Standardized Responses
|
|
368
1135
|
|
|
1136
|
+
```typescript
|
|
1137
|
+
// utils/response.ts
|
|
1138
|
+
export function success(data: any, message = 'Success') {
|
|
1139
|
+
return {
|
|
1140
|
+
status: 'success',
|
|
1141
|
+
message,
|
|
1142
|
+
data
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
369
1145
|
|
|
370
|
-
|
|
1146
|
+
export function error(message: string, code = 'ERROR') {
|
|
1147
|
+
return {
|
|
1148
|
+
status: 'error',
|
|
1149
|
+
message,
|
|
1150
|
+
code
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
371
1153
|
|
|
372
|
-
|
|
1154
|
+
// Usage in routes
|
|
1155
|
+
export async function GET() {
|
|
1156
|
+
try {
|
|
1157
|
+
const users = await findUsers();
|
|
1158
|
+
return success(users, 'Users listed successfully');
|
|
1159
|
+
} catch (err) {
|
|
1160
|
+
return error('Error fetching users');
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
```
|
|
373
1164
|
|
|
374
|
-
|
|
1165
|
+
[⬆ Back to Top](#fastay-documentation)
|
|
375
1166
|
|
|
376
|
-
|
|
1167
|
+
## Middleware System
|
|
1168
|
+
|
|
1169
|
+
### Auto-loaded Middlewares
|
|
1170
|
+
|
|
1171
|
+
```typescript
|
|
377
1172
|
import { Request, Response, Next } from '@syntay/fastay';
|
|
378
1173
|
|
|
379
1174
|
export async function auth(req: Request, _res: Response, next: Next) {
|
|
380
|
-
//
|
|
1175
|
+
// Authentication logic
|
|
381
1176
|
next();
|
|
382
1177
|
}
|
|
383
1178
|
```
|
|
384
1179
|
|
|
385
|
-
|
|
1180
|
+
### Middleware Configuration in Fastay.js
|
|
386
1181
|
|
|
387
|
-
|
|
1182
|
+
In Fastay.js, middlewares are used to intercept and process requests before they reach the defined routes. They work as "intermediate functions" that can perform actions like authentication, data validation, logging, among others, and are executed in the defined sequence until the request reaches the final route.
|
|
1183
|
+
|
|
1184
|
+
#### Execution Flow
|
|
1185
|
+
|
|
1186
|
+
```
|
|
1187
|
+
Request → Middleware 1 → Middleware 2 → ... → Final Route → Response
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
### Basic Structure
|
|
1191
|
+
|
|
1192
|
+
#### Recommended File Structure
|
|
1193
|
+
|
|
1194
|
+
```
|
|
1195
|
+
src/
|
|
1196
|
+
├── middlewares/
|
|
1197
|
+
│ ├── auth.ts # Authentication middleware
|
|
1198
|
+
│ ├── validation.ts # Validation middleware
|
|
1199
|
+
│ ├── logger.ts # Logging middleware
|
|
1200
|
+
│ └── middleware.ts # Main configuration
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
### Configuration
|
|
1204
|
+
|
|
1205
|
+
#### createMiddleware Method
|
|
1206
|
+
|
|
1207
|
+
The createMiddleware method allows associating middlewares with specific routes:
|
|
1208
|
+
|
|
1209
|
+
```typescript
|
|
1210
|
+
export const middleware = createMiddleware({
|
|
1211
|
+
// Syntax: [route]: [array-of-middlewares]
|
|
1212
|
+
'/api/specific-route': [middleware1, middleware2],
|
|
1213
|
+
// Multiple routes
|
|
1214
|
+
'/api/users': [auth],
|
|
1215
|
+
'/api/posts': [auth, logger],
|
|
1216
|
+
'/api/public': [logger],
|
|
1217
|
+
});
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
### Defining Middlewares in Fastay.js
|
|
1221
|
+
|
|
1222
|
+
Middlewares in Fastay.js are defined by convention in a directory called middlewares. Inside this directory, you create a middleware.ts (or middleware.js) file, where you can associate each middleware with a specific route.
|
|
1223
|
+
|
|
1224
|
+
#### middleware.ts File Example
|
|
1225
|
+
|
|
1226
|
+
The middleware.ts file is responsible for loading and applying middlewares to specific routes.
|
|
1227
|
+
|
|
1228
|
+
```typescript
|
|
1229
|
+
// src/middlewares/middleware.ts
|
|
388
1230
|
import { createMiddleware } from '@syntay/fastay';
|
|
389
|
-
import {
|
|
390
|
-
import {
|
|
1231
|
+
import { user } from './user';
|
|
1232
|
+
import { home } from './home';
|
|
391
1233
|
|
|
1234
|
+
// Here, you define the routes and middlewares that will be executed before each route
|
|
392
1235
|
export const middleware = createMiddleware({
|
|
393
|
-
'/
|
|
394
|
-
'/
|
|
1236
|
+
'/api/users': [user], // User middleware will be applied to /api/users route
|
|
1237
|
+
'/api/hello': [home], // Home middleware will be applied to /api/hello route
|
|
395
1238
|
});
|
|
396
1239
|
```
|
|
397
1240
|
|
|
398
|
-
|
|
1241
|
+
- **Route (/api/users)**: The specific route where the middleware will be executed. This allows you to associate middlewares with specific routes.
|
|
1242
|
+
- **Array of Middlewares ([user])**: An array of middlewares that will be executed before the route execution. There can be multiple middlewares in an array, and they will be executed in the order they are defined.
|
|
1243
|
+
|
|
1244
|
+
### Middleware Structure
|
|
1245
|
+
|
|
1246
|
+
A middleware in Fastay.js is basically an asynchronous function that receives three parameters: request, response, and next. The next() is used to indicate that the middleware has finished its execution and that the request can continue to the next middleware or to the target route.
|
|
1247
|
+
|
|
1248
|
+
#### user.ts Middleware Example
|
|
1249
|
+
|
|
1250
|
+
```typescript
|
|
1251
|
+
// src/middlewares/user.ts
|
|
1252
|
+
import { Next, Request, Response } from '@syntay/fastay';
|
|
1253
|
+
|
|
1254
|
+
export async function user(request: Request, _response: Response, next: Next) {
|
|
1255
|
+
console.log('User middleware executed');
|
|
1256
|
+
// Middleware logic, such as authentication or validation
|
|
1257
|
+
// Call the next middleware or route
|
|
1258
|
+
next();
|
|
1259
|
+
}
|
|
1260
|
+
```
|
|
399
1261
|
|
|
400
|
-
**
|
|
1262
|
+
- **request**: The request object containing the request data.
|
|
1263
|
+
- **response**: The response object, which allows manipulating the response before sending it to the client.
|
|
1264
|
+
- **next()**: Calls the next function in the middleware chain or the target route. If you don't call next(), the request will be "stuck" and won't proceed to the next middleware or route.
|
|
401
1265
|
|
|
402
|
-
|
|
1266
|
+
### Middleware Behavior
|
|
403
1267
|
|
|
1268
|
+
- **Sequential Execution**: Middlewares are executed sequentially. If you have multiple middlewares for the same route, they will be called in the order they are defined in the array.
|
|
1269
|
+
- **Execution Interruption**: If any middleware doesn't call next() or returns a response, the execution will be interrupted and the request won't proceed to the next middleware or route.
|
|
404
1270
|
|
|
405
|
-
|
|
1271
|
+
#### Execution Order
|
|
406
1272
|
|
|
407
|
-
|
|
1273
|
+
```typescript
|
|
1274
|
+
export const middleware = createMiddleware({
|
|
1275
|
+
'/api/protected': [
|
|
1276
|
+
middleware1, // Executed first
|
|
1277
|
+
middleware2, // Executed second
|
|
1278
|
+
middleware3 // Executed third
|
|
1279
|
+
],
|
|
1280
|
+
});
|
|
1281
|
+
```
|
|
408
1282
|
|
|
409
|
-
|
|
1283
|
+
#### Validation Middleware Example
|
|
410
1284
|
|
|
411
|
-
|
|
1285
|
+
```typescript
|
|
1286
|
+
// src/middlewares/validate.ts
|
|
1287
|
+
import { Next, Request, Response } from '@syntay/fastay';
|
|
412
1288
|
|
|
413
|
-
|
|
1289
|
+
export async function validate(request: Request, response: Response, next: Next) {
|
|
1290
|
+
if (!request.headers['authorization']) {
|
|
1291
|
+
response.status(400).json({ error: 'Missing authorization header' });
|
|
1292
|
+
} else {
|
|
1293
|
+
next(); // If validation passes, call next middleware or route
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
```
|
|
414
1297
|
|
|
415
|
-
|
|
1298
|
+
### Caution with Heavy Processing
|
|
416
1299
|
|
|
417
|
-
|
|
1300
|
+
It's important to remember that middlewares should not be used for heavy tasks, such as processing large file uploads, database interactions, or complex calculations. The purpose of middlewares is to be lightweight and fast, with tasks like authentication, validation, or logging, and not for high computational cost operations.
|
|
418
1301
|
|
|
1302
|
+
### Complete Middleware Example in Fastay.js
|
|
419
1303
|
|
|
420
|
-
|
|
1304
|
+
#### Directory Structure
|
|
421
1305
|
|
|
422
|
-
|
|
1306
|
+
```
|
|
1307
|
+
src/
|
|
1308
|
+
middlewares/
|
|
1309
|
+
home.ts
|
|
1310
|
+
user.ts
|
|
1311
|
+
middleware.ts
|
|
1312
|
+
```
|
|
423
1313
|
|
|
424
|
-
|
|
1314
|
+
#### middleware.ts
|
|
425
1315
|
|
|
426
|
-
|
|
1316
|
+
```typescript
|
|
1317
|
+
import { createMiddleware } from '@syntay/fastay';
|
|
1318
|
+
import { user } from './user';
|
|
1319
|
+
import { home } from './home';
|
|
427
1320
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
1321
|
+
export const middleware = createMiddleware({
|
|
1322
|
+
'/api/users': [user], // User middleware for /api/users
|
|
1323
|
+
'/api/hello': [home], // Home middleware for /api/hello
|
|
1324
|
+
});
|
|
1325
|
+
```
|
|
433
1326
|
|
|
434
|
-
|
|
1327
|
+
#### user.ts (Authentication Middleware)
|
|
435
1328
|
|
|
436
|
-
|
|
1329
|
+
```typescript
|
|
1330
|
+
import { Next, Request, Response } from '@syntay/fastay';
|
|
437
1331
|
|
|
438
|
-
|
|
1332
|
+
export async function user(request: Request, _response: Response, next: Next) {
|
|
1333
|
+
console.log('User middleware executed');
|
|
1334
|
+
|
|
1335
|
+
// Simulated authentication token validation
|
|
1336
|
+
if (!request.headers['authorization']) {
|
|
1337
|
+
return _response.status(401).json({ error: 'Unauthorized' });
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// If everything is correct, call next middleware or route
|
|
1341
|
+
next();
|
|
1342
|
+
}
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
#### home.ts (Logging Middleware)
|
|
1346
|
+
|
|
1347
|
+
```typescript
|
|
1348
|
+
import { Next, Request, Response } from '@syntay/fastay';
|
|
1349
|
+
|
|
1350
|
+
export async function home(request: Request, _response: Response, next: Next) {
|
|
1351
|
+
console.log('Home middleware executed');
|
|
1352
|
+
// Add logging logic here
|
|
1353
|
+
// Call next middleware or route
|
|
1354
|
+
next();
|
|
1355
|
+
}
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
This example provides a complete explanation of how to configure and use middlewares in Fastay.js, with ready-to-copy code examples to apply in your project.
|
|
1359
|
+
|
|
1360
|
+
[⬆ Back to Top](#fastay-documentation)
|
|
1361
|
+
|
|
1362
|
+
## Comparison with Other Frameworks
|
|
1363
|
+
|
|
1364
|
+
### Pure Express.js
|
|
1365
|
+
|
|
1366
|
+
```typescript
|
|
1367
|
+
import express from 'express';
|
|
1368
|
+
const app = express();
|
|
1369
|
+
|
|
1370
|
+
// GET
|
|
1371
|
+
app.get('/api/hello', (req, res) => {
|
|
1372
|
+
res.json({ message: 'Hello World' });
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
// POST
|
|
1376
|
+
app.post('/api/hello', (req, res) => {
|
|
1377
|
+
res.json({ message: 'Hello POST World' });
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
app.listen(5000, () => console.log('Server running on port 5000'));
|
|
1381
|
+
```
|
|
1382
|
+
|
|
1383
|
+
**Disadvantages of pure Express:**
|
|
1384
|
+
- ❌ Manual registration of each route
|
|
1385
|
+
- ❌ Middleware and routes mixed together
|
|
1386
|
+
- ❌ Complicated scalability in large projects
|
|
1387
|
+
|
|
1388
|
+
### NestJS
|
|
1389
|
+
|
|
1390
|
+
```typescript
|
|
1391
|
+
import { Controller, Get, Post, Body } from '@nestjs/common';
|
|
1392
|
+
|
|
1393
|
+
@Controller('api/hello')
|
|
1394
|
+
export class HelloController {
|
|
1395
|
+
@Get()
|
|
1396
|
+
getHello() {
|
|
1397
|
+
return { message: 'Hello World' };
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
@Post()
|
|
1401
|
+
postHello(@Body() body: any) {
|
|
1402
|
+
return { message: 'Hello POST World', body };
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
```
|
|
1406
|
+
|
|
1407
|
+
**NestJS Characteristics:**
|
|
1408
|
+
- ✅ Based on decorators and classes
|
|
1409
|
+
- ✅ Module organization
|
|
1410
|
+
- ✅ Type-safe and TypeScript
|
|
1411
|
+
- ⚠️ Learning curve with decorators and DI
|
|
439
1412
|
|
|
440
|
-
|
|
1413
|
+
### Fastay.js
|
|
441
1414
|
|
|
1415
|
+
```typescript
|
|
1416
|
+
import { Request } from '@syntay/fastay';
|
|
1417
|
+
|
|
1418
|
+
// GET /api/hello
|
|
1419
|
+
export async function GET() {
|
|
1420
|
+
return { message: 'Hello World' };
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
// POST /api/hello
|
|
1424
|
+
export async function POST(req: Request) {
|
|
1425
|
+
return { message: 'Hello POST World' };
|
|
1426
|
+
}
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
**Fastay Advantages:**
|
|
1430
|
+
- ✅ File-based - each HTTP method is exported
|
|
1431
|
+
- ✅ Auto-discovered routes - no manual registration
|
|
1432
|
+
- ✅ Separate and organized middleware
|
|
1433
|
+
- ✅ Type-safe, clean and simple
|
|
1434
|
+
|
|
1435
|
+
### Request Flow
|
|
1436
|
+
|
|
1437
|
+
```
|
|
1438
|
+
Client → Fastay Route → Middleware → Route Handler → Service → Response
|
|
1439
|
+
```
|
|
1440
|
+
|
|
1441
|
+
## Contribution
|
|
1442
|
+
|
|
1443
|
+
Contributions are welcome! Follow the steps:
|
|
1444
|
+
|
|
1445
|
+
1. Fork the project
|
|
1446
|
+
2. Create a branch (`git checkout -b my-feature`)
|
|
1447
|
+
3. Commit your changes (`git commit -am 'Add new feature'`)
|
|
1448
|
+
4. Push to the branch (`git push origin my-feature`)
|
|
1449
|
+
5. Open a Pull Request
|
|
1450
|
+
|
|
1451
|
+
## License
|
|
1452
|
+
|
|
1453
|
+
MIT © Syntay Team
|
|
442
1454
|
|
|
1455
|
+
[⬆ Back to Top](#fastay-documentation)
|