Skip to content

Commit 396a414

Browse files
committed
docs: Document and refine API client generation workflow, update scripts to target Refit interface in BookStore.Client, and clarify NSwag usage.
1 parent d8e9985 commit 396a414

7 files changed

Lines changed: 203 additions & 16 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,9 @@ BookStore/
171171
- **Scalar** - API documentation UI
172172
- **Docker** - Container runtime
173173
- **TUnit** - Modern testing framework with built-in code coverage
174+
- **Roslyn Analyzers** - Custom analyzers for Event Sourcing/CQRS patterns
174175
- **Roslynator.Analyzers 4.15.0** - Enhanced code analysis
176+
- **NSwag** - OpenAPI client generation (optional development tool)
175177

176178
## 📊 API Endpoints
177179

_tools/README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# API Client Update Workflow
2+
3+
Quick reference for updating the BookStore API client when the API changes.
4+
5+
## Workflow
6+
7+
### 1. Start the API
8+
9+
```bash
10+
aspire run
11+
```
12+
13+
### 2. Update OpenAPI Spec
14+
15+
```bash
16+
./_tools/update-openapi.sh
17+
```
18+
19+
This downloads the latest OpenAPI spec from the running API to `openapi.json`.
20+
21+
### 3. Update Client (Choose One)
22+
23+
#### Option A: NSwag Auto-Generation (Recommended)
24+
25+
```bash
26+
./_tools/generate-client-nswag.sh
27+
```
28+
29+
**Pros**:
30+
- ✅ Automatic - generates entire interface
31+
- ✅ Consistent - always matches OpenAPI spec
32+
- ✅ Fast - one command
33+
34+
**Cons**:
35+
- ❌ Requires NSwag CLI installed
36+
- ❌ May need manual cleanup
37+
38+
#### Option B: Manual Update
39+
40+
Edit `src/Client/BookStore.Client/IBookStoreApi.cs` manually.
41+
42+
**Pros**:
43+
- ✅ Full control over interface
44+
- ✅ Clean, minimal code
45+
- ✅ No tool dependencies
46+
47+
**Cons**:
48+
- ❌ Manual work
49+
- ❌ Can get out of sync
50+
51+
### 4. Build and Test
52+
53+
```bash
54+
dotnet build
55+
dotnet test
56+
```
57+
58+
### 5. Commit Changes
59+
60+
```bash
61+
git add openapi.json src/Client/BookStore.Client/IBookStoreApi.cs
62+
git commit -m "Update API client: [description]"
63+
```
64+
65+
## Installing NSwag CLI (Optional)
66+
67+
If you want to use auto-generation:
68+
69+
```bash
70+
dotnet tool install --global NSwag.ConsoleCore
71+
```
72+
73+
## Example: Adding a New Endpoint
74+
75+
### API Side
76+
77+
```csharp
78+
// Add new endpoint in BookEndpoints.cs
79+
app.MapGet("/api/books/{id}/reviews", GetBookReviews);
80+
```
81+
82+
### Client Side (Manual)
83+
84+
```csharp
85+
// Add to IBookStoreApi.cs
86+
[Get("/api/books/{id}/reviews")]
87+
Task<PagedListDto<ReviewDto>> GetBookReviews(
88+
Guid id,
89+
[Query] int page = 1,
90+
[Query] int pageSize = 20,
91+
CancellationToken cancellationToken = default);
92+
```
93+
94+
### Client Side (NSwag)
95+
96+
```bash
97+
# Just run the scripts
98+
./_tools/update-openapi.sh
99+
./_tools/generate-client-nswag.sh
100+
```
101+
102+
## Tips
103+
104+
- **Commit `openapi.json`** - Track API changes in git
105+
- **Review diffs** - Check what changed before committing
106+
- **Test after updates** - Ensure client still works
107+
- **Keep in sync** - Update client when API changes
108+
109+
## Current Approach
110+
111+
We use **manual updates** because:
112+
- ✅ Clean, minimal interface
113+
- ✅ Full control over method signatures
114+
- ✅ No build-time dependencies
115+
- ✅ Works in all environments
116+
117+
NSwag is available as an **optional tool** for quick regeneration when needed.

_tools/generate-client-nswag.sh

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22
# Generate API client using NSwag (supports OpenAPI 3.1)
3-
# Alternative to Refitter with better OpenAPI 3.1 support
3+
# Generates Refit interface in BookStore.Client project
44

55
set -e
66

@@ -16,11 +16,11 @@ if [ ! -f "openapi.json" ]; then
1616
fi
1717

1818
# Generate client
19-
echo "📝 Generating C# client..."
19+
echo "📝 Generating Refit interface..."
2020
nswag openapi2csclient \
2121
/input:openapi.json \
22-
/output:src/Web/BookStore.Web/Services/IBookStoreApi.cs \
23-
/namespace:BookStore.Web.Services \
22+
/output:src/Client/BookStore.Client/IBookStoreApi.cs \
23+
/namespace:BookStore.Client \
2424
/className:BookStoreApiClient \
2525
/generateClientInterfaces:true \
2626
/generateDtoTypes:false \
@@ -32,9 +32,10 @@ if [ $? -eq 0 ]; then
3232
echo "✅ Client generated successfully!"
3333
echo ""
3434
echo "📝 Next steps:"
35-
echo " 1. Review generated client: src/Web/BookStore.Web/Services/IBookStoreApi.cs"
35+
echo " 1. Review generated interface: src/Client/BookStore.Client/IBookStoreApi.cs"
3636
echo " 2. Build project: dotnet build"
37-
echo " 3. Commit changes: git add src/Web/BookStore.Web/Services/IBookStoreApi.cs"
37+
echo " 3. Commit changes: git add openapi.json src/Client/BookStore.Client/IBookStoreApi.cs"
38+
echo " 4. Commit message: git commit -m 'Update API client from OpenAPI spec'"
3839
echo ""
3940
else
4041
echo ""

_tools/update-openapi.sh

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ echo "✅ OpenAPI specification updated successfully!"
7272
echo ""
7373
echo "📝 Next steps:"
7474
echo " 1. Review changes: git diff openapi.json"
75-
echo " 2. Rebuild Web project: dotnet build src/Web/BookStore.Web"
76-
echo " 3. Verify client generated: ls -la src/Web/BookStore.Web/Services/IBookStoreApi.cs"
77-
echo " 4. Commit changes: git add openapi.json && git commit -m 'Update OpenAPI spec'"
75+
echo " 2. Update client interface: ./_tools/generate-client-nswag.sh (optional)"
76+
echo " 3. Or manually update: src/Client/BookStore.Client/IBookStoreApi.cs"
77+
echo " 4. Build project: dotnet build"
78+
echo " 5. Commit changes: git add openapi.json && git commit -m 'Update OpenAPI spec'"
7879
echo ""

docs/api-client-generation.md

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
This guide explains how to use the BookStore API client library.
44

5+
> [!NOTE]
6+
> **Build-Time Generation**: Fully automatic compile-time client generation is not currently possible because .NET 9's `Microsoft.Extensions.ApiDescription.Server` requires running the application (including database connections) to generate the OpenAPI spec. We use a runtime generation approach with manual updates or optional NSwag auto-generation from the saved `openapi.json` file.
7+
58
## Overview
69

710
The BookStore API client is provided as a reusable library (`BookStore.Client`) that can be used by any .NET project.
@@ -109,28 +112,88 @@ builder.Services
109112

110113
### When API Changes
111114

112-
1. **Update OpenAPI Spec**:
115+
**Workflow**:
116+
117+
1. **Start the API**:
118+
```bash
119+
aspire run
120+
```
121+
122+
2. **Update OpenAPI Spec**:
113123
```bash
114124
./_tools/update-openapi.sh
115125
```
116126

117-
2. **Update `IBookStoreApi.cs`** if endpoints changed:
118-
- Location: `src/Client/BookStore.Client/IBookStoreApi.cs`
127+
3. **Update Client Interface** (choose one):
128+
129+
**Option A: NSwag Auto-Generation** (optional):
130+
```bash
131+
./_tools/generate-client-nswag.sh
132+
```
133+
134+
**Option B: Manual Update**:
135+
- Edit `src/Client/BookStore.Client/IBookStoreApi.cs`
119136
- Add/update methods to match API changes
120137

121-
3. **Build and Test**:
138+
4. **Build and Test**:
122139
```bash
123140
dotnet build
124141
dotnet test
125142
```
126143

127-
4. **Commit Changes**:
144+
5. **Commit Changes**:
128145
```bash
129146
git add openapi.json src/Client/BookStore.Client/IBookStoreApi.cs
130147
git commit -m "Update API client: [description]"
131148
```
132149

133-
### Update Script
150+
### NSwag Auto-Generation (Optional)
151+
152+
NSwag can automatically generate the Refit interface from the OpenAPI spec.
153+
154+
**Install NSwag CLI**:
155+
```bash
156+
dotnet tool install --global NSwag.ConsoleCore
157+
```
158+
159+
**Pros**:
160+
- ✅ Automatic - generates entire interface
161+
- ✅ Consistent - always matches OpenAPI spec
162+
- ✅ Fast - one command
163+
164+
**Cons**:
165+
- ❌ Requires NSwag CLI installed
166+
- ❌ Generates full client implementation (not just Refit interface)
167+
- ❌ Generates DTOs (conflicts with `BookStore.Shared`)
168+
- ❌ Uses Newtonsoft.Json (we use System.Text.Json)
169+
- ❌ Needs manual cleanup and conversion to Refit
170+
171+
> [!NOTE]
172+
> NSwag's default output generates a complete client implementation with DTOs, which conflicts with our architecture where DTOs are in `BookStore.Shared`. Converting NSwag output to a clean Refit interface requires significant manual work, making the manual approach more practical for this project.
173+
174+
**Current Approach**: We use **manual updates** for clean, minimal code. NSwag is available as a reference tool to see what endpoints exist, but the generated code requires extensive modification.
175+
176+
### Why Not Build-Time Generation?
177+
178+
Build-time OpenAPI generation is not currently feasible because:
179+
180+
1. **`Microsoft.Extensions.ApiDescription.Server` runs the application** during build
181+
2. **Requires database connection** - The app needs PostgreSQL to start
182+
3. **Infrastructure dependencies** - Marten, Wolverine, and other services must initialize
183+
4. **CI/CD complexity** - Would need database available during builds
184+
185+
**Microsoft's Documentation** explicitly states:
186+
> "Build-time OpenAPI document generation functions by launching the app's entrypoint with a mock server implementation."
187+
188+
This means the entire application stack runs, including all service registrations and infrastructure dependencies.
189+
190+
**Our Solution**:
191+
- ✅ Generate OpenAPI at **runtime** from running API
192+
- ✅ Save `openapi.json` to git (tracks API changes)
193+
- ✅ Update client **manually** or with **NSwag** from saved spec
194+
- ✅ Simple, reliable, works in all environments
195+
196+
### Update Scripts
134197

135198
The `update-openapi.sh` script:
136199
- Auto-detects Aspire's dynamic ports

docs/architecture.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ Example event flow:
256256
- **PgAdmin** - Database management
257257
- **OpenTelemetry** - Distributed tracing
258258
- **Health Checks** - Service monitoring
259+
- **Roslyn Analyzers** - Custom analyzers for Event Sourcing/CQRS patterns ([docs](analyzer-rules.md))
260+
- **Roslynator.Analyzers** - Enhanced code analysis
261+
- **NSwag** - OpenAPI client generation (optional development tool)
259262

260263
## Key Design Decisions
261264

openapi.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"servers": [
1212
{
13-
"url": "http://localhost:61910/"
13+
"url": "http://localhost:56132/"
1414
}
1515
],
1616
"paths": {

0 commit comments

Comments
 (0)