This project can be used as a nice starting point for anyone wanting to implement a GraphQL server in Dotnet.
The project scaffolds pretty much every major feature you would need to get started with GraphQL in Dotnet.
Features:
- GraphQL web server using Hot Chocolate 13
- Authentication using Json Web Tokens
- Simple users / account setup for building upon
- Clean example folder structure with SoC
- Working integration tests project included, uncliding SuT and dependency injection and state sharing example
- Scalable query errors
- Scalable and by-the-book graphql validation and exception errors for mutations
- Fluent validation
- Stylecop linting
- Scoped transient database connection using connection pooling (Default is postgres, but you can switch driver)
- Async middleware example, for lazy fetching e.g. the current user in a request
- Example environment variables
- Resolve field example (vague in comment, see documentation, it's not hard)
- AutoMapper setup
- GraphQL voyager setup
- Data loader example
- Internationalization example
This project was developed using dotnet version 7.0.1
.
It proposes a starting point for a Hot Chocolate 13 GraphQL
server using the power of asp-net core
.
In these chpaters i will glance over what is important to understand to get building.
General overview of backend programming domain
JWT defines user identity and access validity through private/public key encryption. Pretty cool, no?
In program.cs
we intialize using .AddAuthorization()
in both ConfigureGraphQLServer
and RegisterApplicationServices
extension methods.
Furthermore, we sign using a secret using:
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetValue<string>("Authentication:Secret")!)),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
};
});
Then of course a RefreshTokenEntity
is generated in /Auth/Persistence/Entities
. This is used to refresh users' tokens.
Other than, the main auth code is as simple as possible and should be easily followed in /Auth/Services
Queries throw errors based on daomin exceptions.
Exceptions should extend the BaseException
class, and when they are thrown, they will be caught and translated
into a Query Error for the client to consume using the BaseExceptionMiddleware
defined in /Common/Domain/Exceptions
.
Mutations also throw errors based on domain exceptions.
However, they are handled differently.
We attach different kinds of errors dynamically. Just add e.g.
[Error(typeof(RefreshTokenExpiredException))]
to the mutation method, and the error will be attached to the mutation result if it is thrown.
This is a Hot Chocolate feature, and it is very powerful. Just call .AddMutationConventions(applyToAllMutations: true)
in ConfigureGraphQLServer
in Program.cs
to enable it.
It can be helpful to visualize it using graphql
voyager.
Also queries become quite complex. Which is a bit unfortunate.
Your queries will have to look like this to query the errors:
mutation Login($input: LoginInput!) {
login(input: $input) {
tokensModel {
accessToken
refreshToken
}
errors {
... on ValidationError {
message
errors {
propertyName
errorMessage
}
}
... on InvalidLoginError {
message
code
}
}
}
}
Integration tests are included, and there is an example of how to access common state, how to send requests, and a bunch of utils which you can extend on.