{"id":25981496,"url":"https://github.com/mreshboboyev/cursor-pagination","last_synced_at":"2026-05-17T17:37:41.361Z","repository":{"id":279985570,"uuid":"940618263","full_name":"MrEshboboyev/cursor-pagination","owner":"MrEshboboyev","description":"Cursor Pagination for ASP.NET Core is a high-performance, production-ready pagination system for .NET 9 and PostgreSQL, designed for efficient data retrieval in large datasets. It features cursor-based pagination, optimized queries, bulk data operations, and a million-record test set, ensuring fast and scalable API responses.","archived":false,"fork":false,"pushed_at":"2025-07-08T14:32:02.000Z","size":30,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-31T15:37:19.227Z","etag":null,"topics":["asp-net-core","connection-pooling","cursor-pagination","database-optimization","ef-core","indexing","large-dataset","postgresql","scalability"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MrEshboboyev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-28T13:51:37.000Z","updated_at":"2025-07-08T14:32:05.000Z","dependencies_parsed_at":"2025-02-28T20:42:43.138Z","dependency_job_id":"f720c751-2b51-4ee9-8150-b5d5de0239b6","html_url":"https://github.com/MrEshboboyev/cursor-pagination","commit_stats":null,"previous_names":["mreshboboyev/cursor-pagination"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/MrEshboboyev/cursor-pagination","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MrEshboboyev%2Fcursor-pagination","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MrEshboboyev%2Fcursor-pagination/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MrEshboboyev%2Fcursor-pagination/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MrEshboboyev%2Fcursor-pagination/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MrEshboboyev","download_url":"https://codeload.github.com/MrEshboboyev/cursor-pagination/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MrEshboboyev%2Fcursor-pagination/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33147900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T09:28:26.183Z","status":"ssl_error","status_checked_at":"2026-05-17T09:27:52.702Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-core","connection-pooling","cursor-pagination","database-optimization","ef-core","indexing","large-dataset","postgresql","scalability"],"created_at":"2025-03-05T08:34:10.396Z","updated_at":"2026-05-17T17:37:41.327Z","avatar_url":"https://github.com/MrEshboboyev.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🚀 Cursor Pagination for ASP.NET Core\n\nA high-performance, production-ready implementation of cursor-based pagination for ASP.NET Core 9.0 with PostgreSQL.\n\n## ✨ Features\n\n- 📃 **Cursor-Based Pagination**: Efficient pagination for large datasets using cursor tokens\n- 🔍 **Optimized Queries**: Designed specifically for PostgreSQL performance\n- 🔄 **Bulk Data Operations**: Fast data seeding with Npgsql binary import\n- 📊 **Million Record Test Set**: Includes tools to generate and seed a million test records\n- 🛡️ **Type Safety**: Fully type-safe C# implementation with modern language features\n- 📝 **Clean API Design**: Minimal API endpoints with clear request/response models\n- 🔄 **Health Checks**: Built-in health check endpoint\n- 🧪 **Production Ready**: Includes retries, connection pooling, and error handling\n\n## 🔧 Technical Stack\n\n- ASP.NET Core 9.0\n- Entity Framework Core 9.0\n- PostgreSQL\n- Npgsql Provider\n- Minimal API pattern\n\n## 🏁 Getting Started\n\n### Prerequisites\n\n- .NET 9.0 SDK\n- PostgreSQL 15+ (running locally or in a container)\n\n### Configuration\n\nUpdate the connection string in `appsettings.json`:\n\n```json\n\"ConnectionStrings\": {\n  \"DefaultConnection\": \"Host=localhost;Port=5432;Database=CursorPagination_DB;Username=postgres;Password=postgres;Port=5432;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100;Connection Idle Lifetime=300\"\n}\n```\n\n### Running the Application\n\n```bash\ncd src/Cursor.Pagination\ndotnet run\n```\n\nOn first run, the application will:\n1. Create the database schema\n2. Seed 1,000 users\n3. Generate 1,000,000 notes records distributed among users\n4. Start the API server\n\n## 🌐 API Endpoints\n\n### Notes API\n\n| Method | Endpoint          | Description                               |\n|--------|-------------------|-------------------------------------------|\n| GET    | `/api/notes`      | Get paginated notes with cursor pagination |\n| GET    | `/api/notes/{id}` | Get a specific note by ID                 |\n| POST   | `/api/notes`      | Create a new note                         |\n| PUT    | `/api/notes/{id}` | Update an existing note                   |\n| DELETE | `/api/notes/{id}` | Delete a note                             |\n\n### Health Check\n\n| Method | Endpoint   | Description                     |\n|--------|------------|---------------------------------|\n| GET    | `/health`  | Check API and database health   |\n\n## 📦 Pagination Request Parameters\n\n```\nGET /api/notes?limit=10\u0026cursor=eyJDcmVhdGVkQXQiOiIyMDI0LTAzLTE3VDIzOjU5OjE1LjEyMzQ1NloiLCJJZCI6IjJiM2Y0NTlmLTlhZDEtNDU2Ny04MmY0LWZkMDdjZmVmYTZhZCJ9\n```\n\n| Parameter | Description                                      | Default |\n|-----------|--------------------------------------------------|---------|\n| `limit`   | Number of items per page (1-100)                 | 50      |\n| `cursor`  | Cursor token for pagination (from previous page) | null    |\n| `userId`  | Optional filter by user ID                       | null    |\n| `searchTerm` | Optional search by title or content           | null    |\n| `startDate` | Optional filter by creation date (min)         | null    |\n| `endDate` | Optional filter by creation date (max)           | null    |\n\n## 📊 Pagination Response Format\n\n```json\n{\n  \"items\": [\n    {\n      \"id\": \"2b3f459f-9ad1-4567-82f4-fd07cfefa6ad\",\n      \"userId\": \"a1b2c3d4-e5f6-4321-a9b8-c7d6e5f4a3b2\",\n      \"userName\": \"user42\",\n      \"title\": \"Note 42\",\n      \"content\": \"This is content for note 42. Created by user42.\",\n      \"createdAt\": \"2024-03-16T12:34:56Z\",\n      \"updatedAt\": \"2024-03-16T12:45:22Z\",\n      \"createdTimeAgo\": \"1 day ago\"\n    },\n    // ... more items\n  ],\n  \"nextCursor\": \"eyJDcmVhdGVkQXQiOiIyMDI0LTAzLTE2VDExOjIyOjMzLjQ1NjcxMloiLCJJZCI6ImU3ZDZmNWE0LWMzYjItNDgwNS05NWJlLTc2MTIzZDRlNWY2NyJ9\",\n  \"hasMore\": true\n}\n```\n\n## 🔍 How Cursor Pagination Works\n\n1. The client requests a page of data with a limit (e.g., 50 items)\n2. The server returns the items and a cursor token pointing to the next page\n3. The client uses the cursor token to request the next page\n4. The server uses the cursor to efficiently find the next set of results\n\nBenefits:\n- Consistent results even when data changes\n- No duplicate records or skipped items\n- Efficient for large datasets\n- Works well with database indexes\n\n## 🗃️ Database Schema\n\n### Users Table\n\n```sql\nCREATE TABLE users (\n    id UUID PRIMARY KEY,\n    username VARCHAR(50) NOT NULL UNIQUE,\n    email VARCHAR(100) NOT NULL UNIQUE,\n    created_at TIMESTAMP NOT NULL\n);\n```\n\n### Notes Table\n\n```sql\nCREATE TABLE user_notes (\n    id UUID PRIMARY KEY,\n    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n    title VARCHAR(200) NOT NULL,\n    content TEXT,\n    created_at TIMESTAMP NOT NULL,\n    updated_at TIMESTAMP NOT NULL,\n    CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id)\n);\n\nCREATE INDEX ix_user_notes_user_id ON user_notes(user_id);\nCREATE INDEX ix_user_notes_created_at_id ON user_notes(created_at, id);\n```\n\n## 🔧 Performance Optimizations\n\n- **Composite Index**: Optimized for cursor pagination with `(created_at, id)` index\n- **Bulk Import**: Uses Npgsql binary import for efficient data seeding\n- **Connection Pooling**: Configured for optimal connection reuse\n- **AsNoTracking**: Used for read-only queries to reduce memory usage\n- **Efficient Filtering**: Cursor conditions use indexes effectively\n\n## 📚 Key Code Components\n\n### Cursor Implementation\n\n```csharp\npublic class Cursor\n{\n    public DateTime CreatedAt { get; set; }\n    public Guid Id { get; set; }\n\n    // Convert cursor to string\n    public string Encode()\n    {\n        var json = JsonSerializer.Serialize(this);\n        return Convert.ToBase64String(Encoding.UTF8.GetBytes(json));\n    }\n\n    // Try to decode cursor from string\n    public static bool TryDecode(string? encodedCursor, out Cursor? result)\n    {\n        // Implementation...\n    }\n}\n```\n\n### Pagination Query\n\n```csharp\n// Apply cursor filtering if cursor is provided\nif (cursor != null)\n{\n    // This query is optimized for PostgreSQL and uses the composite index\n    query = query.Where(n =\u003e\n        n.CreatedAt \u003c cursor.CreatedAt ||\n        n.CreatedAt == cursor.CreatedAt \u0026\u0026 n.Id.CompareTo(cursor.Id) \u003e 0);\n}\n```\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmreshboboyev%2Fcursor-pagination","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmreshboboyev%2Fcursor-pagination","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmreshboboyev%2Fcursor-pagination/lists"}