Minimal Issue Tracker API
Build a REST API for tracking issues (bugs and tasks) in C# with ASP.NET Core. Issues have a title, description, status, priority, assignee and labels; the API supports creating and updating them, moving them through allowed statuses (open, in progress, resolved, closed), and listing them with filters, sorting and pagination. You'll first store issues in an in-memory repository to get the API right, then swap it for PostgreSQL without changing the endpoints, thanks to a repository interface. JSON is handled by System.Text.Json. You'll learn API design, input validation, status workflows, filtering and pagination, the repository pattern, PostgreSQL with Npgsql, and integration testing.
- Estimate
- ~13h
- Steps
- 7
- Completed by
- 0
- Proposed by
- codeseed.app
System.Text.Json · PostgreSQL
Project roadmap
- 01
Define the API
~1.5hWrite API.md: the issue fields, the allowed status changes (for example closed issues can only be reopened, not moved to in progress), and the endpoints: GET /issues (filters status, priority, assignee, label and q; sort; page and pageSize), GET /issues/{id}, POST, PATCH /issues/{id}, POST /issues/{id}/transitions and DELETE. Include the error format (ProblemDetails). Done when: API.md shows every endpoint with example requests and responses.
- 02
Build it with an in-memory repository
~2.5hCreate an ASP.NET Core project with Minimal APIs or controllers. Define an IIssueRepository interface and an in-memory implementation using a ConcurrentDictionary. Implement the endpoints with request and response DTOs, configuring System.Text.Json to write enums as strings. Return 201, 404 and 204 correctly. Done when: every endpoint works against the in-memory repository.
- 03
Validate and enforce the workflow
~1.5hValidate input (title 3 to 200 characters, priority from the allowed list, at most 10 labels) and return 400 with field errors. Enforce the status rules in one place (a small state machine), returning 409 for disallowed transitions. Record resolved at and closed at automatically. Done when: invalid input and invalid transitions get clear errors.
- 04
Add filtering and pagination
~1.5hImplement the filters, the sort options (created, updated or priority, ascending or descending) and pagination with a maximum page size of 100, returning the items plus totalCount, page and pageSize. Reject unknown sort fields with 400. Done when: combined filters, sorting and paging return the right issues.
- 05
Switch to PostgreSQL
~2.5hImplement IIssueRepository for PostgreSQL with Npgsql (with Dapper or EF Core), creating the tables with a migration, labels stored in a separate table or an array column, and indexes for the common filters. Build the filter query with parameters only. Choose the repository through configuration, so the endpoints don't change at all. Done when: the API behaves exactly the same with PostgreSQL as with the in-memory repository.
- 06
Handle concurrency and failures
~1.5hAdd an ETag based on a version number to GET responses and require If-Match on PATCH, returning 412 when the issue changed since it was read, so two people can't overwrite each other's edits. Return 503 when the database is unreachable, and add a health check. Done when: a stale update is refused with 412.
- 07
Write integration tests
~2hWrite xUnit tests with WebApplicationFactory that run the same test suite against both repositories (PostgreSQL through Testcontainers): CRUD, validation, transitions, filters and paging, and the ETag check. Done when: dotnet test passes for both repositories.
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack