npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@massivescale/tsp-aspnetcore-api

v1.1.0

Published

TypeSpec emitter that generates C# models, ASP.NET Core controllers, service interfaces, and FluentValidation validators.

Readme

@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 that is an HTTP payload (a request or response body, or reachable from one), the emitter produces a public partial class <Name>. A base model carrying @discriminator is emitted abstract, since it has no discriminator value of its own (see Model Generation). Response models (OkResponse-style models with an @body) and metadata-only models (all @header / @query / @path / @statusCode) describe the HTTP envelope rather than data, so no class is emitted for them. Each templated payload instantiation gets its own class (PagedResult<Widget> → PagedResultWidget). See Which models get a class. 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 recursive Patch(T) / PatchAsync(T) updates and TryPatch rejected-path diagnostics, plus IsDefined / IsNull / TryGetValue helpers for fine-grained field-level control. FluentValidation validators enforce which fields are writable per operation, including recursively nested model properties. Explicit object-value defaults such as = #{} generate fresh nested model instances. Each output type can be disabled independently via the emit-* options.


Install

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

Prerelease versions are published under the beta tag:

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

The emitter requires Node.js 22 or later and these peer dependencies:

npm install --save-dev @typespec/compiler @typespec/http @typespec/versioning

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 Models/Users/User.g.cs. Every model shares the MyCompany.Api.Models namespace; the Users folder mirrors the TypeSpec namespace below the root (see Namespace Resolution).

namespace MyCompany.Api.Users;

/** A registered user. */
model User {
  @format("uuid") id: string;
  name: string;
  active: boolean;
  joined?: utcDateTime;
}
// Models/Users/User.g.cs (file header and #pragma lines omitted)
#nullable enable

using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace MyCompany.Api.Models
{
    /// <summary>
    /// A registered user.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
    public partial class User
    {
        [JsonPropertyName("id")]
        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
        public Guid? Id { get; set; }

        [JsonPropertyName("name")]
        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
        public string? Name { get; set; }

        [JsonPropertyName("active")]
        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
        public bool? Active { get; set; }

        [JsonPropertyName("joined")]
        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
        public DateTimeOffset? Joined { get; set; }
    }
}

Add @typespec/http operations to the spec and the emitter also writes abstract controllers and service interfaces. See Controllers and Services for that output and how to implement it, and example/aspnet-versioned for a complete ASP.NET Core project.


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. Each run records the files it wrote in .tsp-aspnetcore-api-manifest (in the emitter output dir) and the next run deletes exactly those. Without a manifest, only files that match file-extension and carry this emitter's signature in their first lines are deleted; bin, obj, node_modules, dot-folders and links are never entered. See Output cleaning. | | controller-query-params | "all" | "required" | "none" | "required" | Which @query parameters become controller action and service method parameters. "all" emits every query parameter, "required" emits only non-optional ones, and "none" emits none. Path and header parameters and the request body are always emitted. See Controllers and Services — Query parameters. | | 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 true, EnumMemberConverter.g.cs is emitted even when the spec has no enums. The converter is always emitted when any enum is generated, because every enum references it. MergePatch.g.cs is always emitted when merge-patch-style is "generic" (it is required by PATCH operations). | | 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}MergePatchUpdate 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, PUT 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. Must be one or more .segment parts of letters, digits, _ or - (e.g. .g.cs, .generated.cs); an empty extension is rejected. | | fully-qualified-types | boolean | false | When false, generated model, enum, and helper references use short names and generated files import the relevant namespaces; names that clash with framework types (Task, Version, …) stay qualified. Set to true to include namespaces in each type reference. See Cross-namespace references. | | 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 — Which models get a class (response and metadata-only models, template instances), 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 Patch — MergePatch<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

Known limitations

  • Models merge all API versions. A versioned spec produces one class per model containing every property from every version. Only routes and validators are version-aware; use the validators-version-strategy option to control how validation treats properties added or removed in later versions.
  • Multipart bodies are not supported. @multipartBody and HttpPart<T> operations are not mapped: the generated action and service method get no body parameter for them.
  • @cookie parameters are not supported. Cookie parameters are left out of the generated action and service signatures; read them from Request.Cookies in your controller.
  • @query arrays default to explode: false. TypeSpec's default for array query parameters is the comma-separated form (?tags=a,b), but ASP.NET Core's [FromQuery] binding expects repeated keys (?tags=a&tags=b). The emitter ignores explode, so declare @query(#{ explode: true }) to make the TypeSpec contract (and any OpenAPI generated from it) match what the controller binds, or add a custom model binder for the comma-separated form.