autoworkflow 3.1.5 → 3.6.0

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 (124) hide show
  1. package/.claude/commands/analyze.md +19 -0
  2. package/.claude/commands/audit.md +26 -0
  3. package/.claude/commands/build.md +39 -0
  4. package/.claude/commands/commit.md +25 -0
  5. package/.claude/commands/fix.md +23 -0
  6. package/.claude/commands/plan.md +18 -0
  7. package/.claude/commands/suggest.md +23 -0
  8. package/.claude/commands/verify.md +18 -0
  9. package/.claude/hooks/post-bash-router.sh +20 -0
  10. package/.claude/hooks/post-commit.sh +140 -0
  11. package/.claude/hooks/post-edit.sh +190 -17
  12. package/.claude/hooks/pre-edit.sh +221 -0
  13. package/.claude/hooks/session-check.sh +90 -0
  14. package/.claude/settings.json +56 -6
  15. package/.claude/settings.local.json +5 -1
  16. package/.claude/skills/actix.md +337 -0
  17. package/.claude/skills/alembic.md +504 -0
  18. package/.claude/skills/angular.md +237 -0
  19. package/.claude/skills/api-design.md +187 -0
  20. package/.claude/skills/aspnet-core.md +377 -0
  21. package/.claude/skills/astro.md +245 -0
  22. package/.claude/skills/auth-clerk.md +327 -0
  23. package/.claude/skills/auth-firebase.md +367 -0
  24. package/.claude/skills/auth-nextauth.md +359 -0
  25. package/.claude/skills/auth-supabase.md +368 -0
  26. package/.claude/skills/axum.md +386 -0
  27. package/.claude/skills/blazor.md +456 -0
  28. package/.claude/skills/chi.md +348 -0
  29. package/.claude/skills/code-review.md +133 -0
  30. package/.claude/skills/csharp.md +296 -0
  31. package/.claude/skills/css-modules.md +325 -0
  32. package/.claude/skills/cypress.md +343 -0
  33. package/.claude/skills/debugging.md +133 -0
  34. package/.claude/skills/diesel.md +392 -0
  35. package/.claude/skills/django.md +301 -0
  36. package/.claude/skills/docker.md +319 -0
  37. package/.claude/skills/doctrine.md +473 -0
  38. package/.claude/skills/documentation.md +182 -0
  39. package/.claude/skills/dotnet.md +409 -0
  40. package/.claude/skills/drizzle.md +293 -0
  41. package/.claude/skills/echo.md +321 -0
  42. package/.claude/skills/eloquent.md +256 -0
  43. package/.claude/skills/emotion.md +426 -0
  44. package/.claude/skills/entity-framework.md +370 -0
  45. package/.claude/skills/express.md +316 -0
  46. package/.claude/skills/fastapi.md +329 -0
  47. package/.claude/skills/fastify.md +299 -0
  48. package/.claude/skills/fiber.md +315 -0
  49. package/.claude/skills/flask.md +322 -0
  50. package/.claude/skills/gin.md +342 -0
  51. package/.claude/skills/git.md +116 -0
  52. package/.claude/skills/github-actions.md +353 -0
  53. package/.claude/skills/go.md +377 -0
  54. package/.claude/skills/gorm.md +409 -0
  55. package/.claude/skills/graphql.md +478 -0
  56. package/.claude/skills/hibernate.md +379 -0
  57. package/.claude/skills/hono.md +306 -0
  58. package/.claude/skills/java.md +400 -0
  59. package/.claude/skills/jest.md +313 -0
  60. package/.claude/skills/jpa.md +282 -0
  61. package/.claude/skills/kotlin.md +347 -0
  62. package/.claude/skills/kubernetes.md +363 -0
  63. package/.claude/skills/laravel.md +414 -0
  64. package/.claude/skills/mcp-browser.md +320 -0
  65. package/.claude/skills/mcp-database.md +219 -0
  66. package/.claude/skills/mcp-fetch.md +241 -0
  67. package/.claude/skills/mcp-filesystem.md +204 -0
  68. package/.claude/skills/mcp-github.md +217 -0
  69. package/.claude/skills/mcp-memory.md +240 -0
  70. package/.claude/skills/mcp-search.md +218 -0
  71. package/.claude/skills/mcp-slack.md +262 -0
  72. package/.claude/skills/micronaut.md +388 -0
  73. package/.claude/skills/mongodb.md +319 -0
  74. package/.claude/skills/mongoose.md +355 -0
  75. package/.claude/skills/mysql.md +281 -0
  76. package/.claude/skills/nestjs.md +335 -0
  77. package/.claude/skills/nextjs-app-router.md +260 -0
  78. package/.claude/skills/nextjs-pages.md +172 -0
  79. package/.claude/skills/nuxt.md +202 -0
  80. package/.claude/skills/openapi.md +489 -0
  81. package/.claude/skills/performance.md +199 -0
  82. package/.claude/skills/php.md +398 -0
  83. package/.claude/skills/playwright.md +371 -0
  84. package/.claude/skills/postgresql.md +257 -0
  85. package/.claude/skills/prisma.md +293 -0
  86. package/.claude/skills/pydantic.md +304 -0
  87. package/.claude/skills/pytest.md +313 -0
  88. package/.claude/skills/python.md +272 -0
  89. package/.claude/skills/quarkus.md +377 -0
  90. package/.claude/skills/react.md +230 -0
  91. package/.claude/skills/redis.md +391 -0
  92. package/.claude/skills/refactoring.md +143 -0
  93. package/.claude/skills/remix.md +246 -0
  94. package/.claude/skills/rest-api.md +490 -0
  95. package/.claude/skills/rocket.md +366 -0
  96. package/.claude/skills/rust.md +341 -0
  97. package/.claude/skills/sass.md +380 -0
  98. package/.claude/skills/sea-orm.md +382 -0
  99. package/.claude/skills/security.md +167 -0
  100. package/.claude/skills/sequelize.md +395 -0
  101. package/.claude/skills/spring-boot.md +416 -0
  102. package/.claude/skills/sqlalchemy.md +269 -0
  103. package/.claude/skills/sqlx-rust.md +408 -0
  104. package/.claude/skills/state-jotai.md +346 -0
  105. package/.claude/skills/state-mobx.md +353 -0
  106. package/.claude/skills/state-pinia.md +431 -0
  107. package/.claude/skills/state-redux.md +337 -0
  108. package/.claude/skills/state-tanstack-query.md +434 -0
  109. package/.claude/skills/state-zustand.md +340 -0
  110. package/.claude/skills/styled-components.md +403 -0
  111. package/.claude/skills/svelte.md +238 -0
  112. package/.claude/skills/sveltekit.md +207 -0
  113. package/.claude/skills/symfony.md +437 -0
  114. package/.claude/skills/tailwind.md +279 -0
  115. package/.claude/skills/terraform.md +394 -0
  116. package/.claude/skills/testing-library.md +371 -0
  117. package/.claude/skills/trpc.md +426 -0
  118. package/.claude/skills/typeorm.md +368 -0
  119. package/.claude/skills/vitest.md +330 -0
  120. package/.claude/skills/vue.md +202 -0
  121. package/.claude/skills/warp.md +365 -0
  122. package/README.md +163 -52
  123. package/package.json +1 -1
  124. package/system/triggers.md +256 -17
@@ -0,0 +1,379 @@
1
+ # Hibernate Skill
2
+
3
+ ## Entity Definition
4
+ \`\`\`java
5
+ @Entity
6
+ @Table(name = "users", indexes = {
7
+ @Index(name = "idx_users_email", columnList = "email")
8
+ })
9
+ public class User {
10
+
11
+ @Id
12
+ @GeneratedValue(strategy = GenerationType.UUID)
13
+ private String id;
14
+
15
+ @Column(unique = true, nullable = false, length = 255)
16
+ private String email;
17
+
18
+ @Column(nullable = false, length = 100)
19
+ private String name;
20
+
21
+ @Column(name = "password_hash", nullable = false)
22
+ private String passwordHash;
23
+
24
+ @Column(name = "is_active")
25
+ private boolean isActive = true;
26
+
27
+ @CreationTimestamp
28
+ @Column(name = "created_at", updatable = false)
29
+ private Instant createdAt;
30
+
31
+ @UpdateTimestamp
32
+ @Column(name = "updated_at")
33
+ private Instant updatedAt;
34
+
35
+ // Relationships
36
+ @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
37
+ private Profile profile;
38
+
39
+ @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
40
+ @OrderBy("createdAt DESC")
41
+ private List<Post> posts = new ArrayList<>();
42
+
43
+ @ManyToMany
44
+ @JoinTable(
45
+ name = "user_roles",
46
+ joinColumns = @JoinColumn(name = "user_id"),
47
+ inverseJoinColumns = @JoinColumn(name = "role_id")
48
+ )
49
+ private Set<Role> roles = new HashSet<>();
50
+
51
+ // Helper methods for bidirectional relationships
52
+ public void addPost(Post post) {
53
+ posts.add(post);
54
+ post.setAuthor(this);
55
+ }
56
+
57
+ public void removePost(Post post) {
58
+ posts.remove(post);
59
+ post.setAuthor(null);
60
+ }
61
+
62
+ public void addRole(Role role) {
63
+ roles.add(role);
64
+ role.getUsers().add(this);
65
+ }
66
+
67
+ // Getters and setters...
68
+
69
+ @Override
70
+ public boolean equals(Object o) {
71
+ if (this == o) return true;
72
+ if (!(o instanceof User user)) return false;
73
+ return id != null && id.equals(user.id);
74
+ }
75
+
76
+ @Override
77
+ public int hashCode() {
78
+ return getClass().hashCode();
79
+ }
80
+ }
81
+
82
+ @Entity
83
+ @Table(name = "posts")
84
+ public class Post {
85
+
86
+ @Id
87
+ @GeneratedValue(strategy = GenerationType.UUID)
88
+ private String id;
89
+
90
+ @Column(nullable = false)
91
+ private String title;
92
+
93
+ @Column(columnDefinition = "TEXT")
94
+ private String content;
95
+
96
+ private boolean published = false;
97
+
98
+ @ManyToOne(fetch = FetchType.LAZY)
99
+ @JoinColumn(name = "author_id")
100
+ private User author;
101
+
102
+ @ManyToMany
103
+ @JoinTable(
104
+ name = "post_tags",
105
+ joinColumns = @JoinColumn(name = "post_id"),
106
+ inverseJoinColumns = @JoinColumn(name = "tag_id")
107
+ )
108
+ private Set<Tag> tags = new HashSet<>();
109
+
110
+ @CreationTimestamp
111
+ @Column(name = "created_at", updatable = false)
112
+ private Instant createdAt;
113
+ }
114
+ \`\`\`
115
+
116
+ ## JPQL Queries
117
+ \`\`\`java
118
+ @Repository
119
+ public interface UserRepository extends JpaRepository<User, String> {
120
+
121
+ // Derived query methods
122
+ Optional<User> findByEmail(String email);
123
+
124
+ List<User> findByIsActiveTrue();
125
+
126
+ boolean existsByEmail(String email);
127
+
128
+ // JPQL queries
129
+ @Query("SELECT u FROM User u WHERE u.isActive = true ORDER BY u.createdAt DESC")
130
+ List<User> findActiveUsers();
131
+
132
+ @Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
133
+ Optional<User> findByIdWithRoles(@Param("id") String id);
134
+
135
+ @Query("SELECT u FROM User u LEFT JOIN FETCH u.posts WHERE u.id = :id")
136
+ Optional<User> findByIdWithPosts(@Param("id") String id);
137
+
138
+ // Pagination
139
+ @Query("SELECT u FROM User u WHERE u.isActive = true")
140
+ Page<User> findActiveUsersPaged(Pageable pageable);
141
+
142
+ // Modifying queries
143
+ @Modifying
144
+ @Query("UPDATE User u SET u.isActive = false WHERE u.id = :id")
145
+ int deactivateUser(@Param("id") String id);
146
+
147
+ @Modifying
148
+ @Query("DELETE FROM User u WHERE u.isActive = false AND u.createdAt < :date")
149
+ int deleteInactiveOlderThan(@Param("date") Instant date);
150
+ }
151
+ \`\`\`
152
+
153
+ ## Criteria API (Dynamic Queries)
154
+ \`\`\`java
155
+ @Repository
156
+ public class UserSearchRepository {
157
+
158
+ @PersistenceContext
159
+ private EntityManager em;
160
+
161
+ public List<User> search(UserSearchCriteria criteria) {
162
+ CriteriaBuilder cb = em.getCriteriaBuilder();
163
+ CriteriaQuery<User> cq = cb.createQuery(User.class);
164
+ Root<User> user = cq.from(User.class);
165
+
166
+ List<Predicate> predicates = new ArrayList<>();
167
+
168
+ if (criteria.getEmail() != null) {
169
+ predicates.add(cb.like(
170
+ cb.lower(user.get("email")),
171
+ "%" + criteria.getEmail().toLowerCase() + "%"
172
+ ));
173
+ }
174
+
175
+ if (criteria.getName() != null) {
176
+ predicates.add(cb.like(
177
+ cb.lower(user.get("name")),
178
+ "%" + criteria.getName().toLowerCase() + "%"
179
+ ));
180
+ }
181
+
182
+ if (criteria.getIsActive() != null) {
183
+ predicates.add(cb.equal(user.get("isActive"), criteria.getIsActive()));
184
+ }
185
+
186
+ if (criteria.getCreatedAfter() != null) {
187
+ predicates.add(cb.greaterThan(user.get("createdAt"), criteria.getCreatedAfter()));
188
+ }
189
+
190
+ cq.where(predicates.toArray(new Predicate[0]));
191
+ cq.orderBy(cb.desc(user.get("createdAt")));
192
+
193
+ return em.createQuery(cq)
194
+ .setFirstResult(criteria.getOffset())
195
+ .setMaxResults(criteria.getLimit())
196
+ .getResultList();
197
+ }
198
+ }
199
+
200
+ // With Specification (Spring Data JPA)
201
+ public class UserSpecifications {
202
+
203
+ public static Specification<User> emailContains(String email) {
204
+ return (root, query, cb) -> email == null ? null :
205
+ cb.like(cb.lower(root.get("email")), "%" + email.toLowerCase() + "%");
206
+ }
207
+
208
+ public static Specification<User> isActive(Boolean active) {
209
+ return (root, query, cb) -> active == null ? null :
210
+ cb.equal(root.get("isActive"), active);
211
+ }
212
+
213
+ public static Specification<User> createdAfter(Instant date) {
214
+ return (root, query, cb) -> date == null ? null :
215
+ cb.greaterThan(root.get("createdAt"), date);
216
+ }
217
+ }
218
+
219
+ // Usage
220
+ List<User> users = userRepository.findAll(
221
+ Specification.where(emailContains(email))
222
+ .and(isActive(true))
223
+ .and(createdAfter(lastWeek))
224
+ );
225
+ \`\`\`
226
+
227
+ ## Entity Graphs (Solving N+1)
228
+ \`\`\`java
229
+ @Entity
230
+ @NamedEntityGraph(
231
+ name = "User.withPostsAndRoles",
232
+ attributeNodes = {
233
+ @NamedAttributeNode("posts"),
234
+ @NamedAttributeNode("roles")
235
+ }
236
+ )
237
+ public class User { ... }
238
+
239
+ // Using in repository
240
+ @EntityGraph(attributePaths = {"posts", "roles"})
241
+ Optional<User> findWithDetailsById(String id);
242
+
243
+ // Or with JPQL
244
+ @Query("SELECT u FROM User u")
245
+ @EntityGraph(attributePaths = {"posts"})
246
+ List<User> findAllWithPosts();
247
+
248
+ // Programmatic entity graph
249
+ public User findWithGraph(String id) {
250
+ EntityGraph<User> graph = em.createEntityGraph(User.class);
251
+ graph.addAttributeNodes("posts", "roles");
252
+
253
+ Map<String, Object> hints = new HashMap<>();
254
+ hints.put("jakarta.persistence.fetchgraph", graph);
255
+
256
+ return em.find(User.class, id, hints);
257
+ }
258
+ \`\`\`
259
+
260
+ ## Transactions and Session Management
261
+ \`\`\`java
262
+ @Service
263
+ @Transactional(readOnly = true)
264
+ public class UserService {
265
+
266
+ private final UserRepository userRepository;
267
+ private final EntityManager em;
268
+
269
+ @Transactional
270
+ public User create(CreateUserRequest request) {
271
+ User user = new User();
272
+ user.setEmail(request.email());
273
+ user.setName(request.name());
274
+ return userRepository.save(user);
275
+ }
276
+
277
+ @Transactional
278
+ public void updateWithManualFlush(String id, String name) {
279
+ User user = userRepository.findById(id).orElseThrow();
280
+ user.setName(name);
281
+ // Explicit flush if needed before external call
282
+ em.flush();
283
+ // Call external service...
284
+ }
285
+
286
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
287
+ public void auditAction(String action) {
288
+ // Runs in separate transaction
289
+ // Commits even if parent transaction rolls back
290
+ }
291
+
292
+ @Transactional(rollbackFor = Exception.class)
293
+ public void complexOperation() {
294
+ // Rolls back on any exception, not just RuntimeException
295
+ }
296
+ }
297
+ \`\`\`
298
+
299
+ ## Caching
300
+ \`\`\`java
301
+ // Entity with second-level cache
302
+ @Entity
303
+ @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
304
+ public class User {
305
+ // ...
306
+
307
+ @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
308
+ @OneToMany(mappedBy = "author")
309
+ private List<Post> posts;
310
+ }
311
+
312
+ // Query cache
313
+ @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
314
+ List<User> findByIsActiveTrue();
315
+
316
+ // Configuration (application.properties)
317
+ spring.jpa.properties.hibernate.cache.use_second_level_cache=true
318
+ spring.jpa.properties.hibernate.cache.use_query_cache=true
319
+ spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory
320
+ spring.jpa.properties.hibernate.javax.cache.provider=org.ehcache.jsr107.EhcacheCachingProvider
321
+
322
+ // Cache eviction
323
+ @CacheEvict(value = "users", key = "#id")
324
+ public void delete(String id) {
325
+ userRepository.deleteById(id);
326
+ }
327
+ \`\`\`
328
+
329
+ ## Batch Operations
330
+ \`\`\`java
331
+ // Batch inserts - configure in properties
332
+ spring.jpa.properties.hibernate.jdbc.batch_size=50
333
+ spring.jpa.properties.hibernate.order_inserts=true
334
+ spring.jpa.properties.hibernate.order_updates=true
335
+
336
+ // Bulk update
337
+ @Modifying
338
+ @Query("UPDATE User u SET u.isActive = false WHERE u.createdAt < :date")
339
+ int bulkDeactivate(@Param("date") Instant date);
340
+
341
+ // Batch processing large datasets
342
+ @Transactional
343
+ public void processAllUsers() {
344
+ int batchSize = 100;
345
+ int offset = 0;
346
+ List<User> batch;
347
+
348
+ do {
349
+ batch = em.createQuery("SELECT u FROM User u", User.class)
350
+ .setFirstResult(offset)
351
+ .setMaxResults(batchSize)
352
+ .getResultList();
353
+
354
+ for (User user : batch) {
355
+ processUser(user);
356
+ }
357
+
358
+ em.flush();
359
+ em.clear(); // Clear persistence context to avoid memory issues
360
+ offset += batchSize;
361
+ } while (!batch.isEmpty());
362
+ }
363
+ \`\`\`
364
+
365
+ ## ✅ DO
366
+ - Use \`FetchType.LAZY\` for all relationships (default for @OneToMany/@ManyToMany)
367
+ - Use \`@EntityGraph\` or \`JOIN FETCH\` to solve N+1 problems
368
+ - Override \`equals()\` and \`hashCode()\` using business key or ID
369
+ - Use helper methods for bidirectional relationships
370
+ - Use \`@Transactional(readOnly = true)\` for read operations
371
+ - Clear persistence context (\`em.clear()\`) when processing large batches
372
+
373
+ ## ❌ DON'T
374
+ - Don't use \`FetchType.EAGER\` - causes performance issues
375
+ - Don't use \`@Data\` from Lombok on entities (breaks equals/hashCode)
376
+ - Don't call getters on lazy relationships outside transaction
377
+ - Don't forget \`orphanRemoval = true\` when needed
378
+ - Don't use \`CascadeType.ALL\` without understanding implications
379
+ - Don't compare entities with \`==\` (use \`equals()\`)
@@ -0,0 +1,306 @@
1
+ # Hono Skill
2
+
3
+ ## Project Structure
4
+ \`\`\`
5
+ src/
6
+ ├── index.ts # Entry point
7
+ ├── app.ts # Hono app factory
8
+ ├── middleware/
9
+ │ ├── auth.ts # JWT auth
10
+ │ └── logger.ts # Request logging
11
+ ├── routes/
12
+ │ ├── index.ts # Route aggregator
13
+ │ └── users.ts # User routes
14
+ └── lib/
15
+ ├── db.ts # Database client
16
+ └── validators.ts # Zod schemas
17
+ \`\`\`
18
+
19
+ ## App Setup
20
+ \`\`\`typescript
21
+ import { Hono } from 'hono';
22
+ import { cors } from 'hono/cors';
23
+ import { logger } from 'hono/logger';
24
+ import { secureHeaders } from 'hono/secure-headers';
25
+ import { prettyJSON } from 'hono/pretty-json';
26
+
27
+ import { usersApp } from './routes/users';
28
+ import { authMiddleware } from './middleware/auth';
29
+ import { errorHandler } from './middleware/error';
30
+
31
+ // Type-safe environment variables
32
+ type Bindings = {
33
+ DATABASE_URL: string;
34
+ JWT_SECRET: string;
35
+ };
36
+
37
+ const app = new Hono<{ Bindings: Bindings }>();
38
+
39
+ // Global middleware
40
+ app.use('*', logger());
41
+ app.use('*', secureHeaders());
42
+ app.use('*', cors({ origin: '*' }));
43
+ app.use('*', prettyJSON());
44
+
45
+ // Error handler
46
+ app.onError(errorHandler);
47
+
48
+ // Routes
49
+ app.route('/api/users', usersApp);
50
+
51
+ // Health check
52
+ app.get('/health', (c) => c.json({ status: 'ok' }));
53
+
54
+ export default app;
55
+ \`\`\`
56
+
57
+ ## Routes with Validation
58
+ \`\`\`typescript
59
+ // routes/users.ts
60
+ import { Hono } from 'hono';
61
+ import { zValidator } from '@hono/zod-validator';
62
+ import { z } from 'zod';
63
+
64
+ // Schemas
65
+ const createUserSchema = z.object({
66
+ email: z.string().email(),
67
+ name: z.string().min(1),
68
+ password: z.string().min(8),
69
+ });
70
+
71
+ const updateUserSchema = createUserSchema.partial();
72
+
73
+ const paramsSchema = z.object({
74
+ id: z.string().uuid(),
75
+ });
76
+
77
+ const querySchema = z.object({
78
+ page: z.coerce.number().min(1).default(1),
79
+ limit: z.coerce.number().min(1).max(100).default(10),
80
+ });
81
+
82
+ // Type inference
83
+ type CreateUser = z.infer<typeof createUserSchema>;
84
+
85
+ export const usersApp = new Hono()
86
+ // GET /users
87
+ .get('/', zValidator('query', querySchema), async (c) => {
88
+ const { page, limit } = c.req.valid('query');
89
+ const users = await db.user.findMany({
90
+ skip: (page - 1) * limit,
91
+ take: limit,
92
+ });
93
+ return c.json({ users, page, limit });
94
+ })
95
+
96
+ // GET /users/:id
97
+ .get('/:id', zValidator('param', paramsSchema), async (c) => {
98
+ const { id } = c.req.valid('param');
99
+ const user = await db.user.findUnique({ where: { id } });
100
+
101
+ if (!user) {
102
+ return c.json({ error: 'User not found' }, 404);
103
+ }
104
+
105
+ return c.json(user);
106
+ })
107
+
108
+ // POST /users
109
+ .post('/', zValidator('json', createUserSchema), async (c) => {
110
+ const data = c.req.valid('json');
111
+ const user = await db.user.create({ data });
112
+ return c.json(user, 201);
113
+ })
114
+
115
+ // PATCH /users/:id
116
+ .patch(
117
+ '/:id',
118
+ zValidator('param', paramsSchema),
119
+ zValidator('json', updateUserSchema),
120
+ async (c) => {
121
+ const { id } = c.req.valid('param');
122
+ const data = c.req.valid('json');
123
+ const user = await db.user.update({ where: { id }, data });
124
+ return c.json(user);
125
+ }
126
+ )
127
+
128
+ // DELETE /users/:id
129
+ .delete('/:id', zValidator('param', paramsSchema), async (c) => {
130
+ const { id } = c.req.valid('param');
131
+ await db.user.delete({ where: { id } });
132
+ return c.body(null, 204);
133
+ });
134
+ \`\`\`
135
+
136
+ ## JWT Authentication
137
+ \`\`\`typescript
138
+ // middleware/auth.ts
139
+ import { jwt } from 'hono/jwt';
140
+ import { createMiddleware } from 'hono/factory';
141
+ import type { Context, Next } from 'hono';
142
+
143
+ // Type-safe user in context
144
+ type Variables = {
145
+ user: { id: string; email: string };
146
+ };
147
+
148
+ // JWT middleware
149
+ export const authMiddleware = jwt({
150
+ secret: (c) => c.env.JWT_SECRET,
151
+ });
152
+
153
+ // Custom auth with user loading
154
+ export const requireAuth = createMiddleware<{ Variables: Variables }>(
155
+ async (c, next) => {
156
+ const authHeader = c.req.header('Authorization');
157
+
158
+ if (!authHeader?.startsWith('Bearer ')) {
159
+ return c.json({ error: 'Unauthorized' }, 401);
160
+ }
161
+
162
+ const token = authHeader.split(' ')[1];
163
+
164
+ try {
165
+ const payload = await verifyToken(token, c.env.JWT_SECRET);
166
+ c.set('user', payload);
167
+ await next();
168
+ } catch {
169
+ return c.json({ error: 'Invalid token' }, 401);
170
+ }
171
+ }
172
+ );
173
+
174
+ // Usage
175
+ app.get('/me', requireAuth, (c) => {
176
+ const user = c.get('user');
177
+ return c.json(user);
178
+ });
179
+
180
+ // Protected route group
181
+ const protectedRoutes = new Hono()
182
+ .use('*', requireAuth)
183
+ .get('/profile', (c) => c.json(c.get('user')))
184
+ .get('/settings', (c) => c.json({ theme: 'dark' }));
185
+
186
+ app.route('/api', protectedRoutes);
187
+ \`\`\`
188
+
189
+ ## Error Handling
190
+ \`\`\`typescript
191
+ // middleware/error.ts
192
+ import { HTTPException } from 'hono/http-exception';
193
+ import type { ErrorHandler } from 'hono';
194
+
195
+ export const errorHandler: ErrorHandler = (err, c) => {
196
+ console.error(err);
197
+
198
+ if (err instanceof HTTPException) {
199
+ return c.json({ error: err.message }, err.status);
200
+ }
201
+
202
+ // Zod validation errors
203
+ if (err.name === 'ZodError') {
204
+ return c.json({
205
+ error: 'Validation failed',
206
+ details: err.errors,
207
+ }, 400);
208
+ }
209
+
210
+ return c.json({ error: 'Internal server error' }, 500);
211
+ };
212
+
213
+ // Throwing HTTP errors
214
+ import { HTTPException } from 'hono/http-exception';
215
+
216
+ app.get('/users/:id', async (c) => {
217
+ const user = await db.user.findUnique({ where: { id } });
218
+
219
+ if (!user) {
220
+ throw new HTTPException(404, { message: 'User not found' });
221
+ }
222
+
223
+ return c.json(user);
224
+ });
225
+ \`\`\`
226
+
227
+ ## Middleware Patterns
228
+ \`\`\`typescript
229
+ // Rate limiting
230
+ import { rateLimiter } from 'hono-rate-limiter';
231
+
232
+ app.use(
233
+ '/api/*',
234
+ rateLimiter({
235
+ windowMs: 15 * 60 * 1000, // 15 minutes
236
+ limit: 100,
237
+ keyGenerator: (c) => c.req.header('CF-Connecting-IP') ?? 'anonymous',
238
+ })
239
+ );
240
+
241
+ // Request timing
242
+ app.use('*', async (c, next) => {
243
+ const start = Date.now();
244
+ await next();
245
+ const duration = Date.now() - start;
246
+ c.header('X-Response-Time', \`\${duration}ms\`);
247
+ });
248
+
249
+ // CORS with options
250
+ import { cors } from 'hono/cors';
251
+
252
+ app.use('/api/*', cors({
253
+ origin: ['https://example.com'],
254
+ allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
255
+ allowHeaders: ['Content-Type', 'Authorization'],
256
+ exposeHeaders: ['X-Request-Id'],
257
+ credentials: true,
258
+ }));
259
+ \`\`\`
260
+
261
+ ## Edge Runtime (Cloudflare Workers)
262
+ \`\`\`typescript
263
+ // wrangler.toml
264
+ // name = "my-api"
265
+ // main = "src/index.ts"
266
+ // compatibility_date = "2024-01-01"
267
+
268
+ // [vars]
269
+ // ENVIRONMENT = "production"
270
+
271
+ // [[d1_databases]]
272
+ // binding = "DB"
273
+ // database_name = "my-db"
274
+
275
+ import { Hono } from 'hono';
276
+
277
+ type Bindings = {
278
+ DB: D1Database;
279
+ ENVIRONMENT: string;
280
+ };
281
+
282
+ const app = new Hono<{ Bindings: Bindings }>();
283
+
284
+ app.get('/users', async (c) => {
285
+ const { results } = await c.env.DB
286
+ .prepare('SELECT * FROM users')
287
+ .all();
288
+ return c.json(results);
289
+ });
290
+
291
+ export default app;
292
+ \`\`\`
293
+
294
+ ## ❌ DON'T
295
+ - Skip input validation
296
+ - Expose stack traces in production
297
+ - Forget error handling middleware
298
+ - Block the event loop with sync operations
299
+
300
+ ## ✅ DO
301
+ - Use Zod for validation (@hono/zod-validator)
302
+ - Use middleware for cross-cutting concerns
303
+ - Use typed context (Bindings, Variables)
304
+ - Deploy to edge (Cloudflare, Vercel, Deno)
305
+ - Handle errors with HTTPException
306
+ - Use route groups for organization