Scribbly.Stencil
0.2.0-SCRB11-dependency-injection.2
See the version list below for details.
dotnet add package Scribbly.Stencil --version 0.2.0-SCRB11-dependency-injection.2
NuGet\Install-Package Scribbly.Stencil -Version 0.2.0-SCRB11-dependency-injection.2
<PackageReference Include="Scribbly.Stencil" Version="0.2.0-SCRB11-dependency-injection.2" />
<PackageVersion Include="Scribbly.Stencil" Version="0.2.0-SCRB11-dependency-injection.2" />
<PackageReference Include="Scribbly.Stencil" />
paket add Scribbly.Stencil --version 0.2.0-SCRB11-dependency-injection.2
#r "nuget: Scribbly.Stencil, 0.2.0-SCRB11-dependency-injection.2"
#:package Scribbly.Stencil@0.2.0-SCRB11-dependency-injection.2
#addin nuget:?package=Scribbly.Stencil&version=0.2.0-SCRB11-dependency-injection.2&prerelease
#tool nuget:?package=Scribbly.Stencil&version=0.2.0-SCRB11-dependency-injection.2&prerelease
Scribbly Stencil
A framework for organizing and generating minimal API endpoints.
Table of Contents
Example
Below is a brief snip of code to get you started before reading more.
- Add a reference to the
Scribbly.Stencil
package and annotate a static method on a partial class.
[EndpointGroup("/lunch")]
public partial class LunchGroup
{
// Annotate any method signature with the VerbEndpoint attribute
[GetEndpoint("/{id}", "Get Lunch", "Fetches the current lunch for the provided restaurant ID.")]
private static IResult GetLunchMenu(HttpContext context, string id)
{
return Results.Ok(id);
}
[PostEndpoint("/{id}", "Get Lunch", "Fetches the current lunch for the provided restaurant ID.")]
private static IResult PostLunchMenu(HttpContext context, string id, LunchItem request)
{
return Results.Ok(request);
}
}
Scribbly.Stencil will generate a few things for you behind the Scenes
- An HTTP Endpoint Registration Method
public static IEndpointRouteBuilder MapLunchGroupGetLunchMenu(this IEndpointRouteBuilder builder)
{
builder.MapPost("/{id}", PostLunchMenu);
return builder;
}
- And an Endpoint Registry for All Endpoints Mapping endpoints to Groups
public static IEndpointRouteBuilder MapScribblyEndpoints(this IEndpointRouteBuilder builder)
{
builder.MapLunchGroupGetLunchMenu();
builder.MapLunchGroupPostLunchMenu();
return builder;
}
- Simply Map Your Application
app.MapStencilApp();
Optionally Add a Group or Route Prefix
app.MapStencilApp("/api");
🎯 Endpoints
With Scribbly.Stencil
endpoints are declared as a static method and automatically mapped to an HTTP request.
The generator will accept several type configurations however the enclosing class MUST be a public partial class
.
The method signature MAY be private or public, but MUST be static.
Note: We are enforcing the use of static
members to ensure your endpoints don't have a closure from the enclosing class.
public partial class BreakfastEndpoints
{
[PutEndpoint("/breakfast/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
private static object PutBreakfastMenu(HttpContext context, [FromRoute] string id)
{
return new { id = id };
}
}
Any method signature supported by a Minimal API Endpoints is support by Scribbly.Stencil
public partial class BreakfastEndpoints
{
[PutEndpoint("/breakfast/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
private static Task<IResult> PutBreakfastMenu([FromQuery] string id)
{
}
[PutEndpoint("/breakfast/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
private static Task<string> PutBreakfastMenu([FromServices] IService service)
{
}
}
The code generator will kick into action when it finds a method annotated with any of the EndpointAttributes
once our APIs are stable support for more HTTP verbs will be added.
Types can declare multiple endpoints
public partial class BreakfastEndpoints
{
[GetEndpoint("/breakfast/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
private static object GetBreakfastMenu(HttpContext context, [FromRoute] string id)
{
}
[PutEndpoint("/breakfast/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
private static object PutBreakfastMenu(HttpContext context, [FromRoute] string id)
{
}
}
Endpoints can also be grouped together with a parent IEndpointRouteBuilder
see groups below for details
[EndpointGroup("/breakfast", "Manage Breakfast Menu")]
public partial class BreakfastGroup
{
}
[GroupMember<BreakfastGroup>]
public partial class BreakfastEndpoints
{
[GetEndpoint("/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
private static object GetBreakfastMenu(HttpContext context, [FromRoute] string id)
{
}
[PutEndpoint("/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
private static object PutBreakfastMenu(HttpContext context, [FromRoute] string id)
{
}
}
Group membership is Type Safe as the Generic Parameter can ONLY accept a Scribbly.Stencil.IGroup
interface.
Membership can be added to endpoints at either the method declaration
or the class declartion
.
[EndpointGroup("/breakfast", "Manage Breakfast Menu")]
public partial class BreakfastGroup
{
}
[EndpointGroup("/menu", "Manage Breakfast Menu")]
public partial class MenuGroup
{
}
[GroupMember<BreakfastGroup>]
public partial class BreakfastEndpoints
{
[GetEndpoint("/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
private static object GetBreakfastMenu(HttpContext context, [FromRoute] string id)
{
}
[PutEndpoint("/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
[GroupMember<MenuGroup>]
private static object PutBreakfastMenu(HttpContext context, [FromRoute] string id)
{
}
}
in the example above the PutBreakfastMenu would be added the MenuGroup
with a route /menu NOT /breakfast
Configuration
Stencil
allows you to easily plug into the configuration for each endpoint generated by your Methods.
Simply add the Configure
attribute to the method declaration
or class declaration
and magic 🐣
Your type will now implement a custom interface used by the code generator. This tells Scribbly.Stencil
to invoke your configure method allowing you to modify and append the builder. This interface forces the compiler to guid you.
public partial class BreakfastEndpoints
{
[PutEndpoint("/{id}", "Edit Breakfast", "Edits a Breakfast Item")]
[Configure]
private static object PutBreakfastMenu(HttpContext context, [FromRoute] string id)
{
return new { id = id };
}
/// <inheritdoc />
public void ConfigurePutBreakfastMenu(IEndpointConventionBuilder putBreakfastMenuBuilder)
{
putBreakfastMenuBuilder.ProducesProblem(404);
}
}
🛒 Groups
Endpoint groups can be created to map several endpoints with common routing and configuration. Scribbly.Stencil
will build this routing tree for you behind the scenes. Just create a Group and attached members.
[EndpointGroup("/lunch", "Manage Lunch Menu")]
public partial class LunchGroup
{
}
This will create a routing group. Groups can even be members of other groups. Use the GroupMember<T>
to target a group. Your new group will be added to the group specified in the Generic Param.
[EndpointGroup("/menu", "Menu")]
public partial class MenuGroup
{
}
[GroupMember<MenuGroup>]
[EndpointGroup("/lunch", "Manage Lunch Menu")]
public partial class LunchGroup
{
}
Of course you'll need to add endpoints to groups as well yielding a routable endpoint /menu/lunch/{id}
public partial class LunchEndpoints
{
[GetEndpoint("lunch/{id}")]
[GroupMember<LunchGroup>]
private static IResult GetLunchMenu(HttpContext context, [FromRoute] string id)
{
return Results.Ok(id);
}
}
Using Scribbly.Stencil
you can also contain endpoints inside of groups. Each endpoint will inherit the behavior of the encapsulating class.
Since the DinnerEndpoints
in the following example and annotated with the EndpointGroup
and the GroupMember<MenuGroup>
the endpoints will yield routes /menu/dinner/{id}
[EndpointGroup("/dinner", "Dinner time endpoints wrapped in a Group.")]
[GroupMember<MenuGroup>]
public partial class DinnerEndpoints
{
[GetEndpoint("/{id}", "Gets Dinner", "Queries a new Dinner Item")]
private static IResult GetDinnerMenu(HttpContext context, [FromRoute] string id)
{
return Results.Ok(id);
}
[PostEndpoint("/{id}", "Create Dinner", "Creates a new Dinner Item")]
private static object PostDinnerMenu(HttpContext context, [FromRoute] string id)
{
return Results.Ok(id);
}
[PutEndpoint("/{id}", "Edit Dinner", "Edits a Dinner Item")]
private static object PutDinnerMenu(HttpContext context, [FromRoute] string id)
{
return new { id = id };
}
}
Configuration
While this is great you may need to plug into the route building and override or append configuration to groups.
Simply add the Configure
attribute to the class declaration
and magic 🐣
Your type will now implement a custom interface used by the code generator. This tells Scribbly.Stencil
to invoke your configure method allowing you to modify and append the builder.
[Configure]
[GroupMember<ApplicationRoot>]
[EndpointGroup("/lunch", "Manage Lunch Menu")]
public partial class LunchGroup
{
/// <inheritdoc />
public void Configure(IEndpointConventionBuilder lunchGroupBuilder)
{
applicationRootBuilder.AddEndpointFilter<MyFilter>();
// .....
}
}
💉 Dependency Injection
Scribbly.Stencil
handlers and groups optionally support Dependency Injection using Microsoft.Extensions.DependencyInjection
.
To utilize dependencies from without your Groups and Handlers configuration callbacks simply invoke the AddStencil
method
builder.Services.AddStencil();
Optional declare a service lifetime for the handlers. This can help use scoped services out of the box.
builder.Services.AddStencil(options =>
{
options.ServicesScope = ServiceLifetime.Scoped;
});
Once registered your groups and handler classes will be resolved from the DI container and services MAY be injected.
public class MyOptions
{
public string[] Tags { get; set; } = ["Menu"];
}
[EndpointGroup("/menu", "Manage Menu Items")]
[Configure]
public partial class MenuGroup(IOptions<MyOptions> options)
{
/// <inheritdoc />
public void Configure(IEndpointConventionBuilder applicationRootBuilder)
{
applicationRootBuilder.WithTags(options.Value.Tags);
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
0.3.2 | 207 | 8/18/2025 |
0.3.1 | 118 | 8/18/2025 |
0.3.1-27-generator-emits-op... | 112 | 8/17/2025 |
0.3.1-27-generator-emits-op... | 114 | 8/17/2025 |
0.3.1-22-move-generated-int... | 124 | 8/13/2025 |
0.3.1-19-removed-string-tem... | 106 | 8/18/2025 |
0.3.0 | 126 | 8/12/2025 |
0.3.0-20-SCRB-configuration... | 112 | 8/12/2025 |
0.2.0 | 122 | 8/11/2025 |
0.2.0-SCRB11-dependency-inj... | 107 | 8/11/2025 |
0.2.0-SCRB11-dependency-inj... | 108 | 8/11/2025 |
0.1.4 | 110 | 8/9/2025 |
0.1.4-7-combined-projects-i... | 99 | 8/9/2025 |
0.1.4-7-combined-projects-i... | 102 | 8/9/2025 |
0.1.3 | 203 | 8/6/2025 |
0.1.2 | 208 | 8/6/2025 |
0.1.1 | 193 | 8/5/2025 |
0.1.0 | 115 | 8/4/2025 |