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