Skip to content

Repository files navigation

@massivescale/tsp-aspnetcore-api

A TypeSpec emitter that generates C# model classes, interfaces, enums, ASP.NET Core controllers, service interfaces, and FluentValidation validators from TypeSpec definitions.

For each TypeSpec model the emitter produces a public partial class <Name> — except a base model carrying @discriminator, which is emitted abstract since it has no discriminator value of its own (see Model Generation). An optional companion public partial interface I<Name> can be enabled with emit-interfaces: true.

TypeSpec enum declarations and named string-literal union types become C# enums with [JsonConverter(typeof(EnumMemberConverterFactory))] and optional [EnumMember(Value = "...")] attributes.

When HTTP operations are present the emitter generates ASP.NET Core controllers and service interfaces. Controllers are emitted as abstract classes, and service interfaces are emitted as partial to enable extension and composition. PATCH operations receive a MergePatch<T> body (RFC 7396 JSON Merge Patch semantics) rather than a distinct update model — the same entity class is used for both POST and PATCH. MergePatch<T> exposes Patch(T) / PatchAsync(T) for one-call entity updates, plus IsDefined / IsNull / TryGetValue helpers for fine-grained field-level control. FluentValidation validators enforce which fields are writable per operation. Each output type can be disabled independently via the emit-* options.


Install

npm install --save-dev @massivescale/tsp-aspnetcore-api

Quick start

Add the emitter to tspconfig.yaml and run the compiler:

emit:
  - "@massivescale/tsp-aspnetcore-api"

options:
  "@massivescale/tsp-aspnetcore-api":
    root-namespace: MyCompany.Api
npx tsp compile .

With root-namespace: MyCompany.Api and the TypeSpec below, the emitter writes Users/User.g.cs:

@doc("A registered user")
namespace MyCompany.Api.Users;

model User {
  @format("uuid") id: string;
  name: string;
  active: boolean;
  joined?: utcDateTime;
}
// Users/User.g.cs
// <auto-generated/>
#nullable enable

using System;
using System.Collections.Generic;

namespace MyCompany.Api.Users
{
    /// <summary>
    /// A registered user
    /// </summary>
    public partial class User
    {
        public Guid? Id { get; set; }
        public string? Name { get; set; }
        public bool? Active { get; set; }
        public DateTimeOffset? Joined { get; set; }
    }
}

Options

Option Type Default Description
abstract-suffix string "Base" Suffix appended to generated abstract controller class names, e.g. UsersControllerBase.
additional-usings string[] [] Extra using directives added to every generated file.
cancellation-token boolean true When true, adds CancellationToken cancellationToken to every controller action and service method, and emits using System.Threading; in controller and service files.
clean-output-dirs boolean true When true, previously-generated files are removed from every output directory before emitting, so renamed or deleted types leave no stale files behind. Only files that match file-extension and carry the <auto-generated> marker are deleted; extension-matching files without the marker are left in place with a warning, and other files are ignored. See Output cleaning.
controllers-namespace string <root>.Controllers Verbatim C# namespace for all generated controller files. When unset, defaults to <root-namespace>.Controllers.
controllers-output-dir string "Controllers" Destination for generated controller files.
emit-controllers boolean true When false, no controller base class files are emitted.
emit-helpers boolean false When false, EnumMemberConverter.g.cs is not emitted. MergePatch.g.cs is always emitted unconditionally when merge-patch-style is "generic" (it is required by PATCH operations). Has no effect on "typed" style.
merge-patch-style "generic" | "typed" "generic" Controls how the RFC 7396 Merge Patch support class is emitted. "generic" emits a single shared MergePatch<T> helper in the helpers directory. "typed" emits a concrete {Model}MergePatch class per entity in the models directory. Both expose the same API surface. See RFC 7396 Merge Patch.
emit-interfaces boolean false When true, a companion I<Model> interface file is emitted for every model class.
emit-services boolean true When false, no service interface files are emitted.
emit-validators boolean false When true, FluentValidation validator classes are generated for models that appear as POST or PATCH request bodies. Discriminator fields from @discriminator hierarchies are excluded from validation rules. See FluentValidation validators.
file-extension string ".g.cs" File extension for all generated files.
helpers-namespace string <root>.Helpers Verbatim C# namespace for all generated helper files. When unset, defaults to <root-namespace>.Helpers.
helpers-output-dir string "Helpers" Destination for generated helper files (MergePatch<T>, EnumMemberConverter).
interfaces-output-dir string "Models" Destination for generated interface files.
models-namespace string <root>.Models Verbatim C# namespace for all generated model, interface, and enum files. Interfaces always share this namespace. When unset, defaults to <root-namespace>.Models.
models-output-dir string "Models" Destination for generated class and enum files. Relative paths resolve against emitter-output-dir.
namespace-from-path boolean false Controls file placement only — does not affect C# namespaces. When true, files go flat in their output directory. When false (default), files are placed in subdirectories derived from the TypeSpec namespace. See Namespace resolution.
namespace-map Record<string, string> {} Rewrites TypeSpec namespace names for file placement (folder path computation). Does not affect verbatim C# namespaces. Longest-prefix match wins; sub-namespaces inherit the rewrite automatically.
nullable-properties boolean true When true, all properties are emitted as nullable (string?, int?). When false, only TypeSpec-optional properties and T | null unions are nullable.
root-namespace string (inferred) Root C# namespace. Used as the prefix for all default section namespaces. Stripped from folder paths when namespace-from-path is false. When omitted, inferred from the TypeSpec namespace tree.
route-prefix string "api/{version}" Prefix prepended to every controller route. The {version} token is replaced with the API version value (e.g. v1.0) for versioned services, and stripped for unversioned services. E.g. "api/{version}" + v1.0/api/v1.0/users.
services-namespace string <root>.Services Verbatim C# namespace for all generated service interface files. When unset, defaults to <root-namespace>.Services.
services-output-dir string "Services" Destination for generated service interface files.
templates Record<string, string> {} Custom Handlebars template paths keyed by template name. See Custom templates.
validators "post" | "patch" | "both" "both" Controls which validator type(s) are emitted per model. "post" emits {Model}Validator.g.cs; "patch" emits {Model}PatchValidator.g.cs; "both" emits both.
validators-namespace string <root>.Validators Verbatim C# namespace for all generated validator files. When unset, defaults to <root-namespace>.Validators.
validators-output-dir string "Validators" Output directory for generated validator and initializer files.
validators-version-strategy "earliest" | "latest" | "per-version" | "version-aware" (auto) Controls how versioning affects validator generation. Auto-detected: "version-aware" when @versioned is present, "earliest" otherwise. See Version strategies.

Documentation

  • Type Mapping — TypeSpec-to-C# scalar and collection mappings, @format overrides, @encode encodings
  • Decorators — Emitter-specific decorators such as @serverName
  • Model Generation — Default property values, enums, @discriminator polymorphism, cross-namespace references
  • Namespace Resolution — How C# namespaces are derived from output paths and TypeSpec namespaces
  • Controllers and Services — ASP.NET Core controller and service interface generation
  • RFC 7396 Merge PatchMergePatch<T> generic container, PATCH body generation, and validator integration
  • FluentValidation Validators — Validator generation, constraint mapping, version strategies
  • Custom Templates — Handlebars template overrides and view model reference
  • Output Cleaning — Removing stale generated files before emit, and its guardrails
  • Development — Build, test, and project structure

About

A simple, but opinionated, TypeSpec emitter for building ASP.NET Core APIs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages