@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,320 @@
1
+ ---
2
+ name: database
3
+ description: Database integration with Fastify using official adapters
4
+ metadata:
5
+ tags: database, postgres, mysql, mongodb, redis, sql
6
+ ---
7
+
8
+ # Database Integration
9
+
10
+ ## Use Official Fastify Database Adapters
11
+
12
+ Always use the official Fastify database plugins from the `@fastify` organization. They provide proper connection pooling, encapsulation, and integration with Fastify's lifecycle.
13
+
14
+ ## PostgreSQL with @fastify/postgres
15
+
16
+ ```typescript
17
+ import Fastify from 'fastify';
18
+ import fastifyPostgres from '@fastify/postgres';
19
+
20
+ const app = Fastify({ logger: true });
21
+
22
+ app.register(fastifyPostgres, {
23
+ connectionString: process.env.DATABASE_URL,
24
+ });
25
+
26
+ // Use in routes
27
+ app.get('/users', async (request) => {
28
+ const client = await app.pg.connect();
29
+ try {
30
+ const { rows } = await client.query('SELECT * FROM users');
31
+ return rows;
32
+ } finally {
33
+ client.release();
34
+ }
35
+ });
36
+
37
+ // Or use the pool directly for simple queries
38
+ app.get('/users/:id', async (request) => {
39
+ const { id } = request.params;
40
+ const { rows } = await app.pg.query(
41
+ 'SELECT * FROM users WHERE id = $1',
42
+ [id],
43
+ );
44
+ return rows[0];
45
+ });
46
+
47
+ // Transactions
48
+ app.post('/transfer', async (request) => {
49
+ const { fromId, toId, amount } = request.body;
50
+ const client = await app.pg.connect();
51
+
52
+ try {
53
+ await client.query('BEGIN');
54
+ await client.query(
55
+ 'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
56
+ [amount, fromId],
57
+ );
58
+ await client.query(
59
+ 'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
60
+ [amount, toId],
61
+ );
62
+ await client.query('COMMIT');
63
+ return { success: true };
64
+ } catch (error) {
65
+ await client.query('ROLLBACK');
66
+ throw error;
67
+ } finally {
68
+ client.release();
69
+ }
70
+ });
71
+ ```
72
+
73
+ ## MySQL with @fastify/mysql
74
+
75
+ ```typescript
76
+ import Fastify from 'fastify';
77
+ import fastifyMysql from '@fastify/mysql';
78
+
79
+ const app = Fastify({ logger: true });
80
+
81
+ app.register(fastifyMysql, {
82
+ promise: true,
83
+ connectionString: process.env.MYSQL_URL,
84
+ });
85
+
86
+ app.get('/users', async (request) => {
87
+ const connection = await app.mysql.getConnection();
88
+ try {
89
+ const [rows] = await connection.query('SELECT * FROM users');
90
+ return rows;
91
+ } finally {
92
+ connection.release();
93
+ }
94
+ });
95
+ ```
96
+
97
+ ## MongoDB with @fastify/mongodb
98
+
99
+ ```typescript
100
+ import Fastify from 'fastify';
101
+ import fastifyMongo from '@fastify/mongodb';
102
+
103
+ const app = Fastify({ logger: true });
104
+
105
+ app.register(fastifyMongo, {
106
+ url: process.env.MONGODB_URL,
107
+ });
108
+
109
+ app.get('/users', async (request) => {
110
+ const users = await app.mongo.db
111
+ .collection('users')
112
+ .find({})
113
+ .toArray();
114
+ return users;
115
+ });
116
+
117
+ app.get('/users/:id', async (request) => {
118
+ const { id } = request.params;
119
+ const user = await app.mongo.db
120
+ .collection('users')
121
+ .findOne({ _id: new app.mongo.ObjectId(id) });
122
+ return user;
123
+ });
124
+
125
+ app.post('/users', async (request) => {
126
+ const result = await app.mongo.db
127
+ .collection('users')
128
+ .insertOne(request.body);
129
+ return { id: result.insertedId };
130
+ });
131
+ ```
132
+
133
+ ## Redis with @fastify/redis
134
+
135
+ ```typescript
136
+ import Fastify from 'fastify';
137
+ import fastifyRedis from '@fastify/redis';
138
+
139
+ const app = Fastify({ logger: true });
140
+
141
+ app.register(fastifyRedis, {
142
+ url: process.env.REDIS_URL,
143
+ });
144
+
145
+ // Caching example
146
+ app.get('/data/:key', async (request) => {
147
+ const { key } = request.params;
148
+
149
+ // Try cache first
150
+ const cached = await app.redis.get(`cache:${key}`);
151
+ if (cached) {
152
+ return JSON.parse(cached);
153
+ }
154
+
155
+ // Fetch from database
156
+ const data = await fetchFromDatabase(key);
157
+
158
+ // Cache for 5 minutes
159
+ await app.redis.setex(`cache:${key}`, 300, JSON.stringify(data));
160
+
161
+ return data;
162
+ });
163
+ ```
164
+
165
+ ## Database as Plugin
166
+
167
+ Encapsulate database access in a plugin:
168
+
169
+ ```typescript
170
+ // plugins/database.ts
171
+ import fp from 'fastify-plugin';
172
+ import fastifyPostgres from '@fastify/postgres';
173
+
174
+ export default fp(async function databasePlugin(fastify) {
175
+ await fastify.register(fastifyPostgres, {
176
+ connectionString: fastify.config.DATABASE_URL,
177
+ });
178
+
179
+ // Add health check
180
+ fastify.decorate('checkDatabaseHealth', async () => {
181
+ try {
182
+ await fastify.pg.query('SELECT 1');
183
+ return true;
184
+ } catch {
185
+ return false;
186
+ }
187
+ });
188
+ }, {
189
+ name: 'database',
190
+ dependencies: ['config'],
191
+ });
192
+ ```
193
+
194
+ ## Repository Pattern
195
+
196
+ Abstract database access with repositories:
197
+
198
+ ```typescript
199
+ // repositories/user.repository.ts
200
+ import type { FastifyInstance } from 'fastify';
201
+
202
+ export interface User {
203
+ id: string;
204
+ email: string;
205
+ name: string;
206
+ }
207
+
208
+ export function createUserRepository(app: FastifyInstance) {
209
+ return {
210
+ async findById(id: string): Promise<User | null> {
211
+ const { rows } = await app.pg.query(
212
+ 'SELECT * FROM users WHERE id = $1',
213
+ [id],
214
+ );
215
+ return rows[0] || null;
216
+ },
217
+
218
+ async findByEmail(email: string): Promise<User | null> {
219
+ const { rows } = await app.pg.query(
220
+ 'SELECT * FROM users WHERE email = $1',
221
+ [email],
222
+ );
223
+ return rows[0] || null;
224
+ },
225
+
226
+ async create(data: Omit<User, 'id'>): Promise<User> {
227
+ const { rows } = await app.pg.query(
228
+ 'INSERT INTO users (email, name) VALUES ($1, $2) RETURNING *',
229
+ [data.email, data.name],
230
+ );
231
+ return rows[0];
232
+ },
233
+
234
+ async update(id: string, data: Partial<User>): Promise<User | null> {
235
+ const fields = Object.keys(data);
236
+ const values = Object.values(data);
237
+ const setClause = fields
238
+ .map((f, i) => `${f} = $${i + 2}`)
239
+ .join(', ');
240
+
241
+ const { rows } = await app.pg.query(
242
+ `UPDATE users SET ${setClause} WHERE id = $1 RETURNING *`,
243
+ [id, ...values],
244
+ );
245
+ return rows[0] || null;
246
+ },
247
+
248
+ async delete(id: string): Promise<boolean> {
249
+ const { rowCount } = await app.pg.query(
250
+ 'DELETE FROM users WHERE id = $1',
251
+ [id],
252
+ );
253
+ return rowCount > 0;
254
+ },
255
+ };
256
+ }
257
+
258
+ // Usage in plugin
259
+ import fp from 'fastify-plugin';
260
+ import { createUserRepository } from './repositories/user.repository.js';
261
+
262
+ export default fp(async function repositoriesPlugin(fastify) {
263
+ fastify.decorate('repositories', {
264
+ users: createUserRepository(fastify),
265
+ });
266
+ }, {
267
+ name: 'repositories',
268
+ dependencies: ['database'],
269
+ });
270
+ ```
271
+
272
+ ## Testing with Database
273
+
274
+ Use transactions for test isolation:
275
+
276
+ ```typescript
277
+ import { describe, it, beforeEach, afterEach } from 'node:test';
278
+ import { build } from './app.js';
279
+
280
+ describe('User API', () => {
281
+ let app;
282
+ let client;
283
+
284
+ beforeEach(async () => {
285
+ app = await build();
286
+ client = await app.pg.connect();
287
+ await client.query('BEGIN');
288
+ });
289
+
290
+ afterEach(async () => {
291
+ await client.query('ROLLBACK');
292
+ client.release();
293
+ await app.close();
294
+ });
295
+
296
+ it('should create a user', async (t) => {
297
+ const response = await app.inject({
298
+ method: 'POST',
299
+ url: '/users',
300
+ payload: { email: 'test@example.com', name: 'Test' },
301
+ });
302
+
303
+ t.assert.equal(response.statusCode, 201);
304
+ });
305
+ });
306
+ ```
307
+
308
+ ## Connection Pool Configuration
309
+
310
+ Configure connection pools appropriately:
311
+
312
+ ```typescript
313
+ app.register(fastifyPostgres, {
314
+ connectionString: process.env.DATABASE_URL,
315
+ // Pool configuration
316
+ max: 20, // Maximum pool size
317
+ idleTimeoutMillis: 30000, // Close idle clients after 30s
318
+ connectionTimeoutMillis: 5000, // Timeout for new connections
319
+ });
320
+ ```