{"id":26096275,"url":"https://github.com/juntossomosmais/ndjango.restframework","last_synced_at":"2025-04-12T11:35:26.640Z","repository":{"id":257661749,"uuid":"439088254","full_name":"juntossomosmais/NDjango.RestFramework","owner":"juntossomosmais","description":"Don't code a bunch of code to create CRUD applications. Use NDjango Rest Framework! It aims to provide a robust and flexible foundation for building RESTful APIs using ASP.NET Core.","archived":false,"fork":false,"pushed_at":"2024-10-28T23:05:53.000Z","size":176,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":18,"default_branch":"main","last_synced_at":"2025-03-21T11:02:53.454Z","etag":null,"topics":["api","crud","django","django-rest-framework"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/NDjango.RestFramework","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/juntossomosmais.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2021-12-16T18:23:26.000Z","updated_at":"2024-10-28T23:03:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"3f3fb494-555d-4579-8fb2-04a70b02f76a","html_url":"https://github.com/juntossomosmais/NDjango.RestFramework","commit_stats":null,"previous_names":["juntossomosmais/ndjango.restframework"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juntossomosmais%2FNDjango.RestFramework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juntossomosmais%2FNDjango.RestFramework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juntossomosmais%2FNDjango.RestFramework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juntossomosmais%2FNDjango.RestFramework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juntossomosmais","download_url":"https://codeload.github.com/juntossomosmais/NDjango.RestFramework/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248560746,"owners_count":21124714,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["api","crud","django","django-rest-framework"],"created_at":"2025-03-09T14:36:10.041Z","updated_at":"2025-04-12T11:35:26.618Z","avatar_url":"https://github.com/juntossomosmais.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NDjango.RestFramework\n\nNDjango Rest Framework makes you focus on business, not on boilerplate code. It's designed to follow the famous Django's slogan \"The web framework for perfectionists with deadlines.\" 🤺\n\nThis is a copy of the convention established by [Django REST framework](https://github.com/encode/django-rest-framework), though translated to C# and adapted to the .NET Core framework.\n\n## Quickstart with an example\n\nLet's create a CRUD API for a `Customer` entity with a `CustomerDocument` child entity.\n\n### Entity\n\nSome characteristics of the entities:\n\n- We should inherit from `BaseModel\u003cTPrimaryKey\u003e`.\n  - The `TPrimaryKey` is the type of the primary key. In this case, we are using `Guid`.\n- The `GetFields` method is mandatory. It informs which fields of the entity will be serialized in the API response.\n  - For fields of child or parent entities, we can use `:` to indicate them to be serialized as well. In this case, it is necessary to perform the `Include` with a filter.\n\n```csharp\npublic class Customer : BaseModel\u003cGuid\u003e\n{\n    public string Name { get; set; }\n    public string CNPJ { get; set; }\n    public int Age { get; set; }\n\n    public ICollection\u003cCustomerDocument\u003e CustomerDocument { get; set; }\n\n    public override string[] GetFields()\n    {\n        return new[] { \"Id\", \"Name\", \"CNPJ\", \"Age\", \"CustomerDocument\", \"CustomerDocument:DocumentType\", \"CustomerDocument:Document\" };\n    }\n}\n```\n\n### Entity Framework\n\nAdd the collection to the application's `DbContext`:\n\n```csharp\npublic class ApplicationDbContext : DbContext\n{\n    public DbSet\u003cCustomer\u003e Customer { get; set; }\n}\n```\n\n### DTO\n\nThe DTO is required to inherit from `BaseDto\u003cTPrimaryKey\u003e`, like the entity.\n\n```csharp\npublic class CustomerDto : BaseDto\u003cGuid\u003e\n{\n    public CustomerDto() { }\n\n    public string Name { get; set; }\n    public string CNPJ { get; set; }\n\n    public ICollection\u003cCustomerDocumentDto\u003e CustomerDocuments { get; set; }\n}\n```\n\n### Validation\n\nA validation is not mandatory, but it is recommended to ensure that the data is correct. The validation is done using the `FluentValidation` library.\n\n```csharp\npublic class CustomerDtoValidator : AbstractValidator\u003cCustomerDto\u003e\n{\n    public CustomerDtoValidator(IHttpContextAccessor context)\n    {\n        RuleFor(m =\u003e m.Name)\n            .MinimumLength(3)\n            .WithMessage(\"Name should have at least 3 characters\");\n\n        if (context.HttpContext.Request.Method == HttpMethods.Post)\n            RuleFor(m =\u003e m.CNPJ)\n                .NotEqual(\"567\")\n                .WithMessage(\"CNPJ cannot be 567\");\n    }\n}\n```\n\n### Include child/parent entities\n\nPreviously, we included the `CustomerDocument` entity in the `Customer` entity. Check out the `GetFields` method in the `Customer` entity.\n\n```csharp\npublic class CustomerDocumentIncludeFilter : Filter\u003cCustomer\u003e\n{\n    public override IQueryable\u003cCustomer\u003e AddFilter(IQueryable\u003cCustomer\u003e query, HttpRequest request)\n    {\n        return query.Include(x =\u003e x.CustomerDocument);\n    }\n}\n```\n\n### Controller\n\nThe CRUD API is created by inheriting from the `BaseController` and passing the necessary parameters. Note how `AllowedFields` and `Filters` are set.\n\n```csharp\n[Route(\"api/[controller]\")]\n[ApiController]\npublic class CustomersController : BaseController\u003cCustomerDto, Customer, Guid, ApplicationDbContext\u003e\n{\n    public CustomersController(\n        CustomerSerializer serializer,\n        ApplicationDbContext dbContext,\n        ILogger\u003cCustomer\u003e logger)\n        : base(\n                serializer,\n                dbContext,\n                logger)\n    {\n        AllowedFields = new[] {\n            nameof(Customer.Id),\n            nameof(Customer.Name),\n            nameof(Customer.CNPJ),\n            nameof(Customer.Age),\n        };\n\n        Filters.Add(new QueryStringFilter\u003cCustomer\u003e(AllowedFields));\n        Filters.Add(new QueryStringSearchFilter\u003cCustomer\u003e(AllowedFields));\n        Filters.Add(new QueryStringIdRangeFilter\u003cCustomer, Guid\u003e());\n        Filters.Add(new CustomerDocumentIncludeFilter());\n    }\n}\n```\n\n## API Guide\n\n### Sorting\n\nIn the `ListPaged` method, we use the query parameters `sort` or `sortDesc` to sort by a field. If not specified, we will always use the entity's `Id` field for ascending sorting.\n\n### Filters\n\nFilters are mechanisms applied whenever we try to retrieve entity data in the `GetSingle` and `ListPaged` methods.\n\n#### QueryStringFilter\n\nThe `QueryStringFilter`, perhaps the most relevant, is a filter that matches the fields passed in the query parameters with the fields of the entity whose filter is allowed. All filters are created using the equals (`==`) operator.\n\n#### QueryStringIdRangeFilter\n\nThe `QueryStringIdRangeFilter` goal is to filter the entities by `Id` based on all the `ids` provided in the query parameters.\n\n#### QueryStringSearchFilter\n\nThe `QueryStringSearchFilter` is a filter that allows a `search` parameter to be provided in the query parameters to search, through a single input, in several fields of the entity, even performing `LIKE` on strings.\n\n#### Implementing a filter\n\nGiven an `IQueryable\u003cT\u003e` and an `HttpRequest`, you can implement the filter as you prefer. Just inherit from the base class and add it to your controller:\n\n```csharp\npublic class MyFilter : AspNetCore.RestFramework.Core.Filters.Filter\u003cSeller\u003e\n{\n    private readonly string _forbiddenName;\n\n    public MyFilter(string forbiddenName)\n    {\n        _forbiddenName = forbiddenName;\n    }\n\n    public IQueryable\u003cTEntity\u003e AddFilter(IQueryable\u003cTEntity\u003e query, HttpRequest request)\n    {\n        return query.Where(m =\u003e m.Name != forbiddenName);\n    }\n}\n```\n\n```csharp\npublic class SellerController\n{\n    public SellerController(...)\n        : base(...)\n    {\n        Filters.Add(new MyFilter(\"Example\"));\n    }\n}\n```\n\n### Paginations\n\nBy default, the `BaseController` uses the class `PageNumberPagination`. [It behaves the same as DRF's `PageNumberPagination`](https://www.django-rest-framework.org/api-guide/pagination/#pagenumberpagination). Sample response:\n\n```json\n{\n  \"count\": 13,\n  \"next\": \"http://localhost:8000/api/v1/Persons?page=3\u0026page_size=5\",\n  \"previous\": \"http://localhost:8000/api/v1/Persons?page=1\u0026page_size=5\",\n  \"results\": [\n    {\n      \"name\": \"Sal Paradise\",\n      \"createdAt\": \"2024-10-19T19:22:12.0524797\",\n      \"id\": 6\n    },\n    {\n      \"name\": \"Odulor\",\n      \"createdAt\": \"2024-10-19T19:22:15.4483365\",\n      \"id\": 7\n    },\n    {\n      \"name\": \"Iago\",\n      \"createdAt\": \"2024-10-19T19:22:18.1077698\",\n      \"id\": 8\n    },\n    {\n      \"name\": \"Jafar\",\n      \"createdAt\": \"2024-10-19T19:22:21.5425118\",\n      \"id\": 9\n    },\n    {\n      \"name\": \"Wig\",\n      \"createdAt\": \"2024-10-19T19:22:23.9046811\",\n      \"id\": 10\n    }\n  ]\n}\n```\n\n### Errors\n\nThe `ValidationErrors` and `UnexpectedError` might be returned in the `BaseController` in case of validation errors or other exceptions.\n\n### Validation\n\nImplement validators for the DTOs and configure your application with the extension `ModelStateValidationExtensions.ConfigureValidationResponseFormat` to ensure that in case of the `ModelState` being invalid, a `ValidationErrors` is returned. It might be necessary to add the `HttpContext` accessor to the services. Check the example below:\n\n```csharp\nservices.AddControllers()\n    // ...\n    // At the end of AddControllers, add the following:\n    .AddModelValidationAsyncActionFilter(options =\u003e\n    {\n        options.OnlyApiController = true;\n    })\n    // ModelStateValidationExtensions\n    .ConfigureValidationResponseFormat();\n// ...\nservices.AddHttpContextAccessor();\n```\n\n### Serializer\n\n`Serializer` is a mechanism used by the `BaseController`. Each controller has its own serializer. The serializer's methods can be overridden to add additional or different logic for specific entities. It works more or less similar to the [Django REST framework's serializers](https://www.django-rest-framework.org/api-guide/serializers/).\n\n### Glossary\n\n| Term           | Description                                                 |\n|----------------|-------------------------------------------------------------|\n| `TPrimaryKey`  | Type of the primary key of an entity, usually `Guid`.       |\n| `TEntity`      | Type of the entity we are talking about in a generic class. |\n| `TOrigin`      | In the `BaseController`, it is the same as `TEntity`.       |\n| `TDestination` | Type of the DTO.                                            |\n| `TContext`     | Type of the Entity Framework context.                       |\n\n## Notice\n\nThis project is still in the early stages of development. We recommend that you do not use it in production environments and check the written tests to understand the current functionality.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuntossomosmais%2Fndjango.restframework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuntossomosmais%2Fndjango.restframework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuntossomosmais%2Fndjango.restframework/lists"}