|
| 1 | +# Frequently Asked Questions |
| 2 | + |
| 3 | +#### Where can I find documentation and examples? |
| 4 | +While the [documentation](~/usage/resources/index.md) covers basic features and a few runnable example projects are available [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples), |
| 5 | +many more advanced use cases are available as integration tests [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/test/JsonApiDotNetCoreTests/IntegrationTests), so be sure to check them out! |
| 6 | + |
| 7 | +#### Why can't I use OpenAPI? |
| 8 | +Due to the mismatch between the JSON:API structure and the shape of ASP.NET controller methods, this does not work out of the box. |
| 9 | +This is high on our agenda and we're steadily making progress, but it's quite complex and far from complete. |
| 10 | +See [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1046) for the current status, which includes instructions on trying out the latest build. |
| 11 | + |
| 12 | +#### What's available to implement a JSON:API client? |
| 13 | +It depends on the programming language used. There's an overwhelming list of client libraries at https://jsonapi.org/implementations/#client-libraries. |
| 14 | + |
| 15 | +The JSON object model inside JsonApiDotNetCore is tweaked for server-side handling (be tolerant at inputs and strict at outputs). |
| 16 | +While you technically *could* use our `JsonSerializer` converters from a .NET client application with some hacks, we don't recommend it. |
| 17 | +You'll need to build the resource graph on the client and rely on internal implementation details that are subject to change in future versions. |
| 18 | + |
| 19 | +In the long term, we'd like to solve this through OpenAPI, which enables the generation of a (statically typed) client library in various languages. |
| 20 | + |
| 21 | +#### How can I debug my API project? |
| 22 | +Due to auto-generated controllers, you may find it hard to determine where to put your breakpoints. |
| 23 | +In Visual Studio, controllers are accessible below **Solution Explorer > Project > Dependencies > Analyzers > JsonApiDotNetCore.SourceGenerators**. |
| 24 | + |
| 25 | +After turning on [Source Link](https://devblogs.microsoft.com/dotnet/improving-debug-time-productivity-with-source-link/#enabling-source-link) (which enables to download the JsonApiDotNetCore source code from GitHub), you can step into our source code and add breakpoints there too. |
| 26 | + |
| 27 | +Here are some key places in the execution pipeline to set a breakpoint: |
| 28 | +- `JsonApiRoutingConvention.Apply`: Controllers are registered here (executes once at startup) |
| 29 | +- `JsonApiMiddleware.InvokeAsync`: Content negotiation and `IJsonApiRequest` setup |
| 30 | +- `QueryStringReader.ReadAll`: Parses the query string parameters |
| 31 | +- `JsonApiReader.ReadAsync`: Parses the request body |
| 32 | +- `OperationsProcessor.ProcessAsync`: Entry point for handling atomic operations |
| 33 | +- `JsonApiResourceService`: Called by controllers, delegating to the repository layer |
| 34 | +- `EntityFrameworkCoreRepository.ApplyQueryLayer`: Builds the `IQueryable<>` that is offered to Entity Framework Core (which turns it into SQL) |
| 35 | +- `JsonApiWriter.WriteAsync`: Renders the response body |
| 36 | +- `ExceptionHandler.HandleException`: Interception point for thrown exceptions |
| 37 | + |
| 38 | +Aside from debugging, you can get more info by: |
| 39 | +- Including exception stack traces and incoming request bodies in error responses, as well as writing human-readable JSON: |
| 40 | + |
| 41 | + ```c# |
| 42 | + // Program.cs |
| 43 | + builder.Services.AddJsonApi<AppDbContext>(options => |
| 44 | + { |
| 45 | + options.IncludeExceptionStackTraceInErrors = true; |
| 46 | + options.IncludeRequestBodyInErrors = true; |
| 47 | + options.SerializerOptions.WriteIndented = true; |
| 48 | + }); |
| 49 | + ``` |
| 50 | +- Turning on verbose logging and logging of executed SQL statements, by adding the following to your `appsettings.Development.json`: |
| 51 | + |
| 52 | + ```json |
| 53 | + { |
| 54 | + "Logging": { |
| 55 | + "LogLevel": { |
| 56 | + "Default": "Warning", |
| 57 | + "Microsoft.EntityFrameworkCore.Database.Command": "Information", |
| 58 | + "JsonApiDotNetCore": "Verbose" |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + ``` |
| 63 | + |
| 64 | +#### What if my JSON:API resources do not exactly match the shape of my database tables? |
| 65 | +We often find users trying to write custom code to solve that. They usually get it wrong or incomplete, and it may not perform well. |
| 66 | +Or it simply fails because it cannot be translated to SQL. |
| 67 | +The good news is that there's an easier solution most of the time: configure Entity Framework Core mappings to do the work. |
| 68 | + |
| 69 | +For example, if your primary key column is named "CustomerId" instead of "Id": |
| 70 | +```c# |
| 71 | +builder.Entity<Customer>().Property(x => x.Id).HasColumnName("CustomerId"); |
| 72 | +``` |
| 73 | + |
| 74 | +It certainly pays off to read up on these capabilities at [Creating and Configuring a Model](https://learn.microsoft.com/en-us/ef/core/modeling/). |
| 75 | +Another great resource is [Learn Entity Framework Core](https://www.learnentityframeworkcore.com/configuration). |
| 76 | + |
| 77 | +#### Can I share my resource models with .NET Framework projects? |
| 78 | +Yes, you can. Put your model classes in a separate project that only references [JsonApiDotNetCore.Annotations](https://www.nuget.org/packages/JsonApiDotNetCore.Annotations/). |
| 79 | +This package contains just the JSON:API attributes and targets NetStandard 1.0, which makes it flexible to consume. |
| 80 | +At startup, use [Auto-discovery](~/usage/resource-graph.md#auto-discovery) and point it to your shared project. |
| 81 | + |
| 82 | +#### What's the best place to put my custom business/validation logic? |
| 83 | +For basic input validation, use the attributes from [ASP.NET ModelState Validation](https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?source=recommendations&view=aspnetcore-7.0#built-in-attributes) to get the best experience. |
| 84 | +JsonApiDotNetCore is aware of them and adjusts behavior accordingly. And it produces the best possible error responses. |
| 85 | + |
| 86 | +For non-trivial business rules that require custom code, the place to be is [Resource Definitions](~/usage/extensibility/resource-definitions.md). |
| 87 | +They provide a callback-based model where you can respond to everything going on. |
| 88 | +The great thing is that your callbacks are invoked for various endpoints. |
| 89 | +For example, the filter callback on Author executes at `GET /authors?filter=`, `GET /books/1/authors?filter=` and `GET /books?include=authors?filter[authors]=`. |
| 90 | +Likewise, the callbacks for changing relationships execute for POST/PATCH resource endpoints, as well as POST/PATCH/DELETE relationship endpoints. |
| 91 | + |
| 92 | +#### Can API users send multiple changes in a single request? |
| 93 | +Yes, just activate [atomic operations](~/usage/writing/bulk-batch-operations.md). |
| 94 | +It enables sending multiple changes in a batch request, which are executed in a database transaction. |
| 95 | +If something fails, all changes are rolled back. The error response indicates which operation failed. |
| 96 | + |
| 97 | +#### Is there any way to add `[Authorize(Roles = "...")]` to the generated controllers? |
| 98 | +Sure, this is possible. Simply add the attribute at the class level. |
| 99 | +See the docs on [Augmenting controllers](~/usage/extensibility/controllers.md#augmenting-controllers). |
| 100 | + |
| 101 | +#### How do I expose non-JSON:API endpoints? |
| 102 | +You can add your own controllers that do not derive from `(Base)JsonApiController` or `(Base)JsonApiOperationsController`. |
| 103 | +Whatever you do in those is completely ignored by JsonApiDotNetCore. |
| 104 | +This is useful if you want to add a few RPC-style endpoints or provide binary file uploads/downloads. |
| 105 | + |
| 106 | +A middle-ground approach is to add custom action methods to existing JSON:API controllers. |
| 107 | +While you can route them as you like, they must return JSON:API resources. |
| 108 | +And on error, a JSON:API error response is produced. |
| 109 | +This is useful if you want to stay in the JSON:API-compliant world, but need to expose something non-standard, for example: `GET /users/me`. |
| 110 | + |
| 111 | +#### How do I optimize for high scalability and prevent denial of service? |
| 112 | +Fortunately, JsonApiDotNetCore [scales pretty well](https://github.com/json-api-dotnet/PerformanceReports) under high load and/or large database tables. |
| 113 | +It never executes filtering, sorting, or pagination in-memory and tries pretty hard to produce the most efficient query possible. |
| 114 | +There are a few things to keep in mind, though: |
| 115 | +- Prevent users from executing slow queries by locking down [attribute capabilities](~/usage/resources/attributes.md#capabilities) and [relationship capabilities](~/usage/resources/relationships.md#capabilities). |
| 116 | + Ensure the right database indexes are in place for what you enable. |
| 117 | +- Prevent users from fetching lots of data by tweaking [maximum page size/number](~/usage/options.md#pagination) and [maximum include depth](~/usage/options.md#maximum-include-depth). |
| 118 | +- Avoid long-running transactions by tweaking `MaximumOperationsPerRequest` in options. |
| 119 | +- Tell your users to utilize [E-Tags](~/usage/caching.md) to reduce network traffic. |
| 120 | +- Not included in JsonApiDotNetCore: Apply general practices such as rate limiting, load balancing, authentication/authorization, blocking very large URLs/request bodies, etc. |
| 121 | + |
| 122 | +#### Can I offload requests to a background process? |
| 123 | +Yes, that's possible. Override controller methods to return `HTTP 202 Accepted`, with a `Location` HTTP header where users can retrieve the result. |
| 124 | +Your controller method needs to store the request state (URL, query string, and request body) in a queue, which your background process can read from. |
| 125 | +From within your background process job handler, reconstruct the request state, execute the appropriate `JsonApiResourceService` method and store the result. |
| 126 | +There's a basic example available at https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1144, which processes a captured query string. |
| 127 | + |
| 128 | +### What if I want to use something other than Entity Framework Core? |
| 129 | +This basically means you'll need to implement data access yourself. There are two approaches for interception: at the resource service level and at the repository level. |
| 130 | +Either way, you can use the built-in query string and request body parsing, as well as routing, error handling, and rendering of responses. |
| 131 | + |
| 132 | +Here are some injectable request-scoped types to be aware of: |
| 133 | +- `IJsonApiRequest`: This contains routing information, such as whether a primary, secondary, or relationship endpoint is being accessed. |
| 134 | +- `ITargetedFields`: Lists the attributes and relationships from an incoming POST/PATCH resource request. Any fields missing there should not be stored (partial updates). |
| 135 | +- `IEnumerable<IQueryConstraintProvider>`: Provides access to the parsed query string parameters. |
| 136 | +- `IEvaluatedIncludeCache`: This tells the response serializer which related resources to render, which you need to populate. |
| 137 | +- `ISparseFieldSetCache`: This tells the response serializer which fields to render in the attributes and relationship objects. You need to populate this as well. |
| 138 | + |
| 139 | +You may also want to inject the singletons `IJsonApiOptions` (which contains settings such as default page size) and `IResourceGraph` (the JSON:API model of resources and relationships). |
| 140 | + |
| 141 | +So, back to the topic of where to intercept. It helps to familiarize yourself with the [execution pipeline](~/internals/queries.md). |
| 142 | +Replacing at the service level is the simplest. But it means you'll need to read the parsed query string parameters and invoke |
| 143 | +all resource definition callbacks yourself. And you won't get change detection (HTTP 203 Not Modified). |
| 144 | +Take a look at [JsonApiResourceService](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs) to see what you're missing out on. |
| 145 | + |
| 146 | +You'll get a lot more out of the box if replacing at the repository level instead. You don't need to apply options, analyze query strings or populate caches for the serializer. |
| 147 | +And most resource definition callbacks are handled. |
| 148 | +That's because the built-in resource service translates all JSON:API aspects of the request into a database-agnostic data structure called `QueryLayer`. |
| 149 | +Now the hard part for you becomes reading that data structure and producing data access calls from that. |
| 150 | +If your data store provides a LINQ provider, you may reuse most of [QueryableBuilder](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryableBuilder.cs), |
| 151 | +which drives the translation into [System.Linq.Expressions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/). |
| 152 | +Note however, that it also produces calls to `.Include("")`, which is an Entity Framework Core-specific extension method, so you'll likely need to prevent that from happening. |
| 153 | +We use this for accessing [MongoDB](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/blob/674889e037334e3f376550178ce12d0842d7560c/src/JsonApiDotNetCore.MongoDb/Queries/Internal/QueryableBuilding/MongoQueryableBuilder.cs). |
| 154 | + |
| 155 | +> [!TIP] |
| 156 | +> [ExpressionTreeVisualizer](https://github.com/zspitz/ExpressionTreeVisualizer) is very helpful in trying to debug LINQ expression trees! |
| 157 | +
|
| 158 | +#### I love JsonApiDotNetCore! How can I support the team? |
| 159 | +The best way to express your gratitude is by starring our repository. |
| 160 | +This increases our leverage when asking for bug fixes in dependent projects, such as the .NET runtime and Entity Framework Core. |
| 161 | +Of course, a simple thank-you message in our [Gitter channel](https://gitter.im/json-api-dotnet-core/Lobby) is appreciated too! |
| 162 | +We don't take monetary contributions at the moment. |
| 163 | + |
| 164 | +If you'd like to do more: try things out, ask questions, create GitHub bug reports or feature requests, or upvote existing issues that are important to you. |
| 165 | +We welcome PRs, but keep in mind: The worst thing in the world is opening a PR that gets rejected after you've put a lot of effort into it. |
| 166 | +So for any non-trivial changes, please open an issue first to discuss your approach and ensure it fits the product vision. |
| 167 | + |
| 168 | +#### Is there anything else I should be aware of? |
| 169 | +See [Common Pitfalls](~/usage/common-pitfalls.md). |
0 commit comments