@ranimontagna/agent-toolkit 0.1.4 → 0.1.5

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.
Files changed (30) hide show
  1. package/README.md +282 -277
  2. package/docs/assets/install-plan.svg +29 -0
  3. package/docs/assets/install-skill-packages.svg +31 -0
  4. package/docs/assets/install-status.svg +32 -0
  5. package/package.json +10 -9
  6. package/setup-agent-toolkit.sh +1 -1
  7. package/skills/backend/fastify-best-practices/LICENSE +21 -0
  8. package/skills/backend/fastify-best-practices/NOTICE.md +11 -0
  9. package/skills/backend/fastify-best-practices/SKILL.md +75 -0
  10. package/skills/backend/fastify-best-practices/rules/authentication.md +521 -0
  11. package/skills/backend/fastify-best-practices/rules/configuration.md +217 -0
  12. package/skills/backend/fastify-best-practices/rules/content-type.md +387 -0
  13. package/skills/backend/fastify-best-practices/rules/cors-security.md +445 -0
  14. package/skills/backend/fastify-best-practices/rules/database.md +320 -0
  15. package/skills/backend/fastify-best-practices/rules/decorators.md +416 -0
  16. package/skills/backend/fastify-best-practices/rules/deployment.md +423 -0
  17. package/skills/backend/fastify-best-practices/rules/error-handling.md +412 -0
  18. package/skills/backend/fastify-best-practices/rules/hooks.md +464 -0
  19. package/skills/backend/fastify-best-practices/rules/http-proxy.md +247 -0
  20. package/skills/backend/fastify-best-practices/rules/logging.md +402 -0
  21. package/skills/backend/fastify-best-practices/rules/performance.md +425 -0
  22. package/skills/backend/fastify-best-practices/rules/plugins.md +320 -0
  23. package/skills/backend/fastify-best-practices/rules/routes.md +467 -0
  24. package/skills/backend/fastify-best-practices/rules/schemas.md +585 -0
  25. package/skills/backend/fastify-best-practices/rules/serialization.md +475 -0
  26. package/skills/backend/fastify-best-practices/rules/testing.md +536 -0
  27. package/skills/backend/fastify-best-practices/rules/typescript.md +458 -0
  28. package/skills/backend/fastify-best-practices/rules/websockets.md +421 -0
  29. package/skills/backend/fastify-best-practices/tile.json +11 -0
  30. package/skills/core/agent-toolkit-maintainer/SKILL.md +16 -14
@@ -0,0 +1,467 @@
1
+ ---
2
+ name: routes
3
+ description: Route organization and handlers in Fastify
4
+ metadata:
5
+ tags: routes, handlers, http, rest, api
6
+ ---
7
+
8
+ # Route Organization and Handlers
9
+
10
+ ## Basic Route Definition
11
+
12
+ Define routes with the shorthand methods or the full route method:
13
+
14
+ ```typescript
15
+ import Fastify from 'fastify';
16
+
17
+ const app = Fastify();
18
+
19
+ // Shorthand methods
20
+ app.get('/users', async (request, reply) => {
21
+ return { users: [] };
22
+ });
23
+
24
+ app.post('/users', async (request, reply) => {
25
+ return { created: true };
26
+ });
27
+
28
+ // Full route method with all options
29
+ app.route({
30
+ method: 'GET',
31
+ url: '/users/:id',
32
+ schema: {
33
+ params: {
34
+ type: 'object',
35
+ properties: {
36
+ id: { type: 'string' },
37
+ },
38
+ required: ['id'],
39
+ },
40
+ },
41
+ handler: async (request, reply) => {
42
+ return { id: request.params.id };
43
+ },
44
+ });
45
+ ```
46
+
47
+ ## Route Parameters
48
+
49
+ Access URL parameters through `request.params`:
50
+
51
+ ```typescript
52
+ // Single parameter
53
+ app.get('/users/:id', async (request) => {
54
+ const { id } = request.params as { id: string };
55
+ return { userId: id };
56
+ });
57
+
58
+ // Multiple parameters
59
+ app.get('/users/:userId/posts/:postId', async (request) => {
60
+ const { userId, postId } = request.params as { userId: string; postId: string };
61
+ return { userId, postId };
62
+ });
63
+
64
+ // Wildcard parameter (captures everything after)
65
+ app.get('/files/*', async (request) => {
66
+ const path = (request.params as { '*': string })['*'];
67
+ return { filePath: path };
68
+ });
69
+
70
+ // Regex parameters (Fastify uses find-my-way)
71
+ app.get('/orders/:id(\\d+)', async (request) => {
72
+ // Only matches numeric IDs
73
+ const { id } = request.params as { id: string };
74
+ return { orderId: parseInt(id, 10) };
75
+ });
76
+ ```
77
+
78
+ ## Query String Parameters
79
+
80
+ Access query parameters through `request.query`:
81
+
82
+ ```typescript
83
+ app.get('/search', {
84
+ schema: {
85
+ querystring: {
86
+ type: 'object',
87
+ properties: {
88
+ q: { type: 'string' },
89
+ page: { type: 'integer', default: 1 },
90
+ limit: { type: 'integer', default: 10, maximum: 100 },
91
+ },
92
+ required: ['q'],
93
+ },
94
+ },
95
+ handler: async (request) => {
96
+ const { q, page, limit } = request.query as {
97
+ q: string;
98
+ page: number;
99
+ limit: number;
100
+ };
101
+ return { query: q, page, limit };
102
+ },
103
+ });
104
+ ```
105
+
106
+ ## Request Body
107
+
108
+ Access the request body through `request.body`:
109
+
110
+ ```typescript
111
+ app.post('/users', {
112
+ schema: {
113
+ body: {
114
+ type: 'object',
115
+ properties: {
116
+ name: { type: 'string', minLength: 1 },
117
+ email: { type: 'string', format: 'email' },
118
+ age: { type: 'integer', minimum: 0 },
119
+ },
120
+ required: ['name', 'email'],
121
+ },
122
+ },
123
+ handler: async (request, reply) => {
124
+ const user = request.body as { name: string; email: string; age?: number };
125
+ // Create user...
126
+ reply.code(201);
127
+ return { user };
128
+ },
129
+ });
130
+ ```
131
+
132
+ ## Headers
133
+
134
+ Access request headers through `request.headers`:
135
+
136
+ ```typescript
137
+ app.get('/protected', {
138
+ schema: {
139
+ headers: {
140
+ type: 'object',
141
+ properties: {
142
+ authorization: { type: 'string' },
143
+ },
144
+ required: ['authorization'],
145
+ },
146
+ },
147
+ handler: async (request) => {
148
+ const token = request.headers.authorization;
149
+ return { authenticated: true };
150
+ },
151
+ });
152
+ ```
153
+
154
+ ## Reply Methods
155
+
156
+ Use reply methods to control the response:
157
+
158
+ ```typescript
159
+ app.get('/examples', async (request, reply) => {
160
+ // Set status code
161
+ reply.code(201);
162
+
163
+ // Set headers
164
+ reply.header('X-Custom-Header', 'value');
165
+ reply.headers({ 'X-Another': 'value', 'X-Third': 'value' });
166
+
167
+ // Set content type
168
+ reply.type('application/json');
169
+
170
+ // Redirect
171
+ // reply.redirect('/other-url');
172
+ // reply.redirect(301, '/permanent-redirect');
173
+
174
+ // Return response (automatic serialization)
175
+ return { status: 'ok' };
176
+ });
177
+
178
+ // Explicit send (useful in non-async handlers)
179
+ app.get('/explicit', (request, reply) => {
180
+ reply.send({ status: 'ok' });
181
+ });
182
+
183
+ // Stream response
184
+ app.get('/stream', async (request, reply) => {
185
+ const stream = fs.createReadStream('./large-file.txt');
186
+ reply.type('text/plain');
187
+ return reply.send(stream);
188
+ });
189
+ ```
190
+
191
+ ## Route Organization by Feature
192
+
193
+ Organize routes by feature/domain in separate files:
194
+
195
+ ```
196
+ src/
197
+ routes/
198
+ users/
199
+ index.ts # Route definitions
200
+ handlers.ts # Handler functions
201
+ schemas.ts # JSON schemas
202
+ posts/
203
+ index.ts
204
+ handlers.ts
205
+ schemas.ts
206
+ ```
207
+
208
+ ```typescript
209
+ // routes/users/schemas.ts
210
+ export const userSchema = {
211
+ type: 'object',
212
+ properties: {
213
+ id: { type: 'string', format: 'uuid' },
214
+ name: { type: 'string' },
215
+ email: { type: 'string', format: 'email' },
216
+ },
217
+ };
218
+
219
+ export const createUserSchema = {
220
+ body: {
221
+ type: 'object',
222
+ properties: {
223
+ name: { type: 'string', minLength: 1 },
224
+ email: { type: 'string', format: 'email' },
225
+ },
226
+ required: ['name', 'email'],
227
+ },
228
+ response: {
229
+ 201: userSchema,
230
+ },
231
+ };
232
+
233
+ // routes/users/handlers.ts
234
+ import type { FastifyRequest, FastifyReply } from 'fastify';
235
+
236
+ export async function createUser(
237
+ request: FastifyRequest<{ Body: { name: string; email: string } }>,
238
+ reply: FastifyReply,
239
+ ) {
240
+ const { name, email } = request.body;
241
+ const user = await request.server.db.users.create({ name, email });
242
+ reply.code(201);
243
+ return user;
244
+ }
245
+
246
+ export async function getUsers(request: FastifyRequest) {
247
+ return request.server.db.users.findAll();
248
+ }
249
+
250
+ // routes/users/index.ts
251
+ import type { FastifyInstance } from 'fastify';
252
+ import { createUser, getUsers } from './handlers.js';
253
+ import { createUserSchema } from './schemas.js';
254
+
255
+ export default async function userRoutes(fastify: FastifyInstance) {
256
+ fastify.get('/', getUsers);
257
+ fastify.post('/', { schema: createUserSchema }, createUser);
258
+ }
259
+ ```
260
+
261
+ ## Route Constraints
262
+
263
+ Add constraints to routes for versioning or host-based routing:
264
+
265
+ ```typescript
266
+ // Version constraint
267
+ app.get('/users', {
268
+ constraints: { version: '1.0.0' },
269
+ handler: async () => ({ version: '1.0.0', users: [] }),
270
+ });
271
+
272
+ app.get('/users', {
273
+ constraints: { version: '2.0.0' },
274
+ handler: async () => ({ version: '2.0.0', data: { users: [] } }),
275
+ });
276
+
277
+ // Client sends: Accept-Version: 1.0.0
278
+
279
+ // Host constraint
280
+ app.get('/', {
281
+ constraints: { host: 'api.example.com' },
282
+ handler: async () => ({ api: true }),
283
+ });
284
+
285
+ app.get('/', {
286
+ constraints: { host: 'www.example.com' },
287
+ handler: async () => ({ web: true }),
288
+ });
289
+ ```
290
+
291
+ ## Route Prefixing
292
+
293
+ Use prefixes to namespace routes:
294
+
295
+ ```typescript
296
+ // Using register
297
+ app.register(async function (fastify) {
298
+ fastify.get('/list', async () => ({ users: [] }));
299
+ fastify.get('/:id', async (request) => ({ id: request.params.id }));
300
+ }, { prefix: '/users' });
301
+
302
+ // Results in:
303
+ // GET /users/list
304
+ // GET /users/:id
305
+ ```
306
+
307
+ ## Multiple Methods
308
+
309
+ Handle multiple HTTP methods with one handler:
310
+
311
+ ```typescript
312
+ app.route({
313
+ method: ['GET', 'HEAD'],
314
+ url: '/resource',
315
+ handler: async (request) => {
316
+ return { data: 'resource' };
317
+ },
318
+ });
319
+ ```
320
+
321
+ ## 404 Handler
322
+
323
+ Customize the not found handler:
324
+
325
+ ```typescript
326
+ app.setNotFoundHandler({
327
+ preValidation: async (request, reply) => {
328
+ // Optional pre-validation hook
329
+ },
330
+ preHandler: async (request, reply) => {
331
+ // Optional pre-handler hook
332
+ },
333
+ }, async (request, reply) => {
334
+ reply.code(404);
335
+ return {
336
+ error: 'Not Found',
337
+ message: `Route ${request.method} ${request.url} not found`,
338
+ statusCode: 404,
339
+ };
340
+ });
341
+ ```
342
+
343
+ ## Method Not Allowed
344
+
345
+ Handle method not allowed responses:
346
+
347
+ ```typescript
348
+ // Fastify doesn't have built-in 405 handling
349
+ // Implement with a custom not found handler that checks allowed methods
350
+ app.setNotFoundHandler(async (request, reply) => {
351
+ // Check if the URL exists with a different method
352
+ const route = app.hasRoute({
353
+ url: request.url,
354
+ method: 'GET', // Check other methods
355
+ });
356
+
357
+ if (route) {
358
+ reply.code(405);
359
+ return { error: 'Method Not Allowed' };
360
+ }
361
+
362
+ reply.code(404);
363
+ return { error: 'Not Found' };
364
+ });
365
+ ```
366
+
367
+ ## Route-Level Configuration
368
+
369
+ Apply configuration to specific routes:
370
+
371
+ ```typescript
372
+ app.get('/slow-operation', {
373
+ config: {
374
+ rateLimit: { max: 10, timeWindow: '1 minute' },
375
+ },
376
+ handler: async (request) => {
377
+ return { result: await slowOperation() };
378
+ },
379
+ });
380
+
381
+ // Access config in hooks
382
+ app.addHook('onRequest', async (request, reply) => {
383
+ const config = request.routeOptions.config;
384
+ if (config.rateLimit) {
385
+ // Apply rate limiting
386
+ }
387
+ });
388
+ ```
389
+
390
+ ## Async Route Registration
391
+
392
+ Register routes from async sources:
393
+
394
+ ```typescript
395
+ app.register(async function (fastify) {
396
+ const routeConfigs = await loadRoutesFromDatabase();
397
+
398
+ for (const config of routeConfigs) {
399
+ fastify.route({
400
+ method: config.method,
401
+ url: config.path,
402
+ handler: createDynamicHandler(config),
403
+ });
404
+ }
405
+ });
406
+ ```
407
+
408
+ ## Auto-loading Routes with @fastify/autoload
409
+
410
+ Use `@fastify/autoload` to automatically load routes from a directory structure:
411
+
412
+ ```typescript
413
+ import Fastify from 'fastify';
414
+ import autoload from '@fastify/autoload';
415
+ import { join } from 'node:path';
416
+
417
+ const app = Fastify({ logger: true });
418
+
419
+ // Auto-load plugins
420
+ app.register(autoload, {
421
+ dir: join(import.meta.dirname, 'plugins'),
422
+ options: { prefix: '' },
423
+ });
424
+
425
+ // Auto-load routes
426
+ app.register(autoload, {
427
+ dir: join(import.meta.dirname, 'routes'),
428
+ options: { prefix: '/api' },
429
+ });
430
+
431
+ await app.listen({ port: 3000 });
432
+ ```
433
+
434
+ Directory structure:
435
+
436
+ ```
437
+ src/
438
+ plugins/
439
+ database.ts # Loaded automatically
440
+ auth.ts # Loaded automatically
441
+ routes/
442
+ users/
443
+ index.ts # GET/POST /api/users
444
+ _id/
445
+ index.ts # GET/PUT/DELETE /api/users/:id
446
+ posts/
447
+ index.ts # GET/POST /api/posts
448
+ ```
449
+
450
+ Route file example:
451
+
452
+ ```typescript
453
+ // routes/users/index.ts
454
+ import type { FastifyPluginAsync } from 'fastify';
455
+
456
+ const users: FastifyPluginAsync = async (fastify) => {
457
+ fastify.get('/', async () => {
458
+ return fastify.repositories.users.findAll();
459
+ });
460
+
461
+ fastify.post('/', async (request) => {
462
+ return fastify.repositories.users.create(request.body);
463
+ });
464
+ };
465
+
466
+ export default users;
467
+ ```