Skip to content

Commit f7e221e

Browse files
committed
Add FAQ
1 parent 714b09a commit f7e221e

File tree

3 files changed

+140
-5
lines changed

3 files changed

+140
-5
lines changed

Diff for: docs/getting-started/faq.md

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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+
#### How can I debug my API project?
8+
Due to auto-generated controllers, you may find it hard to determine where to put your breakpoints.
9+
In Visual Studio, controllers are accessible below **Solution Explorer > Project > Dependencies > Analyzers > JsonApiDotNetCore.SourceGenerators**.
10+
11+
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.
12+
13+
Here are some key places in the execution pipeline to set a breakpoint:
14+
- `JsonApiRoutingConvention.Apply`: Controllers are registered here (executes once at startup)
15+
- `JsonApiMiddleware.InvokeAsync`: Content negotiation and `IJsonApiRequest` setup
16+
- `QueryStringReader.ReadAll`: Parses the query string parameters
17+
- `JsonApiReader.ReadAsync`: Parses the request body
18+
- `OperationsProcessor.ProcessAsync`: Entry point for handling atomic operations
19+
- `JsonApiResourceService`: Called by controllers, delegating to the repository layer
20+
- `EntityFrameworkCoreRepository.ApplyQueryLayer`: Builds the `IQueryable<>` that is offered to Entity Framework Core (which turns it into SQL)
21+
- `JsonApiWriter.WriteAsync`: Renders the response body
22+
- `ExceptionHandler.HandleException`: Interception point for thrown exceptions
23+
24+
Aside from debugging, you can get more info by:
25+
- Including exception stack traces and incoming request bodies in error responses, as well as writing human-readable JSON:
26+
27+
```c#
28+
// Program.cs
29+
builder.Services.AddJsonApi<AppDbContext>(options =>
30+
{
31+
options.IncludeExceptionStackTraceInErrors = true;
32+
options.IncludeRequestBodyInErrors = true;
33+
options.SerializerOptions.WriteIndented = true;
34+
});
35+
```
36+
- Turning on verbose logging and logging of executed SQL statements, by adding the following to your `appsettings.Development.json`:
37+
38+
```json
39+
{
40+
"Logging": {
41+
"LogLevel": {
42+
"Default": "Warning",
43+
"Microsoft.EntityFrameworkCore.Database.Command": "Information",
44+
"JsonApiDotNetCore": "Verbose"
45+
}
46+
}
47+
}
48+
```
49+
50+
#### What if the resources my API exposes do not exactly match the shape of my database tables?
51+
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.
52+
Or it simply fails because it cannot be translated to SQL.
53+
The good news is that there's an easier solution most of the time: configure Entity Framework Core mappings to do the work.
54+
It certainly pays off to read up on its capabilities at [Creating and Configuring a Model](https://learn.microsoft.com/en-us/ef/core/modeling/).
55+
Another great resource is [Learn Entity Framework Core](https://www.learnentityframeworkcore.com/configuration).
56+
57+
#### Can I share my resource models with .NET Framework projects?
58+
Yes, you can. Put your model classes in a separate project that only references [JsonApiDotNetCore.Annotations](https://www.nuget.org/packages/JsonApiDotNetCore.Annotations/).
59+
This package contains just the JSON:API attributes and targets NetStandard 1.0, which makes it flexible to consume.
60+
At startup, use [Auto-discovery](~/usage/resource-graph.md#auto-discovery) and point it to your shared project.
61+
62+
#### What's the best place to put my custom business/validation logic?
63+
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.
64+
JsonApiDotNetCore is aware of them and adjusts behavior accordingly. And it produces the best possible error responses.
65+
66+
For non-trivial business rules that require custom code, the place to be is [Resource Definitions](~/usage/extensibility/resource-definitions.md).
67+
They provide a callback-based model where you can respond to everything going on.
68+
The great thing is that your callbacks are invoked for various endpoints.
69+
For example, the filter callback on Author executes at `GET /authors?filter=`, `GET /books/1/authors?filter=` and `GET /books?include=authors?filter[authors]=`.
70+
Likewise, the callbacks for changing relationships execute for POST/PATCH resource endpoints, as well as POST/PATCH/DELETE relationship endpoints.
71+
72+
#### Can API users send multiple changes in a single request?
73+
Yes, just activate [atomic operations](~/usage/writing/bulk-batch-operations.md).
74+
It enables to send multiple changes in a batch request, which are executed in a database transaction.
75+
If something fails, all changes are rolled back. The error response indicates which operation failed.
76+
77+
#### Is there any way to add `[Authorize(Roles = "...")]` to the generated controllers?
78+
Sure, this is possible. Simply add the attribute at the class level.
79+
See the docs on [Augmenting controllers](~/usage/extensibility/controllers.md#augmenting-controllers).
80+
81+
#### How do I expose non-JSON:API endpoints?
82+
You can add your own controllers that do not derive from `(Base)JsonApiController` or `(Base)JsonApiOperationsController`.
83+
Whatever you do in those is completely ignored by JsonApiDotNetCore.
84+
This is useful if you want to add a few RPC-style endpoints or provide binary file uploads/downloads.
85+
86+
A middle-ground approach is to add custom action methods to existing JSON:API controllers.
87+
While you can route them they way you like, they must return JSON:API resources.
88+
And on error, a JSON:API error response is produced.
89+
This is useful if you want to stay in the JSON:API-compliant world, but need to expose something on-standard, for example: `GET /users/me`.
90+
91+
#### How do I optimize for high scalability and prevent denial of service?
92+
Furtunately, JsonApiDotNetCore [scales pretty well](https://github.com/json-api-dotnet/PerformanceReports) under high load and/or large database tables.
93+
It never executes filtering, sorting or pagination in-memory and tries pretty hard to produce the most efficient query possible.
94+
There are a few things to keep in mind, though:
95+
- Prevent users from executing slow queries by locking down [attribute capabilities](~/usage/resources/attributes.md#capabilities) and [relationship capabilities](~/usage/resources/relationships.md#capabilities).
96+
Ensure the right database indexes are in place for what you enable.
97+
- 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).
98+
- Tell your users to utilize [E-Tags](~/usage/caching.md) to reduce network traffic.
99+
- Not included in JsonApiDotNetCore: Apply general practices such as rate limiting, load balancing, authentication/authorization, block very large URLs/request bodies etc.
100+
101+
#### Can I offload requests to a background process?
102+
Yes, that's possible. Override controller methods to return `HTTP 202 Accepted`, with a `Location` HTTP header where users can retrieve the result.
103+
Your controller method needs to store the request state (URL, query string and request body) in a qeueue, which your background process can read from.
104+
From within your background process job handler, reconstruct the request state, execute the appropriate `JsonApiResourceService` method and store the result.
105+
There's a basic example available at https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1144, which processes a captured query string.
106+
107+
#### What if I don't want to use Entity Framework Core?
108+
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.
109+
Either way, you can use the built-in query string and request body parsing, as well as routing, error handling and rendering of responses.
110+
111+
Here are some injectable request-scoped types to be aware of:
112+
- `IJsonApiRequest`: This contains routing information, such as whether a primary, secondary or relationship endpoint is being accessed.
113+
- `ITargetedFields`: Lists the attributes and relationships from an incoming POST/PATCH resource request. Any fields missing there should not be stored (partial updates).
114+
- `IEnumerable<IQueryConstraintProvider>`: Provides access to the parsed query string parameters.
115+
- `IEvaluatedIncludeCache`: This tells the response serializer which related resources to render, which you need to populate.
116+
- `ISparseFieldSetCache`: This tells the response serializer which fields to render in the attributes and relationship objects. You need to populate this as well.
117+
118+
You may also want to inject the singletons `IJsonApiOptions` (contains settings like default page size) and `IResourceGraph` (the JSON:API model of resources and relationships).
119+
120+
So, back to the topic of where to intercept. It helps to familiarize yourself with the [execution pipeline](~/internals/queries.md).
121+
Replacing at the service level is the simplest. But it means you'll need to read the parsed query string parameters and invoke
122+
all resource definition callbacks yourself. And you won't get change detection (HTTP 203 Not Modified).
123+
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.
124+
125+
You'll get a lot more out of the box if replacing at the repository level instead. There's no need to apply options, analyze query strings or populate caches for the serializer.
126+
And most resource definition callbacks are handled.
127+
That's because the built-in resource service translates all JSON:API aspects of the request into a database-agnostic data structure called `QueryLayer`.
128+
Now the hard part for you becomes reading that data structure and producing data access calls from that.
129+
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),
130+
which drives the translation into [System.Linq.Expressions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/).
131+
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.
132+
We use this for accessing [MongoDB](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/blob/674889e037334e3f376550178ce12d0842d7560c/src/JsonApiDotNetCore.MongoDb/Queries/Internal/QueryableBuilding/MongoQueryableBuilder.cs).
133+
134+
> [!TIP]
135+
> [ExpressionTreeVisualizer](https://github.com/zspitz/ExpressionTreeVisualizer) is very helpful in trying to debug LINQ expression trees!

Diff for: docs/getting-started/toc.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# [Installation](install.md)
2+
3+
# [Step By Step](step-by-step.md)
4+
5+
# [FAQ](faq.md)

Diff for: docs/getting-started/toc.yml

-5
This file was deleted.

0 commit comments

Comments
 (0)