@malamute/ai-rules 1.0.0 → 1.2.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 (133) hide show
  1. package/README.md +270 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
  4. package/configs/_shared/.claude/rules/conventions/git.md +265 -0
  5. package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
  6. package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
  7. package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
  8. package/configs/_shared/.claude/rules/devops/docker.md +275 -0
  9. package/configs/_shared/.claude/rules/devops/nx.md +194 -0
  10. package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
  11. package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
  12. package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
  13. package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
  14. package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
  15. package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
  16. package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
  17. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
  18. package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
  19. package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
  20. package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
  21. package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
  22. package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
  23. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
  24. package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
  25. package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
  26. package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
  27. package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
  28. package/configs/_shared/.claude/rules/quality/logging.md +45 -0
  29. package/configs/_shared/.claude/rules/quality/observability.md +240 -0
  30. package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
  31. package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
  32. package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
  33. package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
  34. package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
  35. package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  36. package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  37. package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  38. package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
  39. package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
  40. package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
  41. package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
  42. package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
  43. package/configs/_shared/CLAUDE.md +52 -149
  44. package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
  45. package/configs/angular/.claude/rules/core/resource.md +285 -0
  46. package/configs/angular/.claude/rules/core/signals.md +323 -0
  47. package/configs/angular/.claude/rules/http.md +338 -0
  48. package/configs/angular/.claude/rules/routing.md +291 -0
  49. package/configs/angular/.claude/rules/ssr.md +312 -0
  50. package/configs/angular/.claude/rules/state/signal-store.md +408 -0
  51. package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
  52. package/configs/angular/.claude/rules/testing.md +7 -7
  53. package/configs/angular/.claude/rules/ui/aria.md +422 -0
  54. package/configs/angular/.claude/rules/ui/forms.md +424 -0
  55. package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
  56. package/configs/angular/.claude/settings.json +1 -0
  57. package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
  58. package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
  59. package/configs/angular/CLAUDE.md +24 -216
  60. package/configs/dotnet/.claude/rules/background-services.md +552 -0
  61. package/configs/dotnet/.claude/rules/configuration.md +426 -0
  62. package/configs/dotnet/.claude/rules/ddd.md +447 -0
  63. package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/.claude/rules/mediatr.md +320 -0
  65. package/configs/dotnet/.claude/rules/middleware.md +489 -0
  66. package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/.claude/rules/validation.md +388 -0
  68. package/configs/dotnet/.claude/settings.json +21 -3
  69. package/configs/dotnet/CLAUDE.md +53 -286
  70. package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/.claude/rules/dependencies.md +170 -0
  72. package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
  73. package/configs/fastapi/.claude/rules/lifespan.md +274 -0
  74. package/configs/fastapi/.claude/rules/middleware.md +229 -0
  75. package/configs/fastapi/.claude/rules/pydantic.md +433 -0
  76. package/configs/fastapi/.claude/rules/responses.md +251 -0
  77. package/configs/fastapi/.claude/rules/routers.md +202 -0
  78. package/configs/fastapi/.claude/rules/security.md +222 -0
  79. package/configs/fastapi/.claude/rules/testing.md +251 -0
  80. package/configs/fastapi/.claude/rules/websockets.md +298 -0
  81. package/configs/fastapi/.claude/settings.json +33 -0
  82. package/configs/fastapi/CLAUDE.md +144 -0
  83. package/configs/flask/.claude/rules/blueprints.md +208 -0
  84. package/configs/flask/.claude/rules/cli.md +285 -0
  85. package/configs/flask/.claude/rules/configuration.md +281 -0
  86. package/configs/flask/.claude/rules/context.md +238 -0
  87. package/configs/flask/.claude/rules/error-handlers.md +278 -0
  88. package/configs/flask/.claude/rules/extensions.md +278 -0
  89. package/configs/flask/.claude/rules/flask.md +171 -0
  90. package/configs/flask/.claude/rules/marshmallow.md +206 -0
  91. package/configs/flask/.claude/rules/security.md +267 -0
  92. package/configs/flask/.claude/rules/testing.md +284 -0
  93. package/configs/flask/.claude/settings.json +33 -0
  94. package/configs/flask/CLAUDE.md +166 -0
  95. package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/.claude/rules/filters.md +376 -0
  97. package/configs/nestjs/.claude/rules/interceptors.md +317 -0
  98. package/configs/nestjs/.claude/rules/middleware.md +321 -0
  99. package/configs/nestjs/.claude/rules/modules.md +26 -0
  100. package/configs/nestjs/.claude/rules/pipes.md +351 -0
  101. package/configs/nestjs/.claude/rules/websockets.md +451 -0
  102. package/configs/nestjs/.claude/settings.json +16 -2
  103. package/configs/nestjs/CLAUDE.md +57 -215
  104. package/configs/nextjs/.claude/rules/api-routes.md +358 -0
  105. package/configs/nextjs/.claude/rules/authentication.md +355 -0
  106. package/configs/nextjs/.claude/rules/components.md +52 -0
  107. package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/.claude/rules/database.md +400 -0
  109. package/configs/nextjs/.claude/rules/middleware.md +303 -0
  110. package/configs/nextjs/.claude/rules/routing.md +324 -0
  111. package/configs/nextjs/.claude/rules/seo.md +350 -0
  112. package/configs/nextjs/.claude/rules/server-actions.md +353 -0
  113. package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
  114. package/configs/nextjs/.claude/settings.json +5 -0
  115. package/configs/nextjs/CLAUDE.md +69 -331
  116. package/package.json +23 -9
  117. package/src/cli.js +220 -0
  118. package/src/config.js +29 -0
  119. package/src/index.js +13 -0
  120. package/src/installer.js +361 -0
  121. package/src/merge.js +116 -0
  122. package/src/tech-config.json +29 -0
  123. package/src/utils.js +96 -0
  124. package/configs/python/.claude/rules/flask.md +0 -332
  125. package/configs/python/.claude/settings.json +0 -18
  126. package/configs/python/CLAUDE.md +0 -273
  127. package/src/install.js +0 -315
  128. /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
  129. /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
  130. /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
  131. /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
  132. /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
  133. /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
@@ -4,316 +4,83 @@
4
4
 
5
5
  ## Stack
6
6
 
7
- - .NET 8+ (latest LTS)
7
+ - .NET 9 / .NET 8 (LTS)
8
8
  - ASP.NET Core Web API
9
9
  - Entity Framework Core
10
- - C# 12+ features
11
- - xUnit for testing
10
+ - C# 13+
11
+ - xUnit + NSubstitute + FluentAssertions
12
12
 
13
- ## Architecture
14
-
15
- ### Clean Architecture (Recommended)
13
+ ## Architecture - Clean Architecture
16
14
 
17
15
  ```
18
16
  src/
19
- ├── Domain/ # Core business logic (no dependencies)
20
- ├── Entities/
21
- │ │ └── User.cs
22
- ├── ValueObjects/
23
- │ └── Email.cs
24
- ├── Enums/
25
- │ ├── Exceptions/
26
- │ │ └── DomainException.cs
27
- │ └── Interfaces/
28
- │ └── IUserRepository.cs
29
-
30
- ├── Application/ # Use cases, CQRS, DTOs
31
- │ ├── Common/
32
- │ │ ├── Behaviors/
33
- │ │ │ ├── ValidationBehavior.cs
34
- │ │ │ └── LoggingBehavior.cs
35
- │ │ ├── Interfaces/
36
- │ │ │ └── IApplicationDbContext.cs
37
- │ │ └── Mappings/
38
- │ │ └── MappingProfile.cs
39
- │ ├── Users/
40
- │ │ ├── Commands/
41
- │ │ │ ├── CreateUser/
42
- │ │ │ │ ├── CreateUserCommand.cs
43
- │ │ │ │ ├── CreateUserCommandHandler.cs
44
- │ │ │ │ └── CreateUserCommandValidator.cs
45
- │ │ │ └── UpdateUser/
46
- │ │ └── Queries/
47
- │ │ ├── GetUser/
48
- │ │ │ ├── GetUserQuery.cs
49
- │ │ │ ├── GetUserQueryHandler.cs
50
- │ │ │ └── UserDto.cs
51
- │ │ └── GetUsers/
52
- │ └── DependencyInjection.cs
53
-
54
- ├── Infrastructure/ # External concerns
55
- │ ├── Data/
56
- │ │ ├── ApplicationDbContext.cs
57
- │ │ ├── Configurations/
58
- │ │ │ └── UserConfiguration.cs
59
- │ │ └── Migrations/
60
- │ ├── Repositories/
61
- │ │ └── UserRepository.cs
62
- │ ├── Services/
63
- │ │ └── DateTimeService.cs
64
- │ └── DependencyInjection.cs
65
-
66
- └── WebApi/ # Presentation layer
67
- ├── Controllers/
68
- │ └── UsersController.cs
69
- ├── Filters/
70
- │ └── ApiExceptionFilterAttribute.cs
71
- ├── Middleware/
72
- │ └── ExceptionHandlingMiddleware.cs
73
- ├── Program.cs
74
- └── appsettings.json
17
+ ├── Domain/ # Entities, ValueObjects, Interfaces
18
+ # ZERO external dependencies
19
+ ├── Application/ # Commands, Queries, DTOs, Validators
20
+ # MediatR, FluentValidation
21
+ ├── Infrastructure/ # DbContext, Repositories, External services
22
+ # EF Core, external APIs
23
+ └── WebApi/ # Controllers/Endpoints, Middleware
24
+ # Presentation layer
75
25
  ```
76
26
 
77
- ### Project References
27
+ **Dependencies**:
28
+ - `WebApi → Application → Domain`
29
+ - `Infrastructure → Application → Domain`
78
30
 
79
- ```
80
- WebApi → Application → Domain
81
- WebApi → Infrastructure → Application → Domain
82
- ```
31
+ ## Core Principles
83
32
 
84
- **Key rule**: Domain has ZERO external dependencies.
33
+ ### CQRS with MediatR
85
34
 
86
- ## Code Style
35
+ - **Commands** (write): Modify state, return minimal data (ID or void)
36
+ - **Queries** (read): Return data, never modify state
37
+ - Commands and queries implement `IRequest<T>`
87
38
 
88
- ### Naming Conventions
39
+ ### C# 12 Style
89
40
 
90
- | Element | Convention | Example |
91
- |---------|------------|---------|
92
- | Classes | PascalCase | `UserService` |
93
- | Interfaces | IPascalCase | `IUserRepository` |
94
- | Methods | PascalCase | `GetUserById` |
95
- | Properties | PascalCase | `FirstName` |
96
- | Private fields | _camelCase | `_userRepository` |
97
- | Parameters | camelCase | `userId` |
98
- | Constants | PascalCase | `MaxRetryCount` |
99
- | Async methods | Suffix Async | `GetUserAsync` |
41
+ - File-scoped namespaces
42
+ - Primary constructors for DI
43
+ - Records for DTOs
44
+ - Nullable reference types enabled
45
+ - Required members with `required` keyword
100
46
 
101
- ### File-Scoped Namespaces
47
+ ### Naming
102
48
 
103
- ```csharp
104
- // Good - C# 10+
105
- namespace MyApp.Domain.Entities;
49
+ | Element | Convention |
50
+ |---------|------------|
51
+ | Classes/Methods | PascalCase |
52
+ | Interfaces | IPascalCase |
53
+ | Private fields | _camelCase |
54
+ | Async methods | Suffix `Async` |
55
+ | Constants | PascalCase |
106
56
 
107
- public class User { }
57
+ ### API Patterns
108
58
 
109
- // Avoid
110
- namespace MyApp.Domain.Entities
111
- {
112
- public class User { }
113
- }
114
- ```
59
+ - **Minimal APIs**: Preferred for new projects (93% less memory in .NET 9)
60
+ - **Controllers**: Only for complex model binding scenarios
61
+ - Use `ISender` from MediatR for dispatching commands/queries
62
+ - Return `TypedResults.Ok()`, `TypedResults.NotFound()`, etc.
63
+ - Built-in OpenAPI: `AddOpenApi()` + `MapOpenApi()`
115
64
 
116
- ### Primary Constructors (C# 12)
65
+ ### Validation
117
66
 
118
- ```csharp
119
- // Good - for simple DI
120
- public class UserService(IUserRepository userRepository, ILogger<UserService> logger)
121
- {
122
- public async Task<User?> GetByIdAsync(Guid id)
123
- {
124
- logger.LogInformation("Getting user {UserId}", id);
125
- return await userRepository.GetByIdAsync(id);
126
- }
127
- }
128
-
129
- // Use traditional constructors when you need field assignment or validation
130
- ```
131
-
132
- ### Records for DTOs
133
-
134
- ```csharp
135
- // Immutable DTOs
136
- public record UserDto(Guid Id, string Email, string Name);
137
-
138
- public record CreateUserRequest(string Email, string Password, string Name);
139
-
140
- // With validation attributes
141
- public record CreateUserCommand(
142
- [Required][EmailAddress] string Email,
143
- [Required][MinLength(8)] string Password,
144
- [Required] string Name
145
- ) : IRequest<Guid>;
146
- ```
147
-
148
- ### Nullable Reference Types
149
-
150
- ```csharp
151
- // Enable in .csproj
152
- <Nullable>enable</Nullable>
153
-
154
- // Be explicit about nullability
155
- public async Task<User?> GetByIdAsync(Guid id); // Can return null
156
- public async Task<User> GetByIdOrThrowAsync(Guid id); // Never null
157
- ```
67
+ - FluentValidation for complex rules
68
+ - Data annotations for simple DTOs
69
+ - Validation in MediatR pipeline behavior
158
70
 
159
71
  ## Commands
160
72
 
161
73
  ```bash
162
- # Development
163
- dotnet run --project src/WebApi
164
-
165
- # Build
166
- dotnet build
167
- dotnet publish -c Release
168
-
169
- # Tests
170
- dotnet test
171
- dotnet test --filter "Category=Unit"
172
- dotnet test --collect:"XPlat Code Coverage"
173
-
174
- # EF Core migrations
175
- dotnet ef migrations add InitialCreate -p src/Infrastructure -s src/WebApi
74
+ dotnet run --project src/WebApi # Dev
75
+ dotnet build # Build
76
+ dotnet test # Test
77
+ dotnet ef migrations add Name -p src/Infrastructure -s src/WebApi
176
78
  dotnet ef database update -p src/Infrastructure -s src/WebApi
177
-
178
- # Format
179
- dotnet format
180
79
  ```
181
80
 
182
- ## Common Patterns
183
-
184
- ### Minimal API Endpoints
185
-
186
- ```csharp
187
- // Program.cs or endpoint extension
188
- app.MapGet("/users/{id:guid}", async (Guid id, ISender sender) =>
189
- {
190
- var user = await sender.Send(new GetUserQuery(id));
191
- return user is not null ? Results.Ok(user) : Results.NotFound();
192
- })
193
- .WithName("GetUser")
194
- .WithOpenApi()
195
- .RequireAuthorization();
196
-
197
- app.MapPost("/users", async (CreateUserCommand command, ISender sender) =>
198
- {
199
- var id = await sender.Send(command);
200
- return Results.CreatedAtRoute("GetUser", new { id }, id);
201
- })
202
- .WithName("CreateUser")
203
- .WithOpenApi();
204
- ```
205
-
206
- ### Controller-Based API
207
-
208
- ```csharp
209
- [ApiController]
210
- [Route("api/[controller]")]
211
- public class UsersController(ISender sender) : ControllerBase
212
- {
213
- [HttpGet("{id:guid}")]
214
- [ProducesResponseType<UserDto>(StatusCodes.Status200OK)]
215
- [ProducesResponseType(StatusCodes.Status404NotFound)]
216
- public async Task<IActionResult> Get(Guid id)
217
- {
218
- var user = await sender.Send(new GetUserQuery(id));
219
- return user is not null ? Ok(user) : NotFound();
220
- }
221
-
222
- [HttpPost]
223
- [ProducesResponseType<Guid>(StatusCodes.Status201Created)]
224
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
225
- public async Task<IActionResult> Create(CreateUserCommand command)
226
- {
227
- var id = await sender.Send(command);
228
- return CreatedAtAction(nameof(Get), new { id }, id);
229
- }
230
- }
231
- ```
232
-
233
- ### Global Exception Handling
234
-
235
- ```csharp
236
- public class ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
237
- {
238
- public async Task InvokeAsync(HttpContext context)
239
- {
240
- try
241
- {
242
- await next(context);
243
- }
244
- catch (ValidationException ex)
245
- {
246
- context.Response.StatusCode = StatusCodes.Status400BadRequest;
247
- await context.Response.WriteAsJsonAsync(new ProblemDetails
248
- {
249
- Status = 400,
250
- Title = "Validation Error",
251
- Detail = string.Join(", ", ex.Errors.Select(e => e.ErrorMessage))
252
- });
253
- }
254
- catch (NotFoundException ex)
255
- {
256
- context.Response.StatusCode = StatusCodes.Status404NotFound;
257
- await context.Response.WriteAsJsonAsync(new ProblemDetails
258
- {
259
- Status = 404,
260
- Title = "Not Found",
261
- Detail = ex.Message
262
- });
263
- }
264
- catch (Exception ex)
265
- {
266
- logger.LogError(ex, "Unhandled exception");
267
- context.Response.StatusCode = StatusCodes.Status500InternalServerError;
268
- await context.Response.WriteAsJsonAsync(new ProblemDetails
269
- {
270
- Status = 500,
271
- Title = "Server Error"
272
- });
273
- }
274
- }
275
- }
276
- ```
277
-
278
- ### Dependency Injection Setup
279
-
280
- ```csharp
281
- // Application/DependencyInjection.cs
282
- public static class DependencyInjection
283
- {
284
- public static IServiceCollection AddApplication(this IServiceCollection services)
285
- {
286
- services.AddMediatR(cfg => {
287
- cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
288
- cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
289
- cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
290
- });
291
-
292
- services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
293
- services.AddAutoMapper(Assembly.GetExecutingAssembly());
294
-
295
- return services;
296
- }
297
- }
298
-
299
- // Infrastructure/DependencyInjection.cs
300
- public static class DependencyInjection
301
- {
302
- public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
303
- {
304
- services.AddDbContext<ApplicationDbContext>(options =>
305
- options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
306
-
307
- services.AddScoped<IApplicationDbContext>(sp =>
308
- sp.GetRequiredService<ApplicationDbContext>());
309
-
310
- services.AddScoped<IUserRepository, UserRepository>();
311
-
312
- return services;
313
- }
314
- }
81
+ ## Code Style
315
82
 
316
- // Program.cs
317
- builder.Services.AddApplication();
318
- builder.Services.AddInfrastructure(builder.Configuration);
319
- ```
83
+ - `readonly` for immutable fields
84
+ - Expression-bodied members when simple
85
+ - Pattern matching over type checks
86
+ - `async`/`await` all the way down
@@ -0,0 +1,254 @@
1
+ ---
2
+ paths:
3
+ - "**/*.py"
4
+ ---
5
+
6
+ # FastAPI Background Tasks
7
+
8
+ ## Simple Background Tasks
9
+
10
+ ```python
11
+ from fastapi import BackgroundTasks
12
+
13
+ def write_log(message: str):
14
+ with open("log.txt", "a") as f:
15
+ f.write(f"{datetime.now()}: {message}\n")
16
+
17
+ @router.post("/items")
18
+ async def create_item(
19
+ item: ItemCreate,
20
+ background_tasks: BackgroundTasks,
21
+ ) -> ItemResponse:
22
+ # Process item
23
+ created_item = await item_service.create(item)
24
+
25
+ # Queue background task
26
+ background_tasks.add_task(write_log, f"Item created: {created_item.id}")
27
+
28
+ # Return immediately
29
+ return created_item
30
+ ```
31
+
32
+ ## Async Background Tasks
33
+
34
+ ```python
35
+ async def send_notification(user_id: int, message: str):
36
+ async with httpx.AsyncClient() as client:
37
+ await client.post(
38
+ "https://notification-service/send",
39
+ json={"user_id": user_id, "message": message},
40
+ )
41
+
42
+ @router.post("/orders")
43
+ async def create_order(
44
+ order: OrderCreate,
45
+ background_tasks: BackgroundTasks,
46
+ db: DbSession,
47
+ ) -> OrderResponse:
48
+ created_order = await order_service.create(db, order)
49
+
50
+ # Async task
51
+ background_tasks.add_task(
52
+ send_notification,
53
+ order.user_id,
54
+ f"Order {created_order.id} confirmed",
55
+ )
56
+
57
+ return created_order
58
+ ```
59
+
60
+ ## Multiple Background Tasks
61
+
62
+ ```python
63
+ @router.post("/signup")
64
+ async def signup(
65
+ user: UserCreate,
66
+ background_tasks: BackgroundTasks,
67
+ ) -> UserResponse:
68
+ created_user = await user_service.create(user)
69
+
70
+ # Queue multiple tasks
71
+ background_tasks.add_task(send_welcome_email, created_user.email)
72
+ background_tasks.add_task(notify_admin, created_user.id)
73
+ background_tasks.add_task(update_analytics, "new_signup")
74
+
75
+ return created_user
76
+ ```
77
+
78
+ ## Background Tasks in Dependencies
79
+
80
+ ```python
81
+ async def log_request(
82
+ request: Request,
83
+ background_tasks: BackgroundTasks,
84
+ ):
85
+ background_tasks.add_task(
86
+ log_to_database,
87
+ path=request.url.path,
88
+ method=request.method,
89
+ timestamp=datetime.now(),
90
+ )
91
+
92
+ @router.get("/data", dependencies=[Depends(log_request)])
93
+ async def get_data() -> dict:
94
+ return {"data": "value"}
95
+ ```
96
+
97
+ ## Long-Running Tasks with Celery
98
+
99
+ ```python
100
+ # tasks.py
101
+ from celery import Celery
102
+
103
+ celery_app = Celery(
104
+ "tasks",
105
+ broker="redis://localhost:6379/0",
106
+ backend="redis://localhost:6379/0",
107
+ )
108
+
109
+ @celery_app.task
110
+ def process_large_file(file_path: str) -> dict:
111
+ # Long-running process
112
+ result = heavy_computation(file_path)
113
+ return {"status": "completed", "result": result}
114
+
115
+ # router.py
116
+ from tasks import process_large_file
117
+
118
+ @router.post("/process")
119
+ async def process_file(file: UploadFile) -> dict:
120
+ # Save file
121
+ file_path = f"/tmp/{file.filename}"
122
+ async with aiofiles.open(file_path, "wb") as f:
123
+ await f.write(await file.read())
124
+
125
+ # Queue Celery task
126
+ task = process_large_file.delay(file_path)
127
+
128
+ return {"task_id": task.id, "status": "processing"}
129
+
130
+ @router.get("/process/{task_id}")
131
+ async def get_task_status(task_id: str) -> dict:
132
+ task = process_large_file.AsyncResult(task_id)
133
+
134
+ if task.ready():
135
+ return {"status": "completed", "result": task.result}
136
+
137
+ return {"status": task.status}
138
+ ```
139
+
140
+ ## ARQ (Async Redis Queue)
141
+
142
+ ```python
143
+ # tasks.py
144
+ from arq import create_pool
145
+ from arq.connections import RedisSettings
146
+
147
+ async def send_email_task(ctx, email: str, subject: str, body: str):
148
+ await email_service.send(email, subject, body)
149
+
150
+ class WorkerSettings:
151
+ functions = [send_email_task]
152
+ redis_settings = RedisSettings()
153
+
154
+ # router.py
155
+ from arq import create_pool
156
+
157
+ @router.post("/send-email")
158
+ async def send_email(email: EmailSchema) -> dict:
159
+ redis = await create_pool(RedisSettings())
160
+
161
+ job = await redis.enqueue_job(
162
+ "send_email_task",
163
+ email.to,
164
+ email.subject,
165
+ email.body,
166
+ )
167
+
168
+ return {"job_id": job.job_id, "status": "queued"}
169
+ ```
170
+
171
+ ## Periodic Tasks
172
+
173
+ ```python
174
+ # Using APScheduler
175
+ from apscheduler.schedulers.asyncio import AsyncIOScheduler
176
+
177
+ scheduler = AsyncIOScheduler()
178
+
179
+ async def cleanup_expired_sessions():
180
+ async with async_session() as db:
181
+ await db.execute(
182
+ delete(Session).where(Session.expires_at < datetime.utcnow())
183
+ )
184
+ await db.commit()
185
+
186
+ @asynccontextmanager
187
+ async def lifespan(app: FastAPI):
188
+ # Start scheduler
189
+ scheduler.add_job(
190
+ cleanup_expired_sessions,
191
+ "interval",
192
+ hours=1,
193
+ )
194
+ scheduler.start()
195
+ yield
196
+ # Shutdown
197
+ scheduler.shutdown()
198
+
199
+ app = FastAPI(lifespan=lifespan)
200
+ ```
201
+
202
+ ## Task Status Tracking
203
+
204
+ ```python
205
+ from enum import Enum
206
+ import uuid
207
+
208
+ class TaskStatus(str, Enum):
209
+ PENDING = "pending"
210
+ RUNNING = "running"
211
+ COMPLETED = "completed"
212
+ FAILED = "failed"
213
+
214
+ # In-memory store (use Redis in production)
215
+ tasks_store: dict[str, dict] = {}
216
+
217
+ async def process_with_tracking(task_id: str, data: dict):
218
+ tasks_store[task_id]["status"] = TaskStatus.RUNNING
219
+
220
+ try:
221
+ result = await heavy_process(data)
222
+ tasks_store[task_id].update({
223
+ "status": TaskStatus.COMPLETED,
224
+ "result": result,
225
+ })
226
+ except Exception as e:
227
+ tasks_store[task_id].update({
228
+ "status": TaskStatus.FAILED,
229
+ "error": str(e),
230
+ })
231
+
232
+ @router.post("/process")
233
+ async def start_processing(
234
+ data: ProcessData,
235
+ background_tasks: BackgroundTasks,
236
+ ) -> dict:
237
+ task_id = str(uuid.uuid4())
238
+
239
+ tasks_store[task_id] = {
240
+ "status": TaskStatus.PENDING,
241
+ "created_at": datetime.utcnow(),
242
+ }
243
+
244
+ background_tasks.add_task(process_with_tracking, task_id, data.dict())
245
+
246
+ return {"task_id": task_id}
247
+
248
+ @router.get("/process/{task_id}")
249
+ async def get_status(task_id: str) -> dict:
250
+ if task_id not in tasks_store:
251
+ raise HTTPException(404, "Task not found")
252
+
253
+ return tasks_store[task_id]
254
+ ```