vector-framework 0.9.6 → 0.9.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 CHANGED
@@ -1,8 +1,11 @@
1
1
  # Vector Framework
2
2
 
3
- **The speed of Bun. The developer experience you love.**
3
+ **Blazing fast, secure, and developer-friendly API framework for Bun**
4
4
 
5
- Vector brings the blazing performance of Bun to developers who appreciate the simplicity and elegance of frameworks like Encore. Build production-ready APIs with a familiar, declarative syntax while leveraging Bun's incredible speed.
5
+ - **70,000+ requests/second** - Optimized for extreme performance
6
+ - **Single dependency** - Only itty-router, minimizing security risks
7
+ - **Zero build step** - Native TypeScript execution with Bun
8
+ - **Encore-like DX** - Declarative, type-safe APIs you'll love
6
9
 
7
10
  ## Quick Start
8
11
 
@@ -12,9 +15,9 @@ Vector brings the blazing performance of Bun to developers who appreciate the si
12
15
  bun add vector-framework
13
16
  ```
14
17
 
15
- ### Configuration
18
+ ### 1. Configure Your App
16
19
 
17
- Create a `vector.config.ts` file in your project root:
20
+ Create `vector.config.ts` in your project root:
18
21
 
19
22
  ```typescript
20
23
  // vector.config.ts
@@ -22,507 +25,643 @@ import type { VectorConfigSchema } from "vector-framework";
22
25
 
23
26
  const config: VectorConfigSchema = {
24
27
  // Server configuration
25
- port: 3000, // Server port (default: 3000)
26
- hostname: "localhost", // Server hostname (default: localhost)
27
- routesDir: "./routes", // Routes directory (default: ./routes)
28
- development: process.env.NODE_ENV !== "production", // Development mode
29
- reusePort: true, // Reuse port (default: true)
30
- idleTimeout: 60, // Idle timeout in seconds (default: 60)
28
+ port: 3000,
29
+ hostname: "localhost",
30
+ development: process.env.NODE_ENV !== "production",
31
+ routesDir: "./routes", // Auto-discovers routes here
31
32
 
32
33
  // CORS configuration
33
34
  cors: {
34
- origin: "*", // String, array, or function
35
- credentials: true, // Allow credentials
35
+ origin: "*",
36
+ credentials: true,
36
37
  allowHeaders: ["Content-Type", "Authorization"],
37
38
  allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
38
- exposeHeaders: ["X-Total-Count"],
39
- maxAge: 86400, // Preflight cache duration in seconds
40
39
  },
40
+
41
+ // Authentication handler
42
+ auth: async (request) => {
43
+ const token = request.headers.get("Authorization")?.replace("Bearer ", "");
44
+ if (token === "valid-token") {
45
+ return { id: "user-123", email: "user@example.com" };
46
+ }
47
+ throw new Error("Invalid token");
48
+ },
49
+
50
+ // Optional: Cache handler (Redis example)
51
+ cache: async (key, factory, ttl) => {
52
+ // Your caching logic here
53
+ const cached = await redis.get(key);
54
+ if (cached) return JSON.parse(cached);
55
+
56
+ const value = await factory();
57
+ await redis.setex(key, ttl, JSON.stringify(value));
58
+ return value;
59
+ },
60
+
61
+ // Optional: Middleware
62
+ before: [
63
+ async (request) => {
64
+ console.log(`${request.method} ${request.url}`);
65
+ return request;
66
+ }
67
+ ],
68
+ after: [
69
+ async (response, request) => {
70
+ response.headers.set("X-Response-Time", `${Date.now() - request.startTime}ms`);
71
+ return response;
72
+ }
73
+ ],
41
74
  };
42
75
 
43
76
  export default config;
44
77
  ```
45
78
 
46
- ### Your First API (Encore-style)
79
+ ### 2. Create Your First Route
47
80
 
48
81
  ```typescript
49
82
  // routes/hello.ts
50
83
  import { route } from "vector-framework";
51
84
 
52
- // Public endpoint - clean and declarative
85
+ // Simple public endpoint
53
86
  export const hello = route(
54
87
  {
55
88
  method: "GET",
56
89
  path: "/hello/:name",
90
+ expose: true, // Public endpoint (default: true)
57
91
  },
58
92
  async (req) => {
59
- const { name } = req.params!;
60
- return { message: `Hello ${name}!` };
93
+ return { message: `Hello ${req.params.name}!` };
61
94
  }
62
95
  );
63
96
 
64
- // Protected endpoint - auth built-in
97
+ // Protected endpoint - uses auth from config
65
98
  export const getProfile = route(
66
99
  {
67
100
  method: "GET",
68
101
  path: "/profile",
69
- auth: true, // That's it! Auth handled.
102
+ auth: true, // Requires authentication
103
+ expose: true,
70
104
  },
71
105
  async (req) => {
72
106
  return {
73
- user: req.authUser,
74
- lastLogin: new Date(),
107
+ user: req.authUser, // Typed from your auth handler
108
+ timestamp: new Date(),
75
109
  };
76
110
  }
77
111
  );
78
- ```
79
112
 
80
- ### Start Your Server
81
-
82
- ```typescript
83
- // server.ts
84
- import vector from "vector-framework";
85
-
86
- // Set up auth (once, globally)
87
- vector.protected = async (request) => {
88
- const token = request.headers.get("Authorization")?.replace("Bearer ", "");
89
- // Your auth logic here
90
- if (token === "valid-token") {
91
- return { id: "user-123", email: "user@example.com" };
113
+ // Cached endpoint
114
+ export const getUsers = route(
115
+ {
116
+ method: "GET",
117
+ path: "/users",
118
+ cache: 300, // Cache for 5 minutes
119
+ expose: true,
120
+ },
121
+ async () => {
122
+ // Expensive operation, will be cached
123
+ const users = await db.users.findMany();
124
+ return { users };
92
125
  }
93
- throw new Error("Invalid token");
94
- };
95
-
96
- // Start server - routes auto-discovered from ./routes
97
- vector.serve({ port: 3000 });
126
+ );
98
127
  ```
99
128
 
100
- ### CLI Commands
129
+ ### 3. Start Your Server
101
130
 
102
131
  ```bash
103
- # Run development server
132
+ # Development mode with hot reload
104
133
  bun vector dev
105
134
 
106
- # Run production server
135
+ # Production mode
107
136
  bun vector start
108
137
 
109
- # Build for production
110
- bun vector build
111
-
112
- # Run with custom options
113
- bun vector dev --port 3000 --routes ./api
138
+ # With custom options
139
+ bun vector dev --port 4000 --routes ./api
114
140
  ```
115
141
 
116
- ## Why Vector?
117
-
118
- If you've been looking for Encore-like developer experience with Bun's performance, Vector is your answer. Define your routes declaratively, enjoy automatic type safety, and ship faster than ever.
142
+ That's it! Your API is running at `http://localhost:3000` 🎉
119
143
 
120
- ## Features
144
+ ## TypeScript Type Safety
121
145
 
122
- - **Fast & Lightweight** - Built on Bun and itty-router for maximum performance
123
- - **Type-Safe** - Full TypeScript support with excellent type inference
124
- - **Auto Route Discovery** - Automatically discovers and loads routes from your filesystem
125
- - **Middleware System** - Flexible pre/post request middleware pipeline
126
- - **Built-in Authentication** - Simple but powerful authentication system
127
- - **Response Caching** - Automatic response caching with configurable TTL
128
- - **CORS Support** - Configurable CORS with sensible defaults
129
- - **Developer Experience** - Auto route discovery and CLI tools
146
+ Vector provides full type safety with customizable types. Define your types in the config and use them in routes:
130
147
 
131
- ## Familiar Patterns, Modern Performance
148
+ ```typescript
149
+ // vector.config.ts
150
+ import type { VectorConfigSchema, VectorTypes } from "vector-framework";
132
151
 
133
- ### Real-World Example: User Service
152
+ // Define your custom user type
153
+ interface MyUser {
154
+ id: string;
155
+ email: string;
156
+ role: "admin" | "user";
157
+ permissions: string[];
158
+ }
134
159
 
135
- ```typescript
136
- // routes/users.ts
137
- import { route } from "vector-framework";
138
- import { db } from "../db";
160
+ // Extend Vector types
161
+ interface MyAppTypes extends VectorTypes {
162
+ auth: MyUser;
163
+ }
139
164
 
140
- // Public endpoint with caching
141
- export const listUsers = route(
142
- {
143
- method: "GET",
144
- path: "/users",
145
- cache: 300, // Cache for 5 minutes
146
- },
147
- async () => {
148
- const users = await db.user.findMany();
149
- return { users };
150
- }
151
- );
165
+ // Use in config with type parameter
166
+ const config: VectorConfigSchema<MyAppTypes> = {
167
+ port: 3000,
152
168
 
153
- // Protected endpoint with automatic body parsing
154
- export const createUser = route(
155
- {
156
- method: "POST",
157
- path: "/users",
158
- auth: true, // Auth required
169
+ // Auth handler returns your custom type
170
+ auth: async (request): Promise<MyUser> => {
171
+ // Your auth logic
172
+ return {
173
+ id: "user-123",
174
+ email: "user@example.com",
175
+ role: "admin",
176
+ permissions: ["read", "write"],
177
+ };
159
178
  },
160
- async (req) => {
161
- const { name, email } = req.content; // Type-safe, auto-parsed
179
+ };
162
180
 
163
- const user = await db.user.create({
164
- data: { name, email },
165
- });
181
+ export default config;
182
+ export type { MyAppTypes }; // Export for use in routes
183
+ ```
166
184
 
167
- return { user };
168
- }
169
- );
185
+ ```typescript
186
+ // routes/admin.ts
187
+ import { route, APIError } from "vector-framework";
188
+ import type { MyAppTypes } from "../vector.config";
170
189
 
171
- // Parameter extraction made simple
172
- export const getUser = route(
190
+ // Use type parameter to get fully typed request
191
+ export const adminOnly = route<MyAppTypes>(
173
192
  {
174
193
  method: "GET",
175
- path: "/users/:id",
194
+ path: "/admin/data",
195
+ auth: true,
196
+ expose: true,
176
197
  },
177
198
  async (req) => {
178
- const { id } = req.params!;
179
- const user = await db.user.findUnique({ where: { id } });
180
-
181
- if (!user) {
182
- throw new APIError("User not found", 404);
199
+ // req.authUser is now typed as MyUser
200
+ if (req.authUser?.role !== "admin") {
201
+ throw APIError.forbidden("Admin access required");
183
202
  }
184
203
 
185
- return { user };
204
+ // TypeScript knows these properties exist
205
+ return {
206
+ user: req.authUser.email,
207
+ permissions: req.authUser.permissions,
208
+ };
186
209
  }
187
210
  );
188
211
  ```
189
212
 
190
- ## Why Choose Vector?
213
+ ## Core Features
191
214
 
192
- ### 🚀 Bun-Powered Performance
215
+ ### Route Options
193
216
 
194
- - **Native TypeScript** execution without transpilation overhead
195
- - **Significantly faster** startup times and request handling
196
- - **Minimal memory footprint** thanks to Bun's efficient runtime
197
-
198
- ### 💡 Developer Experience
199
-
200
- - **Encore-inspired API** - If you know Encore, you already know Vector
201
- - **Auto route discovery** - Just write your routes, we'll find them
202
- - **Type safety everywhere** - Full TypeScript support
203
- - **Built-in essentials** - Auth, caching, CORS, middleware - all included
217
+ ```typescript
218
+ interface RouteOptions {
219
+ method: string; // HTTP method (GET, POST, etc.)
220
+ path: string; // Route path with params (/users/:id)
221
+ expose?: boolean; // Make route accessible (default: true)
222
+ auth?: boolean; // Require authentication
223
+ cache?: number | { // Cache configuration
224
+ ttl: number; // Time to live in seconds
225
+ key?: string; // Custom cache key
226
+ };
227
+ rawRequest?: boolean; // Skip body parsing
228
+ rawResponse?: boolean; // Return raw response
229
+ responseContentType?: string; // Response content type
230
+ }
231
+ ```
204
232
 
205
- ### 🔄 Easy Migration
233
+ ### Request Object
206
234
 
207
- Moving from Express, Fastify, or Encore? Vector makes it simple:
235
+ Every route handler receives a typed request object:
208
236
 
209
237
  ```typescript
210
- // Encore-style (what you know)
211
- export const get = api(
212
- { expose: true, method: "GET", path: "/hello/:name" },
213
- async ({ name }: { name: string }): Promise<Response> => {
214
- const msg = `Hello ${name}!`;
215
- return { message: msg };
216
- }
217
- );
218
-
219
- // Vector-style (what you write)
220
- export const hello = route(
221
- { expose: true, method: "GET", path: "/hello/:name" },
238
+ export const example = route(
239
+ { method: "POST", path: "/example/:id" },
222
240
  async (req) => {
223
- return { message: `Hello ${req.params.name}!` };
241
+ // All available request properties:
242
+ req.params.id; // URL parameters
243
+ req.query.search; // Query parameters
244
+ req.headers; // Request headers
245
+ req.cookies; // Parsed cookies
246
+ req.content; // Parsed body (JSON/form data)
247
+ req.authUser; // Authenticated user (when auth: true)
248
+ req.context; // Request context
249
+ req.metadata; // Route metadata
224
250
  }
225
251
  );
226
252
  ```
227
253
 
228
- ## For Encore Users
229
-
230
- Switching from Encore? You'll feel right at home. Vector provides the same declarative, type-safe API design with the performance benefits of Bun:
254
+ ### Error Handling
231
255
 
232
- | Encore | Vector |
233
- | ---------------------- | -------------------------------- |
234
- | `api.requireAuth()` | `{ auth: true }` in route config |
235
- | Auto-generated clients | Not yet available |
236
- | Built-in tracing | Middleware support |
237
- | Cloud deployment | Deploy anywhere Bun runs |
238
-
239
- **The key difference:** Vector runs on Bun, giving you significantly better performance and lower resource usage while maintaining the developer experience you love.
240
-
241
- ## Route Options
256
+ Vector provides comprehensive error responses:
242
257
 
243
258
  ```typescript
244
- interface RouteOptions {
245
- method: string; // HTTP method (GET, POST, etc.)
246
- path: string; // Route path with params (/users/:id)
247
- expose?: boolean; // Make route accessible (default: true)
248
- auth?: boolean; // Require authentication (default: false)
249
- cache?:
250
- | number
251
- | {
252
- // Cache configuration
253
- ttl: number; // Time to live in seconds
254
- key?: string; // Custom cache key
255
- };
256
- rawRequest?: boolean; // Skip body parsing (default: false)
257
- rawResponse?: boolean; // Return raw response (default: false)
258
- responseContentType?: string; // Response content type
259
- }
260
- ```
259
+ import { APIError } from "vector-framework";
261
260
 
262
- ## Middleware
261
+ export const example = route(
262
+ { method: "GET", path: "/data/:id" },
263
+ async (req) => {
264
+ // Client errors (4xx)
265
+ if (!req.params.id) {
266
+ throw APIError.badRequest("ID is required");
267
+ }
263
268
 
264
- ### Before Middleware (Pre-handlers)
269
+ const data = await findData(req.params.id);
270
+ if (!data) {
271
+ throw APIError.notFound("Data not found");
272
+ }
265
273
 
266
- ```typescript
267
- const authMiddleware = async (request) => {
268
- // Modify request or return Response to short-circuit
269
- request.customData = "value";
270
- return request;
271
- };
274
+ if (!canAccess(req.authUser, data)) {
275
+ throw APIError.forbidden("Access denied");
276
+ }
272
277
 
273
- const rateLimitMiddleware = async (request) => {
274
- // Return Response to stop processing
275
- if (tooManyRequests) {
276
- return new Response("Too Many Requests", { status: 429 });
278
+ // Rate limiting
279
+ if (await isRateLimited(req)) {
280
+ throw APIError.tooManyRequests("Please wait before trying again");
281
+ }
282
+
283
+ // Server errors (5xx)
284
+ try {
285
+ return await processData(data);
286
+ } catch (error) {
287
+ throw APIError.internalServerError("Processing failed");
288
+ }
277
289
  }
278
- return request;
279
- };
290
+ );
280
291
  ```
281
292
 
282
- ### Finally Middleware (Post-handlers)
293
+ ## Configuration Reference
294
+
295
+ ### VectorConfigSchema
283
296
 
284
297
  ```typescript
285
- const corsMiddleware = async (response, request) => {
286
- // Modify response headers
287
- response.headers.set("X-Custom-Header", "value");
288
- return response;
289
- };
298
+ interface VectorConfigSchema {
299
+ // Server
300
+ port?: number; // Server port (default: 3000)
301
+ hostname?: string; // Server hostname (default: localhost)
302
+ reusePort?: boolean; // Reuse port (default: true)
303
+ development?: boolean; // Development mode
304
+ routesDir?: string; // Routes directory (default: ./routes)
305
+ idleTimeout?: number; // Idle timeout in seconds
306
+
307
+ // CORS
308
+ cors?: CorsOptions | boolean;
309
+
310
+ // Handlers
311
+ auth?: ProtectedHandler; // Authentication handler
312
+ cache?: CacheHandler; // Cache handler
313
+
314
+ // Middleware
315
+ before?: BeforeMiddleware[]; // Pre-request middleware
316
+ after?: AfterMiddleware[]; // Post-response middleware
317
+ }
290
318
  ```
291
319
 
292
- ## Authentication
293
-
294
- Implement your authentication logic:
320
+ ### Example: Full Configuration
295
321
 
296
322
  ```typescript
297
- vector.protected = async (request) => {
298
- const authHeader = request.headers.get("Authorization");
299
-
300
- if (!authHeader?.startsWith("Bearer ")) {
301
- throw new Error("Invalid authorization header");
302
- }
323
+ // vector.config.ts
324
+ import type { VectorConfigSchema } from "vector-framework";
325
+ import { verifyJWT } from "./lib/auth";
326
+ import { redis } from "./lib/redis";
303
327
 
304
- const token = authHeader.substring(7);
305
- const user = await validateToken(token);
328
+ const config: VectorConfigSchema = {
329
+ port: process.env.PORT || 3000,
330
+ hostname: "0.0.0.0",
331
+ development: process.env.NODE_ENV !== "production",
332
+ routesDir: "./api/routes",
333
+ idleTimeout: 60,
306
334
 
307
- if (!user) {
308
- throw new Error("Invalid token");
309
- }
335
+ cors: {
336
+ origin: ["https://example.com", "https://app.example.com"],
337
+ credentials: true,
338
+ allowHeaders: ["Content-Type", "Authorization", "X-Request-ID"],
339
+ allowMethods: ["GET", "POST", "PUT", "DELETE"],
340
+ maxAge: 86400,
341
+ },
310
342
 
311
- return user; // This will be available as req.authUser
312
- };
313
- ```
343
+ auth: async (request) => {
344
+ const token = request.headers.get("Authorization")?.replace("Bearer ", "");
345
+ if (!token) throw new Error("No token provided");
314
346
 
315
- ## Caching
347
+ const user = await verifyJWT(token);
348
+ if (!user) throw new Error("Invalid token");
316
349
 
317
- Implement your caching strategy:
350
+ return user;
351
+ },
318
352
 
319
- ```typescript
320
- vector.cache = async (key, factory, ttl) => {
321
- // Example with Redis
322
- const cached = await redis.get(key);
323
- if (cached) return JSON.parse(cached);
353
+ cache: async (key, factory, ttl) => {
354
+ const cached = await redis.get(key);
355
+ if (cached) return JSON.parse(cached);
324
356
 
325
- const value = await factory();
326
- await redis.setex(key, ttl, JSON.stringify(value));
357
+ const value = await factory();
358
+ await redis.setex(key, ttl, JSON.stringify(value));
359
+ return value;
360
+ },
327
361
 
328
- return value;
362
+ before: [
363
+ // Logging middleware
364
+ async (request) => {
365
+ request.startTime = Date.now();
366
+ console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`);
367
+ return request;
368
+ },
369
+
370
+ // Request ID middleware
371
+ async (request) => {
372
+ request.id = crypto.randomUUID();
373
+ return request;
374
+ },
375
+ ],
376
+
377
+ after: [
378
+ // Response time header
379
+ async (response, request) => {
380
+ const duration = Date.now() - request.startTime;
381
+ response.headers.set("X-Response-Time", `${duration}ms`);
382
+ return response;
383
+ },
384
+
385
+ // Security headers
386
+ async (response) => {
387
+ response.headers.set("X-Content-Type-Options", "nosniff");
388
+ response.headers.set("X-Frame-Options", "DENY");
389
+ return response;
390
+ },
391
+ ],
329
392
  };
393
+
394
+ export default config;
330
395
  ```
331
396
 
332
- ## Configuration
397
+ ## CLI Commands
333
398
 
334
- ```typescript
335
- interface VectorConfig {
336
- port?: number; // Server port (default: 3000)
337
- hostname?: string; // Server hostname (default: localhost)
338
- reusePort?: boolean; // Reuse port (default: true)
339
- development?: boolean; // Development mode
340
- routesDir?: string; // Routes directory (default: ./routes)
341
- autoDiscover?: boolean; // Auto-discover routes (default: true)
342
- idleTimeout?: number; // Idle timeout in seconds (default: 60)
343
- cors?: CorsOptions; // CORS configuration
344
- before?: BeforeMiddlewareHandler[]; // Pre-request middleware
345
- finally?: AfterMiddlewareHandler[]; // Post-response middleware
346
- }
399
+ ```bash
400
+ # Development server with hot reload
401
+ bun vector dev
347
402
 
348
- interface CorsOptions {
349
- origin?: string | string[] | ((origin: string) => boolean);
350
- credentials?: boolean;
351
- allowHeaders?: string | string[];
352
- allowMethods?: string | string[];
353
- exposeHeaders?: string | string[];
354
- maxAge?: number;
355
- }
356
- ```
403
+ # Production server
404
+ bun vector start
357
405
 
358
- ### Middleware Configuration
406
+ # Build for production
407
+ bun vector build
359
408
 
360
- ```typescript
361
- // Add before middleware (runs before routes)
362
- vector.before(async (request) => {
363
- console.log(`${request.method} ${request.url}`);
364
- return request;
365
- });
366
-
367
- // Add finally middleware (runs after routes)
368
- vector.finally(async (response, request) => {
369
- response.headers.set("X-Response-Time", Date.now() - request.startTime);
370
- return response;
371
- });
409
+ # Command options
410
+ bun vector dev --port 4000 # Custom port
411
+ bun vector dev --host 0.0.0.0 # Custom host
412
+ bun vector dev --routes ./api # Custom routes directory
413
+ bun vector dev --config ./custom.config.ts # Custom config file
372
414
  ```
373
415
 
374
416
  ## Project Structure
375
417
 
376
418
  ```
377
419
  my-app/
378
- ├── routes/ # Auto-discovered routes
379
- ├── users.ts
380
- │ ├── posts.ts
381
- └── health.ts
382
- ├── middleware/ # Custom middleware
420
+ ├── vector.config.ts # Framework configuration
421
+ ├── routes/ # Auto-discovered routes
422
+ │ ├── users.ts # /users endpoints
423
+ ├── posts.ts # /posts endpoints
424
+ │ └── admin/ # Nested routes
425
+ │ └── stats.ts # /admin/stats endpoints
426
+ ├── lib/ # Your libraries
383
427
  │ ├── auth.ts
384
- └── logging.ts
385
- ├── server.ts # Main server file
428
+ ├── db.ts
429
+ │ └── redis.ts
386
430
  └── package.json
387
431
  ```
388
432
 
389
- ## TypeScript Support
433
+ ## Performance
390
434
 
391
- Vector is written in TypeScript and provides full type safety with customizable types:
435
+ Vector achieves exceptional performance through:
392
436
 
393
- ```typescript
394
- import { createVector, route, APIError } from "vector-framework";
395
- import type { VectorRequest, VectorTypes } from "vector-framework";
437
+ - **Bun Runtime**: Native TypeScript execution without transpilation
438
+ - **Minimal Dependencies**: Only itty-router (3KB) as dependency
439
+ - **Optimized Routing**: Efficient regex-based route matching
440
+ - **Smart Caching**: Built-in response caching with configurable TTL
396
441
 
397
- // Define your custom user type
398
- interface MyUser {
399
- id: string;
400
- email: string;
401
- role: "admin" | "user";
402
- permissions: string[];
403
- }
442
+ Benchmarks show Vector handling **70,000+ requests/second** on standard hardware.
404
443
 
405
- // Extend Vector types
406
- interface MyAppTypes extends VectorTypes {
407
- auth: MyUser;
408
- }
444
+ ## Why Vector?
409
445
 
410
- // Create typed Vector instance
411
- const vector = createVector<MyAppTypes>();
412
-
413
- // Configure authentication with your custom type
414
- vector.protected = async (request): Promise<MyUser> => {
415
- // Your auth logic here
416
- return {
417
- id: "user-123",
418
- email: "user@example.com",
419
- role: "admin",
420
- permissions: ["read", "write"],
421
- };
422
- };
446
+ ### For Encore Users
447
+ Love Encore's declarative API design but need more flexibility? Vector provides the same developer experience with the freedom to deploy anywhere Bun runs.
423
448
 
424
- // Routes automatically have typed authUser
425
- vector.route(
426
- { method: "GET", path: "/admin", expose: true, auth: true },
427
- async (request) => {
428
- // request.authUser is typed as MyUser
429
- if (request.authUser?.role !== "admin") {
430
- throw APIError.forbidden("Admin access required");
431
- }
432
- return { adminData: "..." };
433
- }
434
- );
435
- ```
449
+ ### For Express/Fastify Users
450
+ Tired of middleware chains and verbose configurations? Vector's declarative approach makes APIs cleaner and more maintainable.
436
451
 
437
- ## Error Handling
452
+ ### For New Projects
453
+ Starting fresh? Vector gives you production-ready features from day one with minimal configuration.
438
454
 
439
- Vector provides comprehensive built-in error responses for all HTTP status codes:
455
+ ## Error Reference
440
456
 
441
- ### Common Client Errors (4xx)
457
+ Vector provides comprehensive error responses for all HTTP status codes. All errors return a consistent format:
458
+
459
+ ```json
460
+ {
461
+ "error": true,
462
+ "message": "Error message",
463
+ "statusCode": 400,
464
+ "timestamp": "2025-01-01T00:00:00.000Z"
465
+ }
466
+ ```
467
+
468
+ ### Client Errors (4xx)
442
469
 
443
470
  ```typescript
444
471
  import { APIError } from "vector-framework";
445
472
 
446
- // Basic errors
447
- APIError.badRequest("Invalid input"); // 400
448
- APIError.unauthorized("Please login"); // 401
449
- APIError.forbidden("Access denied"); // 403
450
- APIError.notFound("Resource not found"); // 404
451
- APIError.conflict("Resource already exists"); // 409
452
-
453
- // Validation and input errors
454
- APIError.unprocessableEntity("Invalid data"); // 422
455
- APIError.invalidArgument("Field required"); // 422 (alias)
456
- APIError.payloadTooLarge("File too large"); // 413
457
- APIError.unsupportedMediaType("Invalid type"); // 415
458
-
459
- // Rate limiting and timeouts
460
- APIError.tooManyRequests("Rate limit exceeded"); // 429
461
- APIError.rateLimitExceeded("Try again later"); // 429 (alias)
462
- APIError.requestTimeout("Request took too long"); // 408
463
-
464
- // Method and protocol errors
465
- APIError.methodNotAllowed("POST not allowed"); // 405
466
- APIError.notAcceptable("Cannot produce response"); // 406
467
- APIError.preconditionFailed("ETag mismatch"); // 412
473
+ // 400 Bad Request
474
+ APIError.badRequest("Invalid input data");
475
+
476
+ // 401 Unauthorized
477
+ APIError.unauthorized("Authentication required");
478
+
479
+ // 402 Payment Required
480
+ APIError.paymentRequired("Subscription expired");
481
+
482
+ // 403 Forbidden
483
+ APIError.forbidden("Access denied");
484
+
485
+ // 404 Not Found
486
+ APIError.notFound("Resource not found");
487
+
488
+ // 405 Method Not Allowed
489
+ APIError.methodNotAllowed("POST not allowed on this endpoint");
490
+
491
+ // 406 Not Acceptable
492
+ APIError.notAcceptable("Cannot produce requested content type");
493
+
494
+ // 408 Request Timeout
495
+ APIError.requestTimeout("Request took too long");
496
+
497
+ // 409 Conflict
498
+ APIError.conflict("Resource already exists");
499
+
500
+ // 410 Gone
501
+ APIError.gone("Resource permanently deleted");
502
+
503
+ // 411 Length Required
504
+ APIError.lengthRequired("Content-Length header required");
505
+
506
+ // 412 Precondition Failed
507
+ APIError.preconditionFailed("ETag mismatch");
508
+
509
+ // 413 Payload Too Large
510
+ APIError.payloadTooLarge("Request body exceeds limit");
511
+
512
+ // 414 URI Too Long
513
+ APIError.uriTooLong("URL exceeds maximum length");
514
+
515
+ // 415 Unsupported Media Type
516
+ APIError.unsupportedMediaType("Content-Type not supported");
517
+
518
+ // 416 Range Not Satisfiable
519
+ APIError.rangeNotSatisfiable("Requested range cannot be satisfied");
520
+
521
+ // 417 Expectation Failed
522
+ APIError.expectationFailed("Expect header requirements not met");
523
+
524
+ // 418 I'm a Teapot
525
+ APIError.imATeapot("I refuse to brew coffee");
526
+
527
+ // 421 Misdirected Request
528
+ APIError.misdirectedRequest("Request sent to wrong server");
529
+
530
+ // 422 Unprocessable Entity
531
+ APIError.unprocessableEntity("Validation failed");
532
+
533
+ // 423 Locked
534
+ APIError.locked("Resource is locked");
535
+
536
+ // 424 Failed Dependency
537
+ APIError.failedDependency("Dependent request failed");
538
+
539
+ // 425 Too Early
540
+ APIError.tooEarly("Request is too early");
541
+
542
+ // 426 Upgrade Required
543
+ APIError.upgradeRequired("Protocol upgrade required");
544
+
545
+ // 428 Precondition Required
546
+ APIError.preconditionRequired("Precondition headers required");
547
+
548
+ // 429 Too Many Requests
549
+ APIError.tooManyRequests("Rate limit exceeded");
550
+
551
+ // 431 Request Header Fields Too Large
552
+ APIError.requestHeaderFieldsTooLarge("Headers too large");
553
+
554
+ // 451 Unavailable For Legal Reasons
555
+ APIError.unavailableForLegalReasons("Content blocked for legal reasons");
468
556
  ```
469
557
 
470
558
  ### Server Errors (5xx)
471
559
 
472
560
  ```typescript
473
- // Server errors
474
- APIError.internalServerError("Something went wrong"); // 500
475
- APIError.notImplemented("Feature coming soon"); // 501
476
- APIError.badGateway("Upstream server error"); // 502
477
- APIError.serviceUnavailable("Service down"); // 503
478
- APIError.maintenance("Under maintenance"); // 503 (alias)
479
- APIError.gatewayTimeout("Upstream timeout"); // 504
561
+ // 500 Internal Server Error
562
+ APIError.internalServerError("Something went wrong");
563
+
564
+ // 501 Not Implemented
565
+ APIError.notImplemented("Feature not yet available");
566
+
567
+ // 502 Bad Gateway
568
+ APIError.badGateway("Upstream server error");
569
+
570
+ // 503 Service Unavailable
571
+ APIError.serviceUnavailable("Service temporarily down");
572
+
573
+ // 504 Gateway Timeout
574
+ APIError.gatewayTimeout("Upstream server timeout");
575
+
576
+ // 505 HTTP Version Not Supported
577
+ APIError.httpVersionNotSupported("HTTP/3 not supported");
578
+
579
+ // 506 Variant Also Negotiates
580
+ APIError.variantAlsoNegotiates("Content negotiation error");
581
+
582
+ // 507 Insufficient Storage
583
+ APIError.insufficientStorage("Server storage full");
584
+
585
+ // 508 Loop Detected
586
+ APIError.loopDetected("Infinite loop detected");
587
+
588
+ // 510 Not Extended
589
+ APIError.notExtended("Extension required");
590
+
591
+ // 511 Network Authentication Required
592
+ APIError.networkAuthenticationRequired("Network login required");
593
+ ```
594
+
595
+ ### Convenience Aliases
596
+
597
+ ```typescript
598
+ // Alias for 422 Unprocessable Entity
599
+ APIError.invalidArgument("Field 'email' is required");
600
+
601
+ // Alias for 429 Too Many Requests
602
+ APIError.rateLimitExceeded("Try again in 60 seconds");
603
+
604
+ // Alias for 503 Service Unavailable
605
+ APIError.maintenance("Scheduled maintenance in progress");
480
606
  ```
481
607
 
482
608
  ### Custom Errors
483
609
 
484
610
  ```typescript
485
- // Create custom error with any status code
486
- APIError.custom(456, 'Custom error message');
611
+ // Create error with any status code
612
+ APIError.custom(456, "Custom error message");
487
613
 
488
- // All errors include additional metadata
489
- // Response format:
490
- {
491
- "error": true,
492
- "message": "Error message",
493
- "statusCode": 400,
494
- "timestamp": "2025-01-01T00:00:00.000Z"
495
- }
614
+ // With custom content type
615
+ APIError.custom(400, "Invalid XML", "application/xml");
496
616
  ```
497
617
 
498
618
  ### Usage in Routes
499
619
 
500
620
  ```typescript
501
- vector.route(
502
- { method: "GET", path: "/api/data/:id", expose: true },
621
+ export const example = route(
622
+ { method: "POST", path: "/api/users" },
503
623
  async (req) => {
504
- // Validation
505
- if (!req.params?.id) {
506
- throw APIError.badRequest("ID is required");
624
+ // Validation errors
625
+ if (!req.content?.email) {
626
+ throw APIError.badRequest("Email is required");
507
627
  }
508
628
 
509
- // Check rate limits
510
- if (await isRateLimited(req)) {
511
- throw APIError.tooManyRequests("Please wait before trying again");
629
+ if (!isValidEmail(req.content.email)) {
630
+ throw APIError.unprocessableEntity("Invalid email format");
512
631
  }
513
632
 
514
- // Fetch data
515
- const data = await fetchData(req.params.id);
516
- if (!data) {
517
- throw APIError.notFound("Data not found");
633
+ // Authentication errors
634
+ if (!req.authUser) {
635
+ throw APIError.unauthorized("Please login first");
518
636
  }
519
637
 
520
- // Check permissions
521
- if (!canAccess(req.authUser, data)) {
522
- throw APIError.forbidden("You cannot access this resource");
638
+ if (req.authUser.role !== "admin") {
639
+ throw APIError.forbidden("Admin access required");
640
+ }
641
+
642
+ // Resource errors
643
+ const existingUser = await findUserByEmail(req.content.email);
644
+ if (existingUser) {
645
+ throw APIError.conflict("Email already registered");
523
646
  }
524
647
 
525
- return data;
648
+ // Rate limiting
649
+ if (await checkRateLimit(req.authUser.id)) {
650
+ throw APIError.tooManyRequests("Maximum 5 users per hour");
651
+ }
652
+
653
+ try {
654
+ const user = await createUser(req.content);
655
+ return { user };
656
+ } catch (error) {
657
+ // Database errors
658
+ if (error.code === "STORAGE_FULL") {
659
+ throw APIError.insufficientStorage("Database full");
660
+ }
661
+
662
+ // Generic server error
663
+ throw APIError.internalServerError("Failed to create user");
664
+ }
526
665
  }
527
666
  );
528
667
  ```