InternalProject is a small ASP.NET Core Web API for managing blog-style posts. The current implementation focuses on a clean layered structure, basic post lifecycle operations, SQLite persistence, and Swagger documentation.
The project currently implements:
- ASP.NET Core Web API with controller-based endpoints.
- Swagger/OpenAPI UI for testing the API in a browser.
- Entity Framework Core with SQLite as the persistence layer.
- Automatic SQLite database creation on application startup.
- Centralized NuGet package version management.
- A simple post domain model with lifecycle states.
- Version-based optimistic concurrency for post mutations.
- Repository interfaces for separating read and write data access.
- Application service layer that contains post use cases.
- Injectable system value provider for deterministic GUID/time usage.
- Global exception handling middleware with JSON error responses and server-side logging.
- Health check endpoints for application and database readiness.
- Console/debug logging configuration for clearer local diagnostics.
- Basic filtering and pagination for the post list endpoint.
- .NET 10
- ASP.NET Core
- Entity Framework Core
- SQLite
- Swashbuckle / Swagger
Package versions are managed centrally in Directory.Packages.props.
Project files should reference packages without inline Version attributes so
the API project and future test projects use the same dependency versions.
Directory.Packages.props
InternalProject/
Application/
ISystemValueProvider.cs
IPostReadRepository.cs
IPostWriteRepository.cs
PostMappingExtensions.cs
PostService.cs
Contracts/
CreatePostRequest.cs
ErrorResponse.cs
PostListItemResponse.cs
PostResponse.cs
PublishPostRequest.cs
UpdatePostRequest.cs
Controllers/
PostsController.cs
Data/
AppDbContext.cs
Domain/
ConcurrencyConflictException.cs
Post.cs
PostStatus.cs
Infrastructure/
EfPostRepository.cs
HealthChecks/
DatabaseHealthCheck.cs
HealthCheckResponseWriter.cs
SystemValueProvider.cs
Middleware/
ExceptionHandlingMiddleware.cs
Program.cs
appsettings.json
The main domain entity is Post.
A post contains:
IdTitleBodyAuthorIdStatusCreatedAtPublishedAtVersion
Supported post statuses:
DraftPublishedUnpublished
The current domain rules are:
- A post title is required.
- A post body is required.
- A post is created in the
Draftstatus. - Only the author can update, publish, or unpublish a post.
- A published post cannot be edited directly.
- A post cannot be published if its title or body is empty.
- Publishing a post sets
PublishedAtto the current UTC time. - Unpublishing a post changes its status to
Unpublished. - Update, publish, and unpublish operations require the caller's expected post version.
- If the expected version does not match the current version, the operation is rejected.
Base route:
/posts
POST /postsRequest body:
{
"title": "Post title",
"body": "Post body",
"authorId": "00000000-0000-0000-0000-000000000000"
}Successful response:
201 CreatedGET /posts/{id}Returns a single post by its identifier.
Possible responses:
200 OK404 Not Found
GET /postsSupported query parameters:
statusauthorIdpagepageSize
Example:
GET /posts?status=Published&page=1&pageSize=20Pagination behavior:
pagedefaults to1.pageSizedefaults to20.pageSizeis limited to100.
PATCH /posts/{id}Request body:
{
"title": "Updated title",
"body": "Updated body",
"requesterId": "00000000-0000-0000-0000-000000000000",
"expectedVersion": 1
}Possible responses:
200 OK400 Bad Request403 Forbidden404 Not Found409 Conflict
PATCH /posts/{id}/publishRequest body:
{
"requesterId": "00000000-0000-0000-0000-000000000000",
"expectedVersion": 1
}Possible responses:
200 OK400 Bad Request403 Forbidden404 Not Found409 Conflict
PATCH /posts/{id}/unpublishRequest body:
{
"requesterId": "00000000-0000-0000-0000-000000000000",
"expectedVersion": 1
}Possible responses:
200 OK400 Bad Request403 Forbidden404 Not Found409 Conflict
The API uses a global exception handling middleware.
Current mappings:
ArgumentException->400 Bad RequestInvalidOperationException->400 Bad RequestUnauthorizedAccessException->403 ForbiddenConcurrencyConflictException->409 Conflict- Any other exception ->
500 Internal Server Error
The middleware logs handled validation, authorization, concurrency, and unexpected errors internally while returning safe JSON messages to clients. Logging is configured to write application diagnostics to console and debug output.
Error response format:
{
"message": "Error message"
}The API exposes JSON health check endpoints:
GET /health/livechecks whether the application process is running.GET /health/readychecks whether dependencies required for handling traffic are available.GET /healthruns all configured checks.
Example response:
{
"status": "Healthy",
"totalDurationMilliseconds": 39.1584,
"checks": [
{
"name": "self",
"status": "Healthy",
"description": "Application is running.",
"durationMilliseconds": 0.0212
},
{
"name": "database",
"status": "Healthy",
"description": "Database connection is available.",
"durationMilliseconds": 37.6742
}
]
}The project uses SQLite.
The default connection string is configured in InternalProject/appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=internal-project.db"
}
}The database schema is created automatically on startup with
EnsureCreatedAsync(). No EF Core migrations are currently implemented.
Local SQLite files are ignored by git:
internal-project.db*
From the solution root:
dotnet restore
dotnet build InternalProject.sln
dotnet run --project InternalProject/InternalProject.csprojTo run on a specific URL:
dotnet run --project InternalProject/InternalProject.csproj --urls http://localhost:5265Swagger UI is available at:
http://localhost:5265/swagger
curl -X POST http://localhost:5265/posts \
-H "Content-Type: application/json" \
-d '{
"title": "First post",
"body": "Hello from the API",
"authorId": "11111111-1111-1111-1111-111111111111"
}'Example response:
{
"id": "5567dfa5-868a-43c3-8cab-b3f3f202e70a",
"title": "First post",
"body": "Hello from the API",
"authorId": "11111111-1111-1111-1111-111111111111",
"status": "Draft",
"createdAt": "2026-05-05T21:17:41.7595799Z",
"publishedAt": null,
"version": 1
}The following parts are not implemented yet:
- Authentication and authorization.
- User management.
- EF Core migrations.
- Automated tests.
- DTO validation attributes.
- Separate development and production database setup.
- External log aggregation.