# Why ServiceStack Source: https://docs.servicestack.net/why-servicestack
ServiceStack is a batteries-included framework for building **typed, message-based APIs** in .NET. Most frameworks give you a way to return JSON from a method. ServiceStack starts one level up: you describe *what the message is*, and everything downstream - the routes, the docs, the validation, the admin UIs, the native clients, the AI tool definitions - is generated from that single description. The result is a framework where **adding a capability usually means adding an attribute**, not adding a project. ## One contract, everything else generated Here is a complete, working, queryable, secured API: ```csharp [ValidateIsAuthenticated] [Description("Find bookings matching the specified criteria")] public class QueryBookings : QueryDb { public int[] Ids { get; set; } } ``` That single Request DTO gives you a REST API with filtering, paging and sorting, an executable API Explorer UI, a portable JSON Schema, an entry in your OpenAPI spec, a Locode CRUD App, an AI-callable tool and native typed clients in **15 languages** - with no Service implementation, no controller, no DTO mapping and no SDK project.
## Write the contract. Skip the rest. The clearest way to see the value is to compare what you write against what you'd otherwise have to write - and keep writing, for every platform, forever.
## Rich UIs your APIs get for free Every ServiceStack App ships with capable, authorized management UIs that are **generated from your APIs at runtime**. There is nothing to install, no separate admin project to maintain, and no drift between your API and the tools used to operate it.
::: info All Admin UIs respect your App's existing authentication and authorization - users only see the screens and actions their roles allow. ::: ## Typed clients in 15 languages [Add ServiceStack Reference](/add-servicestack-reference) reads the metadata your App already publishes and generates a **single native source file** of DTOs for the target language, paired with an idiomatic Service Client that understands the contract: response types, routes, HTTP methods, authentication, structured errors, validation failures and AutoQuery conventions.
.NET
C# · F# · VB.NET
Web & Scripting
TypeScript · JavaScript · Python · PHP · Ruby
Mobile
Swift · Java · Kotlin · Dart
Cloud & Systems
Go · Rust · Zig
Two commands in any language - add the client, then generate the DTOs:
Re-run the same command whenever your API changes. New fields appear in the generated DTOs; removed or renamed members become **compile errors in the consuming App** instead of runtime surprises in production. ::: tip This is the opposite of the SDK treadmill. There's no per-platform SDK repo to version, publish and document - the contract *is* the SDK. ::: ## AI-native, without an AI backend The metadata that generates typed clients also describes your APIs well enough for a model to use them. So instead of building a parallel "AI API" with its own auth, its own schemas and its own risk profile, ServiceStack lets AI call the APIs you already have - **as the signed-in user, through the same pipeline as every other client**.
Because approval forms are generated from the same schema that powers API Explorer, users can **inspect and correct** what a model is about to submit before anything is written. And since it's your normal request pipeline, your authorization, validation, filters and business logic all still apply - a model cannot do anything the current user couldn't do themselves.
AI Chat

A complete multi-provider AI App at /chat using your App's existing users, database and security boundary.

MCP Server

Expose the same approved APIs to external AI Assistants over the Model Context Protocol.

PDF Studio

Design documents with AI, publish immutable revisions, then render production PDFs with no LLM at runtime.

## Host it your way ServiceStack Services are decoupled from HTTP, from any UI technology and from any single host. The same implementation can serve a website, a mobile App, a queue consumer and a gRPC endpoint.
## Why message-based APIs ServiceStack's design follows [Martin Fowler's Data Transfer Object pattern](https://martinfowler.com/eaaCatalog/dataTransferObject.html): when a call crosses a process boundary, send *one well-defined message* rather than many fine-grained calls. This isn't stylistic. A remote call is the most expensive thing in general-purpose computing, and an interface that hides that cost behind method signatures encourages exactly the wrong shape of system.
RPC-style
  • A new method for every way a client wants to ask
  • Adding a parameter is a breaking change
  • N chatty round-trips where one would do
  • Can't be cached, queued, batched or deferred
  • Client-shaped APIs that outlive the client
Message-based
  • One coarse-grained message serves many use cases
  • Adding a field is additive and backwards-compatible
  • Any combination fulfilled in a single call
  • Cacheable, queueable, batchable, proxyable
  • Service-shaped APIs that outlive their consumers
Because the message is a plain POCO with no framework artifacts in it, the *same type* is your server contract, your client contract, your validation schema, your documentation, your UI definition and your AI tool definition. That's the entire trick - and it's why enabling a new capability so often costs one attribute instead of one project. Read the long-form argument in [Advantages of message-based Web Services](/advantages-of-message-based-web-services) and [Why remote services use DTOs](/why-remote-services-use-dtos). ## Everything in the box
## What this means in practice
Less code to maintain

Admin screens, SDKs, docs and API clients are generated from the contract instead of hand-written and left to drift.

Fewer moving parts

Auth, jobs, caching, messaging, logging and analytics ship together and are designed to work together.

Genuinely testable

Services are dependency-free classes and typed clients make integration tests read like unit tests.

Fast by default

Built on high-performance serializers and data access, on the fastest APIs each .NET runtime offers.

Investment preserved

Libraries are continuously improved across 20+ years - not abandoned and replaced.

Commercially supported

Actively developed with paid support, and free for individuals & OSS.

## Start in 60 seconds
🚀
Create a project

Pick a template and download a ready-to-run App - Blazor, Vue, React, MVC, API-only and more.

servicestack.net/start →
📘
Build your first API

A guided walkthrough from empty project to a working, documented, typed API.

Get started →
See what's new

15 typed languages, AI Chat, API Tools, API Schemas and PDF Studio in the latest release.

v10.1 release notes →
::: info Questions? Join the community on [Discord](https://servicestack.net/discord), ask on [GitHub Discussions](https://servicestack.net/ask) or browse the [Live Demos](https://github.com/NetCoreApps/LiveDemos). ::: # Architecture Overview Source: https://docs.servicestack.net/architecture-overview Ultimately behind-the-scenes ServiceStack is just built on top of ASP.NET's Raw [IHttpAsyncHandler](https://msdn.microsoft.com/en-us/library/ms227433.aspx). Existing abstractions and [xmlconfig-encumbered legacy ASP.NET providers](http://mono.servicestack.net/mvc-powerpack/) have been abandoned, in favour of fresh, simple and clean [Caching](/caching), [Session](/auth/sessions) and [Authentication](/auth/authentication-and-authorization) providers all based on clean POCOs, supporting multiple back-ends and all working seamlessly together. Our best-practices architecture is purposely kept simple, introduces minimal new concepts or artificial constructs that can all be eloquently captured in the diagram below: ## Server Architecture ![ServiceStack Logical Architecture View](/img/pages/overview/servicestack-logical-view-02.webp) ## Client Architecture ServiceStack's [Message-based design](/advantages-of-message-based-web-services) allows us to easily support [typed, generic and re-usable Service Clients](/clients-overview) for all our popular formats: ![ServiceStack HTTP Client Architecture](/img/pages/overview/servicestack-httpclients.webp) Having all clients share the same interface allow them to be hot-swappable at run-time without code changes and keep them highly testable where the same unit test can also [serve as an XML, JSON, JSV, SOAP Integration Test](https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Tests/WebServicesTests.cs). By promoting clean (endpoint-ignorant and dependency-free) Service and DTO classes, your web services are instantly re-usable and can be hosted in non-http contexts as well. E.g. The client architecture when one of the [built-in MQ Host is enabled](/redis-mq): ![ServiceStack MQ Client Architecture](/img/pages/overview/servicestack-mqclients.webp) # Instantly Servicify existing Systems Source: https://docs.servicestack.net/servicify Most legacy systems don't need a rewrite so much as a **contract**. ServiceStack's [AutoGen](/autoquery/autogen) reads an existing database's schema at startup and generates a complete, typed API over it - Request DTOs, data models, implementations and human-friendly pluralized routes - without migrating a single row. From there the whole framework applies: [instant UIs](/api-explorer), [typed clients in 15 languages](/add-servicestack-reference), [gRPC](/grpc/) and MQ endpoints, declarative validation, authorization and audit history.
Step 1
Register your database
Step 2
Enable GenerateCrudServices
Step 3
You have a typed API
## How it works
Enabling it is a single plugin option - AutoGen inspects the registered connection and registers AutoQuery and CRUD Services for each table it finds: ```csharp services.AddPlugin(new AutoQueryFeature { MaxLimit = 1000, GenerateCrudServices = new GenerateCrudServices { DbFactory = ormLite.DbFactory, } }); ```
::: info Prefer to review the models before they become APIs? [okai TypeScript Data Models](/autoquery/okai-db) takes the other route - export the DB schema to editable TypeScript models first, then generate the APIs, C# data models and migrations from those. ::: ## Your conventions, not just ours AutoGen's code generation is programmatically customizable. Generated types can be augmented with declarative attributes so your App's existing conventions - authorization, validation rules, tags, descriptions, formatting - are baked into the generated Services rather than bolted on afterwards. When the conventions are right, the generated classes can be **ejected** into code-first C# and developed as normal. AutoGen is a way in, not something you're stuck with.
## Reachable from everywhere Because AutoGen produces ordinary ServiceStack Services, they inherit every endpoint and client the framework supports. That makes it the fastest route to putting **gRPC** in front of an existing system - which in turn opens it to [every language in gRPC's protoc universe](https://grpc.io/docs/languages/).
gRPC High-performance endpoints Dart Scriptable protoc clients Flutter Mobile, web & desktop React Native Typed TypeScript DTOs
The [Smart, Generic C# / F# / VB.NET Service Clients](/grpc/generic) still give the best UX for consuming gRPC Services, but the [protoc-generated Dart client](/grpc/dart) is a close second - a high-level language with native-class performance and script-like productivity, which makes it an ideal way to explore a freshly servicified system. And anything Dart reaches, [Flutter](https://flutter.dev) reaches too. ## Modernizing without rewriting This is what makes AutoGen useful beyond greenfield work: it lets you modernize parts of a legacy system incrementally, at low cost. Once a database is behind typed contracts you can enable [Multitenancy](/multitenancy), [Optimistic Concurrency](/ormlite/optimistic-concurrency), [declarative validation](/declarative-validation) and Executable Audit History without changing the schema. Business users can maintain validation rules in the RDBMS and manage them through the [Admin UI](/admin-ui-validation), where they're applied instantly at runtime and surfaced through ServiceStack's [client UI auto-binding options](/world-validation). [Locode](/locode/) then gives stakeholders an instant UI to search their data, export queries to Excel, or work through a custom UI with fine-grained control over which tables and operations each user can reach. ## Next steps
AutoGen docs

Configure, customize and export the generated APIs and data models.

Read the guide →
AutoQuery RDBMS

Understand the queryable APIs AutoGen is generating for each table.

Learn AutoQuery →
Locode database-first

Turn the generated APIs into a branded CRUD App for your stakeholders.

Build the UI →
# ServiceStack v10.2 Source: https://docs.servicestack.net/releases/v10_02
![](/img/pages/release-notes/v10.2/bg.webp) # Put the content you already have to work **ServiceStack v10.2** is headlined by **Gemini RAG** - a complete, managed knowledge platform that turns the files, documentation and websites you already maintain into two public customer experiences: a citation-backed **AI Assistant** and an instant, model-free **Website Search**. Both are published to any site with a single script tag, from one import pipeline and one curated set of documents. The rest of the release removes friction from everyday .NET development - portable typed **JSON queries** and native **Upsert** APIs in OrmLite, **Startup Tasks** that keep generated client DTOs and PDF models in sync on every restart, **ServiceStack Auth** support in AI Chat, and the results of a codebase-wide **security and reliability audit**. --- ## Gemini RAG Knowledge Bases, Website Search and Assistants Gemini RAG combines Google Gemini's File Search retrieval with ServiceStack's local data ownership, content ingestion, metadata management and AI Chat UI. It provides the full workflow needed to build and operate reliable Retrieval-Augmented Generation without first assembling your own crawler, document catalogue, synchronization jobs, administration UI and embeddable chat client. Import a documentation repository, upload business documents or crawl a website into isolated **File Stores**, then query the entire knowledge base - or precisely the category, product, locale, version and document status relevant to the question. Answers can remain grounded in approved content with inline citations that let readers inspect the supporting source. One import then feeds two independent public experiences from the same curated content: a citation-backed **Website Assistant** grounded in Gemini's semantic index, and a fast, model-free **Website Search** answered entirely by your own database. ### Everything needed to operate a trusted knowledge system | Capability | Customer benefit | | --- | --- | | **Managed File Stores** | Separate knowledge by product, team, customer, access boundary or lifecycle. | | **Multiple ingestion paths** | Upload files and ZIPs, synchronize maintained folders, or crawl complete documentation websites. | | **Previewable synchronization** | See new, changed, removed and unchanged documents before committing an import or embedding work. | | **Metadata-scoped retrieval** | Search only the category, document type, status, locale, product, version and tags relevant to each experience. | | **Grounded answers and citations** | Keep responses anchored to indexed content and let users open the evidence behind supported claims. | | **Embeddable Website Assistants** | Publish a branded, responsive AI support experience on any site with one script tag. | | **Model-free Website Search** | Publish instant `⌘K` documentation search from the same documents, answered by your own database at no cost per query. | | **Search and traffic analytics** | See what visitors search for, what returns nothing, and optionally chart first-party page traffic without a third-party tracker. | | **Conversation intelligence** | Review what customers ask, find documentation gaps and improve the source material that powers future answers. | | **Operational visibility** | Monitor uploads, audit metadata coverage and reconcile the local catalogue with Gemini's indexed state. | ### Bring files, folders and websites into one knowledge pipeline Gemini RAG supports the ingestion workflow that best matches each source: - **Upload files** for PDFs, Markdown, HTML, CSV, JSON, YAML and other supported documents. ZIP directory structures are retained as browsable categories. - **Synchronize folders** for documentation repositories and maintained server content. Imports can be saved and rerun, with normalized content and metadata compared independently so unchanged documents aren't embedded again. - **Crawl websites** into a private Markdown workspace where extracted pages can be inspected, cleaned and transformed before anything is sent to Gemini. Folder and website imports can be previewed before they change the knowledge base. The preview identifies new, changed, metadata-only, unchanged and removed documents, making embedding work and destructive changes visible before they're applied. Pending uploads survive application restarts and continue from the local catalogue. This creates a repeatable content supply chain instead of a one-off upload: source material can evolve, imports can be rerun, and only the documents that actually changed need to be indexed again. :::tip One Assistant can know every site you maintain A File Store is not limited to one website or repository. The File Store shown below combines **five saved imports from four independently maintained sites** - [docs.servicestack.net](https://docs.servicestack.net/), [servicestack.net](https://servicestack.net/), [react-templates.net](https://react-templates.net/) and [sharpscript.net](https://sharpscript.net/) - into one searchable knowledge base. Customers can ask one AI Assistant or use one Search widget without needing to know which site owns the answer. Metadata scopes keep each published experience focused, while custom Search ranking can promote the most authoritative titles, headings, document types and fresher content across the combined corpus. ::: ### Retrieve the right knowledge, not merely more knowledge Large corpora become useful when retrieval can exclude stale, irrelevant or unapproved content. Gemini RAG attaches structured metadata to each document, including category, document type, status, locale, product, versions, tags and a canonical Source URL. The Explorer UI turns that metadata into navigable categories, filters and coverage reports. Start a chat over the entire File Store, a single document, or the exact filtered view currently being explored. The same server-generated filter is passed to Gemini File Search, so a public product assistant can be limited to `status = published` and the visitor's product/version while an internal research chat can search a broader corpus. Grounded responses retain their citations as the conversation continues. Readers can expand each source to inspect the retrieved excerpt or follow its canonical URL, making answers verifiable instead of asking customers to trust an opaque generated response. ### Publish a Website Assistant with one script tag Any File Store - or a server-enforced filtered slice of it - can be published as a branded Website Assistant: ```html ``` The self-contained widget streams Markdown responses, preserves visitor sessions, displays citations and supports suggested questions, welcome messages, maximized reading and keyboard launch. It renders inside a **Shadow DOM**, isolating its styles and keyboard handling from the host website. Each Assistant can have its own: - visitor-facing name, welcome message and suggested questions; - server-enforced category and metadata scope; - behavior template, private system prompt, response detail and Gemini model; - branded theme, typography, panel and launcher appearance; - automatic launch behavior and Ctrl/+K shortcut; - exact or wildcard allowed website origins and per-client request limit; and - publish state, stable deployment ID and retained customer conversations. Retrieval rules, private prompts, model selection, origin restrictions and rate limits remain on the server - they aren't exposed in the embed code and can't be weakened by the host page. The host can still override safe presentation choices like theme, accent color, launcher icon and position. Seven editable behavior templates provide useful starting points for documentation, troubleshooting, customer support, developer/API guidance, product advice, onboarding and policy assistants. Combined with custom prompts and document filters, the same knowledge base can power several purpose-built experiences without duplicating its source content. ### Publish site search that costs nothing per query The same imported documents also build a **local search index** inside your own database, so one content pipeline powers two public experiences. **Website Search** is completely independent of Gemini: queries never reach a model, never incur usage costs and never leave your App. ```html ``` Documents are split into heading-aware sections and queried through whichever RDBMS the App already uses: Results are ranked by one consistent model across every database, with tunable weights for titles, headings, body text, exact phrases, exact titles, content freshness and preferred document types. Each adjustment re-queries immediately against the same term, so relevance is tuned against real results instead of guessed at. The Shadow DOM widget gives visitors the interaction they already expect from a documentation site: Ctrl/+K and / shortcuts, keyboard result navigation, grouped results with match highlighting, infinite scrolling and recently-opened links. Results open the document's canonical Source URL, or a rendered in-place preview when it has none. Either launcher can be mounted inside your own nav bar instead of floating in a corner. ### Understand what visitors are looking for Every published Search reports its own demand and quality signals, without adding any model usage. Related queries are grouped by normalized wording so near-identical phrasings count as one intent, and **no-result searches** point directly at the documentation you haven't written yet. Because the Search script is already on every page, it can optionally double as a lightweight, **first-party website analytics** system - page views, visitors, sessions, bounce rate, load times, referrers, campaigns, devices and platforms - charted over 24 hours, 7, 30 or 90 days. This is off by default and privacy-respecting by design: it sets no cookies, requests no precise location, anonymizes IP addresses to an IPv4 `/24` or IPv6 `/48`, honors Do Not Track, excludes known bots, and enforces your own denied user-agent, IP-range and page-path rules on the server before anything is stored. Retention is configurable and expired data is removed automatically. An optional consent callback can gate collection entirely, and IP geography is only ever resolved when you explicitly register a resolver. The data stays in your database rather than a third party's. ### Learn from every customer conversation Website Assistant conversations, messages and citations are retained server-side for authorized teams to review. Conversation lists surface user-message counts and originating pages, while message navigation makes it easy to move between a customer's question, the generated answer and its supporting sources. This closes the knowledge loop: teams can discover what visitors actually need, identify weak or missing coverage, improve the underlying documentation and let the next synchronized import improve every Assistant grounded in that content. Assistants support complete draft, publish, unpublish, archive, restore and permanent-deletion workflows. Regenerating a deployment ID invalidates old embeds immediately, while destructive operations summarize affected Assistants, imports and conversations and require typed-name confirmation. ### Know what is indexed and keep it healthy AI Chat retains the authoritative document catalogue, source files, imports, metadata, Assistants and conversation history in your App and OrmLite database, while Gemini File Stores contain the indexed copies used for semantic retrieval. This operational visibility is what makes a knowledge base maintainable long after its first successful demo: administrators can find the documents nobody categorized, reconcile what Gemini actually holds, and correct both in bulk rather than one document at a time. ### Enable Gemini RAG Gemini RAG is a built-in AI Chat extension and is enabled automatically when the App has a Gemini API key and an `IDbConnectionFactory` for its local catalogue: ```bash GOOGLE_API_KEY=your_api_key # or GEMINI_API_KEY=your_api_key ``` Create a File Store, import a few documents and open **New Chat** to ask the first grounded question. When the corpus is ready for customers, publish a **Search** widget for instant navigation and an **Assistant** for grounded answers - both from the same script tag pipeline and the same imported content. See [Gemini RAG, Search & Analytics](/chat/gemini-rag) for the complete reference, or jump to [Imports & Synchronization](/chat/gemini-imports), [Website Search](/chat/gemini-search), [AI Assistants](/chat/gemini-assistants), [Analytics & Privacy](/chat/gemini-analytics) and [Operations & Diagnostics](/chat/gemini-operations). ## Portable, Type-Safe JSON Queries Semi-structured JSON columns normally force a trade: either give up typed queries and hand-write provider-specific JSON SQL, or flatten the document into columns it doesn't naturally fit. OrmLite can now query JSON with the same typed `SqlExpression` API used for relational columns across SQLite, PostgreSQL, SQL Server and MySQL - so the same C# query compiles once, refactors with your Data Models and runs unchanged on every supported database. ### Query known JSON Data Models `Sql.Json()` translates ordinary C# member access, collection membership and array indexes into each database's native JSON functions: ```csharp var q = db.From() .Where(x => Sql.Json(x.Data).Customer.Address.State == "WA" && Sql.Json(x.Data).Tags.Contains("priority") && Sql.Json(x.Data).Lines[0].Quantity > 1); var priorityOrders = db.Select(q); ``` Applications can keep the flexibility of document-shaped data without giving up typed, refactor-safe queries or committing their data access layer to provider-specific JSON SQL. Nested scalar values, objects and collections can all be filtered or projected, with typed JSON fragments automatically deserialized back into C# Data Models. When the column is already a complex property serialized by OrmLite, its document type is inferred: ```csharp var q = db.From() .Where(x => Sql.Json(x.Document).Customer.Address.State == "WA" && Sql.Json(x.Document).Lines.Count > 0); ``` Typed members can also be projected as scalar values or complete C# objects: ```csharp var q = db.From() .Select(x => new { x.Id, State = Sql.Json(x.Data).Customer.Address.State, Total = Sql.Json(x.Data).Total, Address = Sql.Json(x.Data).Customer.Address, }); var summaries = db.Select(q); ``` ### Complete JSON API Typed expressions are preferred when a C# Data Model is available, while explicit JSON paths support dynamic documents and paths selected at runtime: | Capability | Typed expression | Explicit JSON function | | --- | --- | --- | | Read a scalar | `Sql.Json(json).Member` | `Sql.JsonValue(json, path)` | | Read an object or array | `Sql.Json(json).Member` | `Sql.JsonQuery(json, path)` | | Read the root object or array | - | `Sql.JsonQuery(json)` | | Array indexing | `Sql.Json(json).Items[index]` | Include `[index]` in the path | | Array length | `Sql.Json(json).Items.Count` | `Sql.JsonArrayLength(json, path)` | | Scalar array membership | `Sql.Json(json).Items.Contains(value)` | `Sql.JsonArrayContains(json, path, value)` | | Validate JSON | - | `Sql.IsJson(json)` | | Test whether a path exists | - | `Sql.JsonExists(json, path)` | | Read a JSON value's type | - | `Sql.JsonType(json, path)` | | Test document containment | - | `Sql.JsonContains(json, candidate, path)` | `Sql.JsonValue()` extracts a scalar and can be used anywhere a normal value can be used, including filters, projections and ordering: ```csharp var totalPath = "$.Total"; var q = db.From() .Where(x => Sql.JsonValue(x.Data, "$.Customer.Address.shipping_state") == "WA" && Sql.JsonValue(x.Data, totalPath) >= 100m) .OrderByDescending(x => Sql.JsonValue(x.Data, totalPath)); ``` `Sql.JsonQuery()` extracts an object, array or complete root document and deserializes it into the requested C# type: ```csharp var address = db.Scalar
(db.From() .Where(x => x.Id == id) .Select(x => Sql.JsonQuery
(x.Data, "$.Customer.Address"))); var document = db.Scalar(db.From() .Where(x => x.Id == id) .Select(x => Sql.JsonQuery(x.Data))); ``` `Sql.IsJson()`, `Sql.JsonExists()` and `Sql.JsonType()` provide validation and document introspection: ```csharp var validRows = db.Count(x => Sql.IsJson(x.Data) == true); var rowsWithTags = db.Select(db.From() .Where(x => Sql.JsonExists(x.Data, "$.Tags"))); var addressType = db.Scalar(db.From() .Where(x => x.Id == id) .Select(x => Sql.JsonType(x.Data, "$.Customer.Address"))); // JsonValueType.Object ``` `Sql.JsonArrayLength()` and `Sql.JsonArrayContains()` query arrays without requiring a typed document: ```csharp var priorityOrders = db.Select(db.From() .Where(x => Sql.JsonArrayLength(x.Data, "$.Lines") > 0 && Sql.JsonArrayContains(x.Data, "$.Tags", "priority") && Sql.JsonArrayContains(x.Data, "$.Numbers", 1))); var withNull = db.Select(db.From() .Where(x => Sql.JsonArrayContains( x.Data, "$.NullableTags", null))); ``` PostgreSQL and MySQL can additionally use `Sql.JsonContains()` to test whether a JSON document or nested array contains another document: ```csharp var candidate = new { Customer = new { Address = new { shipping_state = "WA" } } }; var matchingOrders = db.Select(db.From() .Where(x => Sql.JsonContains(x.Data, candidate))); var requiredTags = new[] { "priority" }; var taggedOrders = db.Select(db.From() .Where(x => Sql.JsonContains( x.Data, requiredTags, "$.Tags"))); ``` All of these remain ordinary `SqlExpression` queries and work with OrmLite's async query APIs: ```csharp var priorityOrders = await db.SelectAsync(q); ``` See [OrmLite JSON Support](/ormlite/json) for supported operations, typed projections, dynamic paths and database-specific capabilities. ## Native Upsert APIs OrmLite's new `Upsert` APIs express one of the most common data workflows directly: insert a row when its Primary Key is new, otherwise bring the existing row up to date. ```csharp var customer = new Customer { Id = 1, Name = "Initial Name", Email = "initial@example.org", }; db.Upsert(customer); // Insert Id=1 customer.Name = "Updated Name"; db.Upsert(customer); // Update Id=1 ``` On SQLite, PostgreSQL, SQL Server and MySQL/MariaDB this uses each database's native single-statement conflict handling. It avoids the extra existence query used by `Save()` and closes the race window between checking for a row and subsequently inserting or updating it. This makes `Upsert` a natural fit for imports, synchronization, event consumers and retryable jobs that should converge on the same persisted state whether a row is new or already exists. ### Update selected fields A typed `updateOnly` expression restricts which fields change when the row already exists, while still inserting every insertable field for a new row: ```csharp db.Upsert(customer, updateOnly: x => new { x.Name, x.Email }); ``` This makes it safe to preserve fields owned by another part of the application. Primary Key and RowVersion fields can't be updated, and `[IgnoreOnUpdate]` properties remain excluded. When the field set is selected at runtime, use the equivalent string field-name overload: ```csharp var fields = includeEmail ? new[] { nameof(Customer.Name), nameof(Customer.Email) } : new[] { nameof(Customer.Name) }; db.Upsert(customer, updateOnly: fields); ``` ### Upsert multiple rows `UpsertAll` inserts new rows and updates existing rows together in a transaction: ```csharp var customers = new[] { new Customer { Id = 1, Name = "Updated", Email = "one@example.org", }, new Customer { Id = 2, Name = "Inserted", Email = "two@example.org", }, }; db.UpsertAll(customers); db.UpsertAll(customers, updateOnly: x => new { x.Name, x.Email }); db.UpsertAll(customers, updateOnly: fields); ``` As with `Upsert`, `updateOnly` restricts updates to existing rows without restricting the fields inserted for new rows. ### Async Upsert APIs Every single-row, batch, typed-field and runtime-field API has an async equivalent with optional `CancellationToken` support: ```csharp await db.UpsertAsync(customer, token: cancellationToken); await db.UpsertAsync(customer, updateOnly: x => new { x.Name, x.Email }, token: cancellationToken); await db.UpsertAsync(customer, updateOnly: fields, token: cancellationToken); await db.UpsertAllAsync(customers, token: cancellationToken); await db.UpsertAllAsync(customers, updateOnly: x => new { x.Name, x.Email }, token: cancellationToken); await db.UpsertAllAsync(customers, updateOnly: fields, token: cancellationToken); ``` Auto-increment models are also supported. A default `[AutoIncrement]` Primary Key inserts a new row and populates its generated ID; subsequent calls use that ID as the Upsert conflict key. See [OrmLite Upsert](/ormlite/upsert) for batch and async APIs, auto-increment keys, selective updates and guidance on choosing between `Upsert`, `Save`, `Insert` and `Update`. ## Development Startup Tasks ![](/img/pages/release-notes/v10.2/startup-tasks.webp) ServiceStack v10.2 introduces [Startup Tasks](/startup-tasks), a new way to automate development-time work as soon as your App is ready. They run after ASP.NET Core and ServiceStack have fully started, giving them access to the configured `AppHost`, plugins and server addresses: ```csharp StartupTasks.Register("search-index", () => appHost.Resolve().Update()); ``` Startup Tasks turn recurring setup commands into part of the normal development loop. Restart the App and the work happens automatically - without another CLI command to remember, a separate toolchain to install, or stale generated artifacts making their way into source control. They're development-only and don't run in Production, whilst failures are isolated and logged so a convenience task can't prevent the App from starting. ## Client DTOs That Stay in Sync The first built-in use of Startup Tasks removes one of the most common interruptions in full-stack ServiceStack development: manually running `npx get-dtos` after changing a server API. ```csharp StartupTasks.Register("dtos", () => appHost.GetPlugin().GenerateDtos()); ``` The easiest way to add it to existing projects is to run: :::sh npx add-in startup-dtos ::: On every development restart, ServiceStack finds the existing `dtos.*` references belonging to the App and regenerates them directly from its Native Types metadata. There's no longer a separate manual DTO update step after changing or updating server APIs - restarting the App keeps its clients synchronized. This also improves agent-driven development workflows: an agent can add or change server APIs, restart the App, and immediately use the regenerated contracts from the client App without needing to discover and run a separate DTO generation command. ## Safe Automatic PDF Model Generation Apps using `PdfFeature` can use the same workflow to regenerate typed C# models from published PDF template schemas whenever the App restarts: ```csharp StartupTasks.Register("pdf", () => appHost.GetPlugin().GeneratePdfs()); ``` Unchanged runs are inexpensive: models are generated in memory and compared with existing source without invoking Typst or rewriting files. Generated PDF models now use a simple ownership marker: ```csharp // // Remove this line to preserve this file on future runs. // ``` While the opening marker remains, ServiceStack keeps the model synchronized with its `.ui.json` schema. Remove it when you want to take ownership of a generated model and future runs will preserve it. This makes automatic regeneration safe and predictable without content hashes or heuristics trying to determine whether a file was edited. See the [Startup Tasks reference](/startup-tasks) for complete DTO discovery, URL matching and generation options, or [Rendering PDFs](/chat/rendering-pdfs) for PDF model generation and customization. ## AI Chat now supports ServiceStack Auth AI Chat is installed **inside** your ServiceStack App, so it never needs a separate user directory, a second login experience or a new identity silo - existing users sign in with the account they already have. Until now that integration assumed ASP.NET Core **Identity Auth**. In v10.2 AI Chat also supports ServiceStack's own built-in **Auth** - `AuthFeature` with `CredentialsAuth()` and your existing `IAuthRepository` - so both identity models are now first-class: ```csharp services.AddPlugin(new ChatFeature { RequireAuth = true, AuthType = ChatAuthType.Credentials, }); ``` The `credentials` sign-in component authenticates through ServiceStack's `Authenticate` API and shares the App's auth cookie, which is what lets it work against either provider - the Chat UI asks the App who the user is instead of maintaining its own answer. Everything AI Chat scopes to an identity behaves identically on both: conversation threads, history and generated media; Projects, personal Skills, Agent Profiles and provider preferences; Gemini File Stores, document catalogues and PDF Studio workspaces; `RequireAuth` and `RequiredRole` gating; and API Tools calls, which execute as the signed-in user through your App's normal authorization. The practical consequence is that adopting AI Chat no longer implies an Identity Auth migration. Apps still running ServiceStack Auth can add AI Chat and have their existing users, roles and permissions apply immediately, whilst account lifecycle - password policy, lockouts, roles, deactivation - remains owned by the App in either case. API Keys remain a valid way in whichever `AuthType` is configured, and `RequireAuth = false` still runs everything as the shared `default` user for single-user or trusted-network deployments. See [Integrated Auth](/chat/auth) for sign-in types, per-user isolation and request validation. --- ## Get Started Everything in v10.2 works on **.NET 8+** ServiceStack Apps, and the OrmLite, Startup Tasks and hardening improvements arrive with the upgrade - there's nothing new to install or configure: | To use | Do this | | --- | --- | | [JSON Queries](/ormlite/json) and [Upsert](/ormlite/upsert) | Upgrade `ServiceStack.OrmLite.*` - both are available on SQLite, PostgreSQL, SQL Server and MySQL/MariaDB. | | [Startup Tasks](/startup-tasks) | Register a task in development, or start from a project template where the `dtos` task is already included. | | [Gemini RAG](/chat/gemini-rag) | Add AI Chat with `npx add-in chat`, set `GOOGLE_API_KEY` and open **Gemini RAG** in `/chat`. | | [AI Chat with ServiceStack Auth](/chat/auth) | Keep your existing `AuthFeature` - set `AuthType = ChatAuthType.Credentials` and your users, roles and permissions apply to `/chat` immediately. | The quickest way to see the headline feature is to point a **folder import** at the documentation folder of a repo you already have, import it, and ask the first grounded question in **New Chat**. From there the same documents publish a [Search](/chat/gemini-search) widget and an [Assistant](/chat/gemini-assistants) with two script tags. ## Hardened ServiceStack ![](/img/pages/release-notes/v10.2/hardened-servicestack.webp) This release also includes the results of a codebase-wide AI-assisted security and reliability audit. The review identified and resolved issues across ServiceStack's core runtime, authentication, data access, messaging, cloud integrations, UI, serialization, image processing and client libraries. The value for existing Apps is that this work happens beneath your code: the same trust boundaries, parsers and background workers your App already relies on now handle malformed input, hostile input and concurrent load more defensively - and every change is published for review in the per-package audit reports below, so security teams can see exactly what was fixed rather than take an upgrade on faith. ### Reliability and correctness The audit also fixed race conditions in distributed locks, caches, serializers, background workers and type registries; made disposal and shutdown deterministic; removed sync-over-async and retry defects; and added defensive null, range and format validation throughout public APIs. Further corrections address master/replica routing, cache key and update semantics, OpenAPI schema generation, route parameters, multipart requests, configuration parsing, image resizing, database metadata and query generation, message acknowledgement and recovery, and cross-platform behavior. Together these changes make failures explicit instead of silently losing data, prevent malformed external input from escalating into process-wide failures, and improve predictable behavior under concurrency, cancellation and partial infrastructure outages. ### Audit reports # Release Notes History Source: https://docs.servicestack.net/release-notes-history ## 2026 - [v10.1](/releases/v10_01) ## 2025 - [v10](/releases/v10_00) - [v8.10](/releases/v8_10) - [v8.9](/releases/v8_09) - [v8.8](/releases/v8_08) - [v8.7](/releases/v8_07) - [v8.6](/releases/v8_06) ## 2024 - [v8.5](/releases/v8_05) - [v8.4](/releases/v8_04) - [v8.3](/releases/v8_03) - [v8.2](/releases/v8_02) - [v8.1](/releases/v8_01) ## 2023 - [v8](/releases/v8_00) - [v6.11](/releases/v6_11) - [v6.10](/releases/v6_10) - [v6.9](/releases/v6_09) - [v6.8](/releases/v6_08) - [v6.7](/releases/v6_07) - [v6.6](/releases/v6_06) ## 2022 - [v6.5](/releases/v6_05) - [v6.4](/releases/v6_04) - [v6.3](/releases/v6_03) - [v6.2](/releases/v6_02) - [v6.1](/releases/v6_01) - [v6](/releases/v6_00) ## 2021 - [v5.13](/releases/v5_13) - [v5.12](/releases/v5_12) - [v5.11](/releases/v5_11) ## 2020 - [v5.10](/releases/v5_10) - [v5.9](/releases/v5_9) - [v5.8](/releases/v5_8) ## 2019 - [v5.7](/releases/v5_7) - [v5.6](/releases/v5_6) - [v5.5](/releases/v5_5) ## 2018 - [v5.4](/releases/v5_4) - [v5.2](/releases/v5_2) - [v5.1.0](/releases/v5_1_0) - [v5.0.2](/releases/v5_0_0) ## 2017 - [v5.0.0](/releases/v5_0_0#v5-release-notes) - [v4.5.14](/releases/v4_5_14) - [v4.5.10](/releases/v4_5_10) - [v4.5.8](/releases/v4_5_8) - [v4.5.6](/releases/v4_5_6) ## 2016 - [v4.5.2](/releases/v4_5_2) - [v4.5.0](/releases/v4_5_0) - [v4.0.62](/releases/v4_0_62) - [v4.0.60](/releases/v4_0_60) - [v4.0.56](/releases/v4_0_56) - [v4.0.54](/releases/v4_0_54) - [v4.0.52](/releases/v4_0_52) ## 2015 - [v4.0.50](/releases/v4_0_50) - [v4.0.48](/releases/v4_0_48) - [v4.0.46](/releases/v4_0_46) - [v4.0.44](/releases/v4_0_44) - [v4.0.42](/releases/v4_0_42) - [v4.0.40](/releases/v4_0_40) - [v4.0.38](/releases/v4_0_38) - [v4.0.36](/releases/v4_0_36) ## 2014 - [v4.0.35](/releases/v4_0_35) - [v4.0.34](/releases/v4_0_34) - [v4.0.33](/releases/v4_0_33) - [v4.0.32](/releases/v4_0_32) - [v4.0.31](/releases/v4_0_31) - [v4.0.30](/releases/v4_0_30) - [v4.0.24](/releases/v4_0_24) - [v4.0.23](/releases/v4_0_23) - [v4.0.22](/releases/v4_0_22) - [v4.0.21](/releases/v4_0_21) - [v4.0.19](/releases/v4_0_19) - [v4.0.18](/releases/v4_0_18) - [v4.0.15](/releases/v4_0_15) - [v4.0.12](/releases/v4_0_12) - [v4.0.11](/releases/v4_0_11) - [v4.0.10](/releases/v4_0_10) - [v4.0.09](/releases/v4_0_09) - [v4.0.08](/releases/v4_0_08) - [v4.0.06](/releases/v4_0_06) - [v4.0.0](/releases/v4_0_0) ## 2013 and prior - [Older v3 Release Notes](/release-notes-v3) # Pre Release NuGet Packages Source: https://docs.servicestack.net/pre-release ## ServiceStack Pre-Release NuGet Packages Our interim pre-release NuGet packages in between major releases on NuGet are published to [Feedz.io](https://feedz.io/). ::: tip If preferred, the pre-release packages are also available in our [MyGet](/myget) or [GitHub Packages Registry](/gh-nuget) ::: ### Add using Mix If you have the [dotnet x tool](/dotnet-tool) installed, you can configure your projects by downloading `NuGet.Config` in the same folder as your **.sln** :::sh npx add-in feedz ::: ### Add using VS .NET Instructions to add ServiceStack's Pre-Release packages feed to VS .NET are: 1. Go to **Tools** > **Options** > **Nuget Package Manager** > **Package Sources** 2. Add the Source `https://f.feedz.io/servicestack/pre-release/nuget/index.json` with the name of your choice, e.g. `ServiceStack Pre-Release` After registering the feed it will show up under NuGet package sources when opening the NuGet package manager dialog: ![NuGet Package Manager](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/wikis/myget/package-manager-ui.png) Which will allow you to search and install pre-release packages from the selected Pre Release packages feed. ### Adding Pre-Release NuGet feed without VS .NET If you're not using or don't have VS .NET installed, you can add the MyGet feed to your NuGet.config at `%AppData%\NuGet\NuGet.config`: ```xml ``` ## Redownloading Pre Release packages If you've already packages with the **same version number** from Feedz previously installed, you will need to manually delete the NuGet `/packages` folder for NuGet to pull down the latest packages. ### Clear NuGet Package Cache You can clear your local NuGet packages cache in any OS by running the command-line below in your favorite Terminal: :::sh nuget locals all -clear ::: If `nuget` is not in your Systems `PATH`, it can also be invoked from the `dotnet` tool: :::sh dotnet nuget locals all --clear ::: Within VS .NET you can clear them from **Tools** > **Options** > **Nuget Package Manager** and click **Clear All NuGet Cache(s)**: ![Clear Packages Cache](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/wikis/myget/clear-package-cache.png) Alternatively on Windows you can delete the Cached NuGet packages manually with: :::sh del %LOCALAPPDATA%\NuGet\Cache\*.nupkg /q ::: ### Full Package Clean In most cases clearing the NuGet packages cache will suffice, sometimes you'll also need to manually delete other local packages caches delete all NuGet packages in `/packages` folder: :::sh rd /q /s packages ::: delete `/bin` and `/obj` folders in host project :::sh rd /q /s bin obj ::: ## Versioning Scheme ::include versioning-scheme.md:: # Create your first WebService Source: https://docs.servicestack.net/create-your-first-webservice
ServiceStack APIs start from a different place than most frameworks. Instead of writing a method that returns JSON, you describe **what the message is** - and the routes, the docs, the validation, the API Explorer and the native clients are all generated from that one description. This walkthrough creates a working API from scratch, then shows exactly what each file does and what you got for free.
That's it - you now have a running .NET 10 API. The template's home page is already calling it with typed DTOs! ## How it works
The important detail is what *isn't* in your Service: no `HttpContext`, no serialization, no routing code, no manual model binding. Your Service accepts a message and returns a message, which is what lets ServiceStack expose it over HTTP, MQs, gRPC or in-process without you changing a line. ## The code
## What that one DTO gave you
::: tip Change the return format on any API by adding `?format=json`, `?format=csv` or `?format=jsonl` - or by sending the matching `Accept` header. See [Formats](/formats) for the full list. ::: ## The solution structure Every ServiceStack template scaffolds the same four projects. The layout isn't ceremony - it's what makes your API contract shareable and your logic testable:
Read more in [Physical Project Structure](/physical-project-structure). ## Call your API from anywhere Your App publishes enough metadata to generate a native, typed client for any supported language - so there's no SDK project to write, version or document. Pick a language to see the two commands:
Re-run the generate command whenever your API changes. New fields show up in the generated DTOs, and removed or renamed members become **compile errors in the consuming App** instead of runtime surprises. ### From a web page, with no build step The `web` template's home page uses your App's built-in [JavaScript DTOs](/javascript-add-servicestack-reference) from [/types/mjs](/javascript-add-servicestack-reference) with the [@servicestack/client](/javascript-client) library, loaded from an [importmap](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap): ```html ``` Which lets you reference the package name in your source instead of its physical location: ```html
``` #### Enable static analysis and intelli-sense For IDE intelli-sense during development, save the annotated Typed DTOs to disk: :::sh npm run dtos ::: Then reference the local file to enable static analysis when calling your typed APIs: ```js import { Hello } from '/js/dtos.mjs' client.api(new Hello({ name })) ``` To also get type-checking for **@servicestack/client**, install the dependency-free library as a dev dependency: :::sh npm install -D @servicestack/client ::: Only its TypeScript definitions are used by the IDE, so you get a rich typed authoring experience with no bundler and no additional build time: ![](/img/pages/release-notes/v6.6/mjs-intellisense.png) ### From a component framework The same `JsonServiceClient` works in every JavaScript App, from SPAs to React Native to Node.js servers, e.g. with TypeScript & [Vue Single-File Components](https://vuejs.org/guide/scaling-up/sfc.html): ```html ``` Compare the same API call across the major front-end frameworks: - [Vue 3 HelloApi.mjs](https://github.com/NetCoreTemplates/blazor-vue/blob/main/MyApp/wwwroot/posts/components/HelloApi.mjs) - [Vue HelloApi.vue](https://github.com/NetCoreTemplates/vue-spa/blob/main/MyApp.Client/src/_posts/components/HelloApi.vue) - [Next.js with swrClient](https://github.com/NetCoreTemplates/nextjs/blob/main/ui/components/intro.tsx) - [React HelloApi.tsx](https://github.com/NetCoreTemplates/react-spa/blob/master/MyApp/src/components/Home/HelloApi.tsx) - [Angular HelloApi.ts](https://github.com/NetCoreTemplates/angular-spa/blob/master/MyApp/src/app/home/HelloApi.ts) Native Mobile and Desktop Apps use the same approach - see [Add ServiceStack Reference](/add-servicestack-reference) for Swift, Java, Kotlin, Dart and .NET clients. ## Where to go next
## Start from a full-featured template The `web` template is deliberately empty. When you want a template that comes with an opinionated front-end, auth, a database and deployment already wired up, start from one of the full project templates instead: ### [C# Project Templates Overview](/templates/)
For Blazor WASM and Server see our [Blazor projects & Tailwind components](/templates/blazor-tailwind), and the rich [Vue 3 Tailwind Components](/vue/) library that all Vue templates are pre-configured with. ## Other ways to create a project ::include empty-projects.md:: # Your first Web Service Explained Source: https://docs.servicestack.net/your-first-webservice-explained Let's look a bit deeper into the [Hello World service](/create-your-first-webservice#how-does-it-work) you created: As you have seen, the convention for response DTO is `RequestDTO` and `RequestDTOResponse`. **Note, request and response DTO should be in the same namespace if you want ServiceStack to recognize the DTO pair**. To support automatic exception handling, you also need to add a `ResponseStatus` property to the response DTO: ```csharp // Request DTO public class Hello : IReturn { public string Name { get; set; } } // Response DTO (follows naming convention) public class HelloResponse { public string Result { get; set; } public ResponseStatus ResponseStatus { get; set; } //Automatic exception handling } ``` Services are implemented in a class that either inherits from the `Service` base class or implements the `IService` empty marker interface. Inheriting from the convenient `Service` base class provides easy access to the most common functionality. ```csharp public class HelloService : Service { public object Any(Hello request) { return new HelloResponse { Result = $"Hello, {request.Name}" }; } } ``` The above service can be called with **Any** HTTP Verb (e.g. GET, POST,..) from any endpoint or format (e.g. JSON, XML, etc). You can also choose to handle a specific Verb by changing the method name to suit. E.g. you can limit the Service to only handle HTTP **GET** requests by using the `Get` method: ```csharp public class HelloService : Service { public object Get(Hello request) => new HelloResponse { Result = $"Hello, {request.Name}" }; } ``` ## Calling Web Services Thanks to the above `IReturn` interface marker you'll be able to use the terse, typed Service Client APIs, e.g: ```csharp var client = new JsonApiClient(BaseUri); HelloResponse response = client.Get(new Hello { Name = "World" }); ``` Request DTOs that don't implement `IReturn` will need to explicitly specify the Response DTO on their call-site, e.g: ```csharp HelloResponse response = client.Get(new Hello { Name = "World" }); HelloResponse response = client.Get("/hello/World!"); ``` Alternatively you could use a general purpose HTTP Client like [HTTP Utils](/http-utils): ```csharp HelloResponse response = "http://base.url/hello/World" .GetJsonFromUrl() .FromJson(); ``` We highly recommend annotating Request DTO's with the above `IReturn` marker as it enables a generic typed API without clients having to know and specify the Response at each call-site, which would be invalidated and need to be manually updated if the Service Response Type changes. More details on the Service Clients is available on the [C#/.NET Service Clients page](/csharp-client). ### Registering Custom Routes If no routes are defined the .NET Service Clients will use the [pre-defined Routes](/routing#pre-defined-routes). You can annotate your Request DTO with the `[Route]` attribute to register additional Custom Routes, e.g: ```csharp //Request DTO [Route("/hello")] [Route("/hello/{Name}")] public class Hello : IReturn { public string Name { get; set; } } ``` The .NET ServiceClients will then use the best matching Route based on the populated properties on the Request DTO. ### Routing Tips No **?queryString** or POST Form Data should be included in the route as ServiceStack automatically populates Request DTOs with all matching params, e.g: ```csharp [Route("/hello")] ``` Matches both `/hello` and `/hello?name=World` with the latter populating the `Name` Request DTO **public property**. When the route includes a variable, e.g: ```csharp [Route("/hello/{Name}")] ``` It only matches: ``` /hello/name ``` Whereas using a wildcard: ```csharp [Route("/hello/{Name*}")] ``` Matches all routes: ``` /hello /hello/name /hello/my/name/is/ServiceStack ``` More details about Routing is available on the [Routing page](/routing). # ServiceStack API design Source: https://docs.servicestack.net/api-design The primary difference between developing RPC vs ServiceStack's [Message-based Services](/what-is-a-message-based-web-service) is that the Services entire contract is defined by its typed messages, specifically the Request DTO which defines both the System inputs and identifies the System output. Typically both are POCO DTOs however the [response can be any serializable object](/service-return-types). The simplest Service example that does this is: ```csharp public class MyRequest : IReturn {} public class MyServices : Service { public object Any(MyRequest request) => request; } ``` As only the `Any()` wildcard method is defined, it will get executed whenever the `MyRequest` Service is invoked via **any HTTP Verb**, [gRPC](/grpc/), [MQ](/messaging) or [SOAP](/soap-support) Request. The Request DTO is also all that's required to invoke it via any [Typed Generic Service Client](/clients-overview) in any supported language, e.g: ```csharp MyRequest response = client.Get(new MyRequest()); ``` All Services are accessible by their [pre-defined routes](/routing#pre-defined-routes), we can turn it into a functional data-driven Service by annotating it with a [user-defined route](/routing) and changing the implementation to return all App Contacts: ```csharp public class Contact { public int Id { get; set; } public string Name { get; set; } } [Route("/contacts")] public class GetContacts : IReturn> { } public class ContactsService : Service { public object Get(GetContacts request) => Db.Select(); } ``` Which your C# clients will still be able to call with: ```csharp List response = client.Get(new GetContacts()); ``` This will make a **GET** call to the custom `/contacts` URL and returns all rows from the `Contact` Table in the configured RDBMS using [OrmLite](/ormlite/) `Select()` extension method on the `base.Db` ADO.NET `IDbConnection` property on ServiceStack's convenience `Service` base class. Using `Get()` limits access to this service from HTTP **GET** requests only, all other HTTP Verbs requests to `/contacts` will return a **404 NotFound** HTTP Error Response. ### Using explicit Response DTO Our recommendation instead of returning naked collections is returning an explicit predictable Response DTO, e.g: ```csharp [Route("/contacts")] public class GetContacts : IReturn { } public class GetContactsResponse { public List Results { get; set; } public ResponseStatus ResponseStatus { get; set; } } public class ContactsService : Service { public object Get(GetContacts request) => new GetContactsResponse { Results = Db.Select() }; } ``` Whilst slightly more verbose this style benefits from [more resilience in evolving and versioning](https://stackoverflow.com/a/12413091/85785) message-based Services and more coarse-grained APIs as additional results can be added to the Response DTO without breaking existing clients. You'll also need to follow the above convention if you also wanted to [support SOAP endpoints](/soap-support) or if you want to be able to handle Typed [Response Messages in MQ Services](/messaging#message-workflow). ### All APIs have a preferred default method Like the `Send*` APIs before them, both [API Explorer](/api-explorer) and the new [`Api*` methods](/csharp-client.html#high-level-api-and-apiasync-methods) send API requests using an APIs **preferred HTTP Method** which can be defined either: - Explicitly annotating Request DTOs with `IGet`, `IPost`, etc. **IVerb** interface markers - Using the verb specified in its user-defined `[Route]` attribute (if single verb specified) - Implicitly when using AutoQuery/CRUD Request DTOs - Using the Services **Verb()** implementation method if not using **Any()** If the HTTP Method can't be inferred, it defaults to using HTTP **POST**. But as good API documentation practice, we recommend specifying the HTTP Method each API should use, preferably using the `IVerb` interface marker, so it's embedded into the APIs Services Contract shared with clients (not required for AutoQuery APIs). ## ServiceStack's API Design We'll walk through a few examples here but for a more detailed look into the usages and capabilities of ServiceStack's API design checkout its [Comprehensive Test Suite](https://github.com/ServiceStack/ServiceStack/blob/master/tests/RazorRockstars.Console.Files/ReqStarsService.cs) At a minimum ServiceStack Services only need to implement the `IService` empty interface: ```csharp public interface IService {} ``` The interface is used as a Marker interface that ServiceStack uses to find, register and auto-wire your existing services. Although you're more likely going to want to inherit from ServiceStack's convenience concrete `Service` class which contains easy access to ServiceStack's providers: ```csharp public class Service : IService { IRequest Request { get; } // HTTP Request Context IResponse Response { get; } // HTTP Response Context IServiceGateway Gateway { get; } // Built-in Service Gateway IMessageProducer MessageProducer { get; } // Message Producer for Registered MQ Server void PublishMessage(T message); // Publish messages to Registered MQ Server IVirtualPathProvider VirtualFileSources { get; } // Virtual FileSystem Sources IVirtualFiles VirtualFiles { get; } // Writable Virtual FileSystem ICacheClient Cache { get; } // Registered Caching Provider ICacheClientAsync CacheAsync { get; } // Registered Async Caching Provider (or sync wrapper) MemoryCacheClient LocalCache { get; } // Local InMemory Caching Provider IDbConnection Db { get; } // Registered ADO.NET IDbConnection IRedisClient Redis { get; } // Registered RedisClient ValueTask GetRedisAsync(); // Registered Async RedisClient IAuthRepository AuthRepository { get; } // Registered User Repository IAuthRepositoryAsync AuthRepositoryAsync { get; } // Registered Async User Repository ISession SessionBag { get; } // Dynamic Session Bag ISessionAsync SessionBagAsync { get; } // Dynamic Async Session Bag Task SessionAsAsync(); // Resolve Typed UserSession Async TUserSession SessionAs(); // Resolve Typed UserSession IAuthSession GetSession() { get; } // Resolve base IAuthSession Task GetSessionAsync(); // Resolve base IAuthSession Async bool IsAuthenticated { get; } // Is Authenticated Request T TryResolve(); // Resolve dependency at runtime T ResolveService(); // Resolve an auto-wired service T GetPlugin(); // Resolve optional registered Plugin T AssertPlugin(); // Resolve required registered Plugin void Dispose(); // Override to implement custom IDispose ValueTask DisposeAsync(); // implement IAsyncDisposable (.NET v4.7.2+) } ``` ### Basic example - Handling Any HTTP Verb Lets revisit the Simple example from earlier: ```csharp [Route("/contacts")] public class GetContacts : IReturn> { } public class ContactsService : Service { public object Get(GetContacts request) => Db.Select(); } ``` ServiceStack maps HTTP Requests to your Services **Actions**. An Action is any method that: - Is `public` - Only contains a **single argument - the typed Request DTO** - Has a Method name matching a **HTTP Method** or **Any** (the fallback that can handle "ANY" method) - Methods can have **Format** suffix to handle specific formats, e.g. if exists `GetJson` will handle **GET JSON** requests - Can specify either `T` or `object` Return type, both have same behavior ### Content-Type Specific Service Implementations Service methods can also use `Verb{Format}` method names to provide a different implementation for handling a specific Content-Type. The Service below defines several different implementation for handling the same Request: ```csharp [Route("/my-request")] public class MyRequest { public string Name { get; set; } } public class ContentTypeServices : Service { public object GetJson(MyRequest request) => ..; // Handles GET /my-request for JSON responses public object GetHtml(MyRequest request) => // Handles GET /my-request for HTML Responses $@"

GetHtml {request.Name}

"; public object AnyHtml(MyRequest request) => // Handles other POST/PUT/etc Verbs for HTML Responses $@"

AnyHtml {request.Name}

"; public object Any(MyRequest request) => ...; // Handles all other unspecified Verbs/Formats } ``` ### Optional *Async Suffixes In addition your Services can optionally have the `*Async` suffix which by .NET Standard (and ServiceStack) guidelines is preferred for Async methods to telegraph to client call sites that its response should be awaited. ```csharp [Route("/contacts")] public class GetContacts : IReturn> { } public class ContactsService : Service { public async Task GetAsync(GetContacts request) => await Db.SelectAsync(); public object GetHtmlAsync(MyRequest request) => $@"

GetHtml {request.Name}

"; } ``` If both exists (e.g. `Post()` and `PostAsync()`) the `*Async` method will take precedence and be invoked instead. Allowing both is useful if you have internal services directly invoking other Services using `HostContext.ResolveService()` where you can upgrade your Service to use an Async implementation without breaking existing clients, e.g. this is used in [RegisterService.cs](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/Auth/RegisterService.cs): ```csharp [Obsolete("Use PostAsync")] public object Post(Register request) { try { var task = PostAsync(request); return task.GetResult(); } catch (Exception e) { throw e.UnwrapIfSingleException(); } } /// /// Create new Registration /// public async Task PostAsync(Register request) { //... async impl } ``` To change to use an async implementation whilst retaining backwards compatibility with existing call sites, e.g: ```csharp using var service = HostContext.ResolveService(Request); var response = service.Post(new Register { ... }); ``` This is important if the response is ignored as the C# compiler wont give you any hints to await the response which can lead to timing issues where the Services is invoked but User Registration hasn't completed as-is often assumed. Alternatively you can rename your method to use `*Async` suffix so the C# compiler will fail on call sites so you can replace the call-sites to `await` the async `Task` response, e.g: ```csharp using var service = HostContext.ResolveService(Request); var response = await service.PostAsync(new Register { ... }); ``` ### Group Services by Tag Related Services by can be grouped by annotating **Request DTOs** with the `[Tag]` attribute where they'll enable functionality in a number of ServiceStack's metadata services where they'll be used to [Group Services in Open API](https://swagger.io/docs/specification/grouping-operations-with-tags/). This feature could be used to tag which Services are used by different platforms: ```csharp [Tag("web")] public class WebApi : IReturn {} [Tag("mobile")] public class MobileApi : IReturn {} [Tag("web"),Tag("mobile")] public class WebAndMobileApi : IReturn {} ``` Where they'll appear as a tab to additionally filter APIs in metadata pages: ![](/img/pages/metadata/tag-groups.webp) They're also supported in [Add ServiceStack Reference](/add-servicestack-reference) where it can be used in the [IncludeTypes](/csharp-add-servicestack-reference#includetypes) DTO customization option where tags can be specified using braces in the format `{tag}` or `{tag1,tag2,tag3}`, e.g: ``` /* Options: IncludeTypes: {web,mobile} ``` Or individually: ``` /* Options: IncludeTypes: {web},{mobile} ``` It works similar to [Dependent Type References wildcard syntax](/csharp-add-servicestack-reference#include-request-dto-and-its-dependent-types) where it expands all Request DTOs with the tag to include all its reference types so including a `{web}` tag would be equivalent to including all Request DTOs & reference types with that reference, e.g: ``` /* Options: IncludeTypes: WebApi.*,WebAndMobileApi.* ``` ### Micro ORMs and ADO.NET's IDbConnection Code-First Micro ORMS like [OrmLite](/ormlite/) and [Dapper](https://github.com/StackExchange/Dapper) provides a pleasant high-level experience whilst working directly against ADO.NET's low-level `IDbConnection`. They both support all major databases so you immediately have access to a flexible RDBMS option out-of-the-box. At the same time you're not limited to using the providers contained in the `Service` class and can continue to use your own register IOC dependencies (inc. an alternate IOC itself). ### Micro ORM POCOs make good DTOs The POCOs used in Micro ORMS are particularly well suited for re-using as DTOs since they don't contain any circular references that the Heavy ORMs have (e.g. EF). OrmLite goes 1-step further and borrows pages from NoSQL's playbook where any complex property e.g. `List` is transparently blobbed in a schema-less text field, promoting the design of frictionless **Pure POCOS** that are uninhibited by RDBMS concerns. In many cases these POCO data models already make good DTOs and can be returned directly instead of mapping to domain-specific DTOs. ### Calling Services from a Typed C# Client In Service development your services DTOs provides your technology agnostic **Service Layer** which you want to keep clean and as 'dependency-free' for maximum accessibility and potential re-use. Our recommendation is to follow our [Recommended Physical Project Structure](/physical-project-structure) and keep your DTOs in a separate ServiceModel project which ensures a well-defined ServiceContract [decoupled from their implementation and accessible from any client](/service-complexity-and-dto-roles#data-transfer-objects---dtos). This recommended Physical project structure is embedded in each [ServiceStack VS.NET Template](/templates/). One of ServiceStack's strengths is its ability to re-use your Server DTOs on the client enabling ServiceStack's productive end-to-end typed API. ServiceStack's use of Typed DTOs in its message-based design enable greater resiliency for your Services where the exact DTOs aren't needed, only the shape of the DTOs is important and clients can also opt to use partial DTOs containing just the fields they're interested in. In the same way extending existing Services with new optional properties wont break existing clients using older DTOs. When developing both Server and Client applications the easiest way to call typed Services from clients is to just have them reference the same ServiceModel .dll the Server uses to define its Service Contract, or for clients that only need to call a couple of Service you can choose to instead copy the class definitions as-is, in both cases calling Services is exactly the same where the Request DTO can be used with any of the generic [C#/.NET Service Clients](/csharp-client) to call Services using a succinct typed API, e.g: #### Service Model Classes ```csharp [Route("/contacts")] public class GetContacts : IReturn> { } public class Contact { ... } ``` Which can used in any ServiceClient with: ```csharp var client = new JsonApiClient(BaseUri); List response = client.Get(new GetContacts()); ``` Which makes a **GET** web request to the `/contacts` route. Custom Routes on Request DTO's are also not required as when none are defined the client automatically falls back to using ServiceStack's [pre-defined routes](/routing#pre-defined-routes). ### Generating Typed DTOs In addition to being able to share your `ServiceModel.dll` on .NET Clients enable a typed end-to-end API without code-gen, clients can alternatively choose to use [Add ServiceStack Reference](/csharp-add-servicestack-reference) support to provide an alternative way to get the Services typed DTOs on the client. In both cases the exact same source code is used to call the Services: ```csharp var client = new JsonApiClient(BaseUri); var response = client.Get(new GetContacts()); ``` Add ServiceStack Reference is also available for [most popular languages](/add-servicestack-reference) used in developing Web, Mobile and Desktop Apps. #### Custom API Requests When preferred, you can also use the previous more explicit client API (ideal for when you don't have the `IReturn<>` marker) which lets you call the Service using just its route: ```csharp var response = client.Get>("/contacts"); ``` ::: info All these Service Client APIs **have async equivalents** with an `*Async` suffix ::: ### API QueryParams ServiceStack's message-based design is centered around sending a single message which is all that's required to invoke any Typed API, however there may be times when you need to send additional params where you can't change the API's Request DTO definition or in AutoQuery's case its [Implicit Conventions](/autoquery/rdbms#implicit-conventions) would require too many permutations to be able to type the entire surface area on each Request DTO. Typically this would inhibit being able to invoke these Services from a typed Service Client API that would instead need to either use the untyped [`Get(relativeUrl)`](https://reference.servicestack.net/api/ServiceStack/IRestClient/#-gettresponsestring) ServiceClient APIs or [HTTP Utils](/http-utils) to construct the API Request path manually. Alternatively Request DTOs can implement `IHasQueryParams` where any entries will be sent as additional query params along with the typed DTO: ```csharp public interface IHasQueryParams { Dictionary QueryParams { get; set; } } ``` Which is available in all AutoQuery DTOs where it's added as a non-serializable property so it's only included in the QueryString: ```csharp [DataContract] public abstract class QueryBase : IQuery, IHasQueryParams { //... [IgnoreDataMember] public virtual Dictionary QueryParams { get; set; } } ``` Which allows using existing ServiceClient typed APIs to send a combination of untyped queries in AutoQuery requests, e.g: ```csharp var api = await client.ApiAsync(new QueryContacts { IdsIn = new[]{ 1, 2, 3 }, QueryParams = new() { ["LastNameStartsWith"] = "A" } }); ``` ## Everything centered around Request DTOs A nice property of ServiceStack's message-based design is all functionality is centered around Typed Request DTOs which easily lets you take advantage of high-level value-added functionality like [Auto Batched Requests](/auto-batched-requests) or [Encrypted Messaging](/auth/encrypted-messaging) which are enabled automatically without any effort or easily opt-in to enhanced functionality by decorating Request DTOs or thier Services with Metadata and [Filter Attributes](/filter-attributes) and everything works together, binded against typed models naturally. E.g. you can take advantage of [ServiceStack's Razor support](https://razor.netcore.io/) and create a web page for this service by just adding a Razor view with the same name as the Request DTO in the `/Views` folder, which for the `GetContacts` Request DTO you can just add `/Views/GetContacts.cshtml` and it will get rendered with the Services Response DTO as its View Model when the Service is called from a browser (i.e. HTTP Request with `Accept: text/html`). Thanks to ServiceStack's built-in Content Negotiation you can fetch the HTML contents calling the same url: ```csharp var html = $"{BaseUri}/contacts".GetStringFromUrl(accept:"text/html"); ``` This [feature is particularly nice](https://razor.netcore.io/#unified-stack) as it lets you **re-use your existing services** to serve both Web and Native Mobile and Desktop clients. ### Action Filters Service actions can also contain fine-grained application of Request and Response filters, e.g: ```csharp public class ContactsService : Service { [ClientCanSwapTemplates] public object Get(GetContacts request) => Db.Select(); } ``` This Request Filter allows the client to [change the selected Razor **View** and **Template**](https://razor.netcore.io/#unified-stack) used at runtime. By default the view with the same name as the **Request** or **Response** DTO is used. ## Handling different HTTP Verbs ServiceStack Services lets you handle any HTTP Verb in the same way, e.g this lets you respond with CORS headers to a HTTP **OPTIONS** request with: ```csharp public class ContactsService : Service { [EnableCors] public void Options(GetContact request) {} } ``` Which if you now make an OPTIONS request to the above service, will emit the default `[EnableCors]` headers: ```csharp var webReq = (HttpWebRequest)WebRequest.Create(Host + "/contacts"); webReq.Method = "OPTIONS"; using var webRes = webReq.GetResponse(); webRes.Headers["Access-Control-Allow-Origin"] // * webRes.Headers["Access-Control-Allow-Methods"] // GET, POST, PUT, DELETE, OPTIONS webRes.Headers["Access-Control-Allow-Headers"] // Content-Type ``` ### PATCH request example Handling a PATCH request is just as easy, e.g. here's an example of using PATCH to handle a partial update of a Resource: ```csharp [Route("/contacts/{Id}", "PATCH")] public class UpdateContact : IReturn { public int Id { get; set; } public int Age { get; set; } } public Contact Patch(UpdateContact request) { var Contact = request.ConvertTo(); Db.UpdateNonDefaults(Contact); return Db.SingleById(request.Id); } ``` And the client call is just as easy as you would expect: ```csharp var response = client.Patch(new UpdateContact { Id = 1, Age = 18 }); ``` Although sending different HTTP Verbs are unrestricted in native clients, they're unfortunately not allowed in some web browsers and proxies. So to simulate a PATCH from an AJAX request you need to set the **X-Http-Method-Override** HTTP Header. ## Structured Error Handling When following the [explicit Response DTO Naming convention](/error-handling#error-response-types) ServiceStack will automatically populate the `ResponseStatus` property with a structured Error Response otherwise if returning other DTOs like naked collections ServiceStack will instead return a generic `ErrorResponse`, although this is mostly a transparent technical detail you don't need to know about as for schema-less formats like JSON they return the exact same wire-format. [Error Handling](/error-handling) works naturally in ServiceStack where you can simply throw C# Exceptions, e.g: ```csharp public List Post(Contact request) { if (!request.Age.HasValue) throw new ArgumentException("Age is required"); Db.Insert(request.ConvertTo()); return Db.Select(); } ``` This will result in an Error thrown on the client if it tried to create an empty Contact: ```csharp try { var response = client.Post(new Contact()); } catch (WebServiceException webEx) { webEx.StatusCode // 400 webEx.StatusDescription // ArgumentException webEx.ResponseStatus.ErrorCode // ArgumentException webEx.ResponseStatus.Message // Age is required webEx.ResponseDto is ErrorResponse // true } ``` The same Service Clients Exception handling is also used to handle any HTTP error generated in or outside of your service, e.g. here's how to detect if a HTTP Method isn't implemented or disallowed: ```csharp try { var response = client.Send(new SearchContacts()); } catch (WebServiceException webEx) { webEx.StatusCode // 405 webEx.StatusDescription // Method Not Allowed } ``` In addition to standard C# exceptions your services can also return multiple, rich and detailed validation errors as enforced by [Fluent Validation's validators](/validation). ### Overriding the default Exception handling You can override the default exception handling in ServiceStack by registering a `ServiceExceptionHandlers`, e.g: ```csharp void Configure(Container container) { this.ServiceExceptionHandlers.Add((req, reqDto, ex) => { return ...; }); } ``` ## Smart Routing For the most part you won't need to know about this as ServiceStack's routing works as you would expect. Although this should still serve as a good reference to describe the resolution order of ServiceStack's Routes: 1. Any exact Literal Matches are used first 2. Exact Verb match is preferred over All Verbs 3. The more variables in your route the less weighting it has 4. When Routes have the same weight, the order is determined by the position of the Action in the service or Order of Registration (FIFO) These Rules only come into play when there are multiple routes that matches the pathInfo of an incoming request. Lets see some examples of these rules in action using the routes defined in the [API Design test suite](https://github.com/ServiceStack/ServiceStack/blob/master/tests/RazorRockstars.Console.Files/ReqStarsService.cs): ```csharp [Route("/contacts")] public class Contact {} [Route("/contacts", "GET")] public class GetContacts {} [Route("/contacts/{Id}", "GET")] public class GetContact {} [Route("/contacts/{Id}/{Field}")] public class ViewContact {} [Route("/contacts/{Id}/delete")] public class DeleteContact {} [Route("/contacts/{Id}", "PATCH")] public class UpdateContact {} [Route("/contacts/reset")] public class ResetContact {} [Route("/contacts/search")] [Route("/contacts/aged/{Age}")] public class SearchContacts {} ``` These are results for these HTTP Requests ``` GET /contacts => GetContacts POST /contacts => Contact GET /contacts/search => SearchContacts GET /contacts/reset => ResetContact PATCH /contacts/reset => ResetContact PATCH /contacts/1 => UpdateContact GET /contacts/1 => GetContact GET /contacts/1/delete => DeleteContact GET /contacts/1/foo => ViewContact ``` And if there were multiple of the exact same routes declared like: ```csharp [Route("/req/{Id}", "GET")] public class Req2 {} [Route("/req/{Id}", "GET")] public class Req1 {} public class MyService : Service { public object Get(Req1 request) { ... } public object Get(Req2 request) { ... } } ``` The Route on the Action that was declared first gets selected, i.e: ``` GET /req/1 => Req1 ``` ### Populating Complex Type Properties on QueryString ServiceStack uses the [JSV-Format](/jsv-format) (JSON without quotes) to parse QueryStrings. JSV lets you embed deep object graphs in QueryString as seen [this example url](https://test.servicestack.net/json/reply/StoreLogs?Loggers=%5B%7BId:786,Devices:%5B%7BId:5955,Type:Panel,TimeStamp:1199303309,Channels:%5B%7BName:Temperature,Value:58%7D,%7BName:Status,Value:On%7D%5D%7D,%7BId:5956,Type:Tank,TimeStamp:1199303309,Channels:%5B%7BName:Volume,Value:10035%7D,%7BName:Status,Value:Full%7D%5D%7D%5D%7D%5D): ``` https://test.servicestack.net/json/reply/StoreLogs?Loggers=[{Id:786,Devices:[{Id:5955,Type:Panel, Channels:[{Name:Temperature,Value:58},{Name:Status,Value:On}]}, {Id:5956,Type:Tank,TimeStamp:1199303309, Channels:[{Name:Volume,Value:10035},{Name:Status,Value:Full}]}]}] ``` ## Advanced Usages ### Custom Hooks The ability to extend ServiceStack's service execution pipeline with Custom Hooks is an advanced customization feature that for most times is not needed as the preferred way to add composable functionality to your services is to use [Request / Response Filter attributes](/filter-attributes) or apply them globally with [Global Request/Response Filters](/request-and-response-filters). ### Custom Serialized Responses The new `IHttpResult.ResultScope` API provides an opportunity to execute serialization within a custom scope, e.g. this can be used to customize the serialized response of adhoc services that's different from the default global configuration with: ```csharp return new HttpResult(dto) { ResultScope = () => JsConfig.With(new Config { IncludeNullValues = true }) }; ``` Which enables custom serialization behavior by performing the serialization within the custom scope, equivalent to: ```csharp using (JsConfig.With(new Config { IncludeNullValues = true })) { var customSerializedResponse = Serialize(dto); } ``` ### Request and Response Converters The [Encrypted Messaging Feature](/auth/encrypted-messaging) takes advantage of Request and Response Converters that let you change the Request DTO and Response DTO's that get used in ServiceStack's Request Pipeline where: #### Request Converters Request Converters are executed directly after any [Custom Request Binders](/serialization-deserialization#create-a-custom-request-dto-binder): ```csharp appHost.RequestConverters.Add(async (req, requestDto) => { //Return alternative Request DTO or null to retain existing DTO }); ``` #### Response Converters Response Converters are executed directly after the Service: ```csharp appHost.ResponseConverters.Add(async (req, response) => //Return alternative Response or null to retain existing Service response }); ``` ### Intercept Service Requests As an alternative to creating a [Custom Service Runner](#using-a-custom-servicerunner) to intercept different events when processing ServiceStack Requests, you can instead override the `OnBeforeExecute()`, `OnAfterExecute()` and `OnExceptionAsync()` callbacks in your `Service` class (or base class) to intercept and modify Request DTOs, Responses or Error Responses, e.g: ```csharp class MyServices : Service { // Log all Request DTOs that implement IHasSessionId public override void OnBeforeExecute(object requestDto) { if (requestDto is IHasSessionId dtoSession) { Log.Debug($"{nameof(OnBeforeExecute)}: {dtoSession.SessionId}"); } } //Return Response DTO Name in HTTP Header with Response public override object OnAfterExecute(object response) { return new HttpResult(response) { Headers = { ["X-Response"] = response.GetType().Name } }; } //Return custom error with additional metadata public override Task OnExceptionAsync(object requestDto, Exception ex) { var error = DtoUtils.CreateErrorResponse(requestDto, ex); if (error is IHttpError httpError) { var errorStatus = httpError.Response.GetResponseStatus(); errorStatus.Meta = new Dictionary { ["InnerType"] = ex.InnerException?.GetType().Name }; } return Task.FromResult(error); } } ``` #### Async Callbacks For async callbacks your Services can implement `IServiceBeforeFilterAsync` and `IServiceAfterFilterAsync`, e.g: ```csharp public class MyServices : Service, IServiceBeforeFilterAsync, IServiceAfterFilterAsync { public async Task OnBeforeExecuteAsync(object requestDto) { //... } public async Task OnAfterExecuteAsync(object response) { //... return response; } } ``` If you're implementing `IService` instead of inheriting the concrete `Service` class, you can implement the interfaces directly: ```csharp // Handle all callbacks public class MyServices : IService, IServiceFilters { //.. } // Or individually, just the callbacks you want public class MyServices : IService, IServiceBeforeFilter, IServiceAfterFilter, IServiceErrorFilter { //.. } ``` ### Custom Service Runner The [IServiceRunner](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Web/IServiceRunner.cs) decouples the execution of your service from the implementation of it which provides an alternative custom hook which lets you add custom behavior to all Services without needing to use a base Service class. To add your own Service Hooks you just need to override the default Service Runner in your AppHost from its default implementation: ```csharp public virtual IServiceRunner CreateServiceRunner(ActionContext actionContext) { return new ServiceRunner(this, actionContext); //Cached per Service Action } ``` With your own: ```csharp public override IServiceRunner CreateServiceRunner(ActionContext actionContext) { return new MyServiceRunner(this, actionContext); //Cached per Service Action } ``` Where `MyServiceRunner` is just a custom class implementing the custom hooks you're interested in, e.g: ```csharp public class MyServiceRunner : ServiceRunner { public override OnBeforeExecute(IRequest req, TRequest request, object service) { // Called just before any Action is executed } public override Task ExecuteAsync(IRequest req, object instance, TRequest requestDto) { // Called to execute the Service instance with the requestDto return base.ExecuteAsync(req, serviceInstance, requestDto); } public override object OnAfterExecute(IRequest req, object response, object service) { // Called just after any Action is executed, you can modify the response returned here as well } public override Task HandleExceptionAsync(IRequest req, TRequest requestDto, Exception ex, object instance) { // Called whenever an exception is thrown in your Services Action } } ``` ## Limitations One limitation of Services is that you can't split the handling of a single Resource (i.e. Request DTO) over multiple service implementations. If you find you need to do this because your service is getting too big, consider using partial classes to spread the implementation over multiple files. Another option is encapsulating some of the re-usable functionality into Logic dependencies and inject them into your service. ## Other Notes Although they're not needed or used anywhere [you can also use HTTP Verb interfaces](https://github.com/ServiceStack/ServiceStack/blob/34acc429ee04053ea766e4fb183e7aad7321ef5e/src/ServiceStack.Interfaces/IService.cs#L27) to enforce the correct signature required by the services, e.g: ```csharp public class MyService : Service, IAny, IGet, IPost { public object Any(GetContacts request) { .. } public object Get(SearchContacts request) { .. } public object Post(Contact request) { .. } } ``` This has no effect to the runtime behaviour and your services will work the same way with or without the added interfaces. # Service Return Types Source: https://docs.servicestack.net/service-return-types From a birds-eye view ServiceStack can return any of: - Any **DTO** object -> serialized to Response ContentType - `HttpResult`, `HttpError`, `CompressedResult` or other `IHttpResult` for Customized HTTP response #### Services should only return Reference Types If a Value Type like `int` or `long` response is needed, it's recommended to wrap the Value Type in a Response DTO, e.g: ```csharp public class MyResponse { public int Result { get; set; } } ``` Alternatively you can return a naked Value Type response by returning it as a `string`, e.g: ```csharp public object Any(MyRequest request) => "1"; ``` ## Different Return Types The following types are not converted (to different Content-Types) but get written directly to the Response Stream: - `String` - `Stream` - `IStreamWriter` - `byte[]` - with the `application/octet-stream` Content Type - `ReadOnlyMemory` - `ReadOnlyMemory` From the [HelloWorld ServiceStack.UseCase](https://github.com/ServiceStack/ServiceStack.UseCases/blob/master/HelloWorld/Global.asax.cs) demo: ```csharp public class HelloService : Service { public HelloResponse Get(Hello request) { return new HelloResponse { Result = $"Hello, {request.Name}!" }; //C# client can call with: //var response = client.Get(new Hello { Name = "ServiceStack" }); } public string Get(HelloHtml request) { return $"

Hello, {request.Name}!

"; } [AddHeader(ContentType = "text/plain")] public string Get(HelloText request) { return $"

Hello, {request.Name}!

"; } [AddHeader(ContentType = "image/png")] public Stream Get(HelloImage request) { var width = request.Width.GetValueOrDefault(640); var height = request.Height.GetValueOrDefault(360); var bgColor = request.Background != null ? Color.FromName(request.Background) : Color.ForestGreen; var fgColor = request.Foreground != null ? Color.FromName(request.Foreground) : Color.White; var image = new Bitmap(width, height); using (var g = Graphics.FromImage(image)) { g.Clear(bgColor); var drawString = $"Hello, {request.Name}!"; var drawFont = new Font("Times", request.FontSize.GetValueOrDefault(40)); var drawBrush = new SolidBrush(fgColor); var drawRect = new RectangleF(0, 0, width, height); var drawFormat = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center }; g.DrawString(drawString, drawFont, drawBrush, drawRect, drawFormat); var ms = new MemoryStream(); image.Save(ms, ImageFormat.Png); return ms; } } } ``` #### Live Examples of the above Hello Service: - [/hello/ServiceStack](http://bootstrapapi.apphb.com/api/hello/ServiceStack) - [/hello/ServiceStack?format=json](http://bootstrapapi.apphb.com/api/hello/ServiceStack?format=json) - [/hellotext/ServiceStack](http://bootstrapapi.apphb.com/api/hellotext/ServiceStack) - [/hellohtml/ServiceStack](http://bootstrapapi.apphb.com/api/hellohtml/ServiceStack) - [/helloimage/ServiceStack?Width=600&height=300&Foreground=Yellow](http://bootstrapapi.apphb.com/api/helloimage/ServiceStack?Width=600&height=300&Foreground=Yellow) ### Content-Type Specific Service Implementations Service implementations can use `Verb{Format}` method names to provide a different implementation for handling a specific Content-Type, e.g. the Service below defines several different implementation for handling the same Request: ```csharp [Route("/my-request")] public class MyRequest { public string Name { get; set; } } public class ContentTypeServices : Service { // Handles all other unspecified Verbs/Formats to /my-request public object Any(MyRequest request) => ...; // Handles GET /my-request for JSON responses public object GetJson(MyRequest request) => ..; // Handles POST/PUT/DELETE/etc /my-request for HTML Responses public object AnyHtml(MyRequest request) => $@"

AnyHtml {request.Name}

"; // Handles GET /my-request for HTML Responses public object GetHtml(MyRequest request) => $@"

GetHtml {request.Name}

"; } ``` This convention can be used for any of the formats listed in `ContentTypes.KnownFormats`, which by default includes: - json - xml - jsv - csv - html - protobuf - msgpack - wire ## Partial Content Support Partial Content Support allows a resource to be split up an accessed in multiple chunks for clients that support HTTP Range Requests. This is a popular feature in download managers for resuming downloads of large files and streaming services for real-time streaming of content (e.g. consumed whilst it's being watched or listened to). [HTTP Partial Content Support](http://benramsey.com/blog/2008/05/206-partial-content-and-range-requests/) is added in true ServiceStack-style where it's now automatically and transparently enabled for any existing services returning: #### A Physical File ```csharp return new HttpResult(new FileInfo(filePath), request.MimeType); ``` #### A Virtual File ```csharp return new HttpResult(VirtualFileSources.GetFile(virtualPath)); ``` #### A Memory Stream ```csharp return new HttpResult(ms, "audio/mpeg"); ``` #### Raw Bytes ```csharp return new HttpResult(bytes, "image/png"); ``` #### Raw Text ```csharp return new HttpResult(customText, "text/plain"); ``` Partial Content was also added to static file downloads served directly through ServiceStack which lets you stream mp3 downloads or should you ever want to your static .html, .css, .js, etc. You can disable Partial Content support with `Config.AllowPartialResponses = false;`. See the [PartialContentResultTests](https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.Endpoints.Tests/PartialContentResultTests.cs) for more examples. ## Writing directly to the Response Stream In addition to returning plain C# objects, ServiceStack allows you to return any **Stream** or `IStreamWriterAsync` (which is a bit more flexible on how you write to the response stream): ```csharp public interface IStreamWriterAsync { Task WriteToAsync(Stream responseStream, CancellationToken token=default); } ``` Both though allow you to write directly to the Response OutputStream without any additional conversion overhead. ### Customizing HTTP Headers If you want to customize the HTTP headers at the same time you just need to implement [IHasOptions](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Web/IHasOptions.cs) where any Dictionary Entry is written to the Response HttpHeaders. ```csharp public interface IHasOptions { IDictionary Options { get; } } ``` Further than that, the IHttpResult allows even finer-grain control of the HTTP output (status code, headers, ...) where you can supply a custom Http Response status code. You can refer to the implementation of the [HttpResult](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/HttpResult.cs) object for a real-world implementation of these above interfaces. ### Further customizing the HTTP Response See the [Customize HTTP Responses](/customize-http-responses) page for more ways of customizing the HTTP Response. # Design RESTful Services Source: https://docs.servicestack.net/design-rest-services ServiceStack encourages a message-based design so each Service should have its own distinct message (aka Request DTO) where it's able to use explicit properties to define what each Service accepts. Something to keep in mind is how you define and design your Services in ServiceStack are de-coupled in how you expose them which can be exposed under any custom Route. ### Use a logical / hierarchical Url structure We recommend adopting a logical hierarchically structured URL that represents the identifier of a resource, i.e. the parent path categorizes your resource and gives it meaningful context. So if you needed to design an API for System that maintained **Events** and their **Reviews** it could adopt the following url structure: ``` /events # all events /events/1 # event #1 /events/1/reviews # event #1 reviews ``` Where each of the above resource identifiers can be invoked using any HTTP **Verb** which represents the action to take on them, e.g: ``` GET /events # View all Events POST /events # Create a new Event PUT /events/{Id} # Update an existing Event DELETE /events/{Id} # Delete an existing Event ``` ### Implementing RESTful Routes For their implementation ServiceStack encourages a message-based design that groups all related operations based on **Response type** and **Call Context**. For an Events and Reviews system it could look something like: ```csharp [Route("/events", "GET")] [Route("/events/category/{Category}", "GET")] // Optional GET example public class SearchEvents : IReturn> { //resultset filter examples, e.g. ?Category=Tech&Query=servicestack public string Category { get; set; } public string Query { get; set; } } [Route("/events", "POST")] public class CreateEvent : IReturn { public string Name { get; set; } public DateTime StartDate { get; set; } } [Route("/events/{Id}", "GET")] [Route("/events/code/{EventCode}", "GET")] // Alternative Id public class GetEvent : IReturn { public int Id { get; set; } public string EventCode { get; set; } // Alternative to fetch Events } [Route("/events/{Id}", "PUT")] public class UpdateEvent : IReturnVoid { public int Id { get; set; } public string Name { get; set; } public DateTime StartDate { get; set; } } ``` Event Reviews would follow a similar pattern: ```csharp [Route("/events/{EventId}/reviews", "GET")] public class GetEventReviews : IReturn> { public int EventId { get; set; } } [Route("/events/{EventId}/reviews/{Id}", "GET")] public class GetEventReview : IReturn { public int EventId { get; set; } public int Id { get; set; } } [Route("/events/{EventId}/reviews", "POST")] public class CreateEventReview : IReturn { public int EventId { get; set; } public string Comments { get; set; } } ``` The above REST Service examples returns naked Types and collections which [ServiceStack has a great story for](/api-design#structured-error-handling), however our personal preference is to design more coarse-grained and versionable [Message-based APIs](/design-message-based-apis) where we'd use an explicit Response DTO for each Service, e.g: ```csharp [Route("/events/{EventId}/reviews", "GET")] public class GetEventReviews : IReturn { public int EventId { get; set; } } public class GetEventReviewsResponse { public List Results { get; set; } } [Route("/events/{EventId}/reviews/{Id}", "GET")] public class GetEventReview : IReturn { public int EventId { get; set; } public int Id { get; set; } } public class GetEventReviewResponse { public EventReview Result { get; set; } public ResponseStatus ResponseStatus { get; set; } // inject structured errors if any } [Route("/events/{EventId}/reviews", "POST")] public class CreateEventReview : IReturn { public int EventId { get; set; } public string Comments { get; set; } } public class CreateEventReviewResponse { public EventReview Result { get; set; } public ResponseStatus ResponseStatus { get; set; } } ``` ### Notes The implementation of each Services then becomes straight-forward based on these messages, which (depending on code-base size) we'd recommend organizing in 2 **EventsService** and **EventReviewsService** classes. Although `UpdateEvent` and `CreateEvent` are seperate Services here, if the use-case permits they can instead be handled by a single idempotent `StoreEvent` Service. ## [Physical Project Structure](/physical-project-structure) Ideally the root-level **AppHost** project should be kept lightweight and implementation-free. Although for small projects or prototypes with only a few services it's ok for everything to be in a single project and to simply grow your architecture when and as needed. For medium-to-large projects we recommend the physical structure below which for the purposes of this example we'll assume our Application is called **Events**. The order of the projects also show its dependencies, e.g. the top-level `Events` project references **all** sub projects whilst the last `Events.ServiceModel` project references **none**: ``` /Events AppHost.cs // ServiceStack Web or Self Host Project /Events.ServiceInterface // Service implementations (akin to MVC Controllers) EventsService.cs EventsReviewsService.cs /Events.Logic // For large projects: extract C# logic, data models, etc IGoogleCalendarGateway // E.g of a external dependency this project could use /Events.ServiceModel // Service Request/Response DTOs and DTO types Events.cs // SearchEvents, CreateEvent, GetEvent DTOs EventReviews.cs // GetEventReviews, CreateEventReview Types/ Event.cs // Event type EventReview.cs // EventReview type ``` With the `Events.ServiceModel` DTO's kept in their own separate implementation and dependency-free dll, you're freely able to share this dll in any .NET client project as-is - which you can use with any of the generic [C# Service Clients](/csharp-server-events-client) to provide an end-to-end typed API without any code-gen. ## More Info - This recommended project structure is embedded in all [ServiceStackVS VS.NET Templates](/templates/). - The [Simple Customer REST Example](/why-servicestack#simple-customer-database-rest-services-example) is a small self-contained, real-world example of creating a simple REST Service utilizing an RDBMS. # Design Message-based APIs Source: https://docs.servicestack.net/design-message-based-apis To give you a flavor of the differences you should think about when designing message-based services in ServiceStack we'll look at some examples to contrast WCF/WebApi vs ServiceStack's approach: ## WCF vs ServiceStack API Design WCF encourages you to think of web services as normal C# method calls, e.g: ```csharp public interface IWcfCustomerService { Customer GetCustomerById(int id); List GetCustomerByIds(int[] id); Customer GetCustomerByUserName(string userName); List GetCustomerByUserNames(string[] userNames); Customer GetCustomerByEmail(string email); List GetCustomerByEmails(string[] emails); } ``` This is what the same Service contract would look like in ServiceStack: ```csharp public class Customers : IReturn> { public int[] Ids { get; set; } public string[] UserNames { get; set; } public string[] Emails { get; set; } } ``` The important concept to keep in mind is that the entire query (aka Request) is captured in the Request Message (i.e. Request DTO) and not in the server method signatures. The obvious immediate benefit of adopting a message-based design is that any combination of the above RPC calls can be fulfilled in 1 remote message, by a single service implementation which improves cacheability and simplifies maintenance and testing with the reduced API surface area. ## WebApi vs ServiceStack API Design Likewise WebApi promotes a similar C#-like RPC Api that WCF does: ```csharp public class ProductsController : ApiController { public IEnumerable GetAllProducts() { return products; } public Product GetProductById(int id) { var product = products.FirstOrDefault((p) => p.Id == id); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return product; } public Product GetProductByName(string categoryName) { var product = products.FirstOrDefault((p) => p.Name == categoryName); if (product == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return product; } public IEnumerable GetProductsByCategory(string category) { return products.Where(p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase)); } public IEnumerable GetProductsByPriceGreaterThan(decimal price) { return products.Where((p) => p.Price > price); } } ``` ### ServiceStack Message-Based API Design Whilst ServiceStack encourages you to retain a Message-based Design: ```csharp public class SearchProducts : IReturn> { public string Category { get; set; } public decimal? PriceGreaterThan { get; set; } } public class GetProduct : IReturn { public int? Id { get; set; } public string Name { get; set; } } public class ProductsService : Service { public object Get(SearchProducts request) { var ret = products.AsQueryable(); if (request.Category != null) ret = ret.Where(x => x.Category == request.Category); if (request.PriceGreaterThan.HasValue) ret = ret.Where(x => x.Price > request.PriceGreaterThan.Value); return ret.ToList(); } public Product Get(GetProduct request) { var product = request.Id.HasValue ? products.FirstOrDefault(x => x.Id == request.Id.Value) : products.FirstOrDefault(x => x.Name == request.Name); if (product == null) throw new HttpError(HttpStatusCode.NotFound, "Product does not exist"); return product; } } ``` Again capturing the essence of the Request in the Request DTO. The message-based design is also able to condense **5 separate RPC** WebAPI Services into **2 message-based** ServiceStack Services. ## Group by Call Semantics and Response Types It's grouped into 2 different services in this example based on **Call Semantics** and **Response Types**: Every property in each Request DTO has the same semantics that is for `SearchProducts` each property acts like a Filter (e.g. an AND) whilst in `GetProduct` it acts like a combinator (e.g. an OR). The Services also return `List` and `Product` return types which will require different handling in the call-sites of Typed APIs. In WCF / WebAPI (and other RPC services frameworks) whenever you have a client-specific requirement you would add a new Server signature on the controller that matches that request. In ServiceStack's message-based approach however you're instead encouraged to think about where this feature intuitively fits and whether you're able to enhance existing services. You should also be thinking about how you can support the client-specific requirement in a **generic way** so that the same service could benefit other future potential use-cases. ### Separate One and Many Services We can use the above context as a guide to design new Services. If we needed to design a Bookings System that needed an API to return **All Bookings** and a **Single Booking** we'd use a separate Services as they'd have different Response Types, e.g. `GetBooking` returns 1 booking whilst `GetBookings` returns many. ### Distinguish Service Operations vs Types There should be a clean split between your Operations (aka Request DTOs) which is unique per service and is used to capture the Services' request, and the DTO types they return. Request DTOs are usually actions so they're verbs, whilst DTO types are entities/data-containers so they're nouns. ### Returning naked collections ServiceStack can return naked collections that [don't require a ResponseStatus](/error-handling#error-response-types) property since if it doesn't exist the generic `ErrorResponse` DTO will be thrown and serialized on the client instead which frees you from having your Responses contain `ResponseStatus` property. ### Returning coarse-grained Response DTOs However since they offer better versionability that can later be extended to return more results without breaking existing clients we prefer specifying explicit Response DTOs for each Service, although this is entirely optional. So our preferred message-based would look similar to: ```csharp // Operations [Route("/bookings/{Id}")] public class GetBooking : IReturn { public int Id { get; set; } } public class GetBookingResponse { public Booking Result { get; set; } public ResponseStatus ResponseStatus { get; set; } // inject structured errors } [Route("/bookings/search")] public class SeachBookings : IReturn { public DateTime BookedAfter { get; set; } } public class SeachBookingsResponse { public List Results { get; set; } public ResponseStatus ResponseStatus { get; set; } // inject structured errors } // Types public class Booking { public int Id { get; set; } public int ShiftId { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public int Limit { get; set; } } ``` When they're not ambiguous we'll typiclly leave out specifying the **Verb** in `[Route]` definitions for **GET** Requests as its unnecessary. ### Using AutoQuery Where possible we'll also use [AutoQuery for Search Services](/autoquery/rdbms) which require dramatically less effort whilst offering a lot more functionality out-of-the-box. E.g. The Search Bookings Service with AutoQuery could adopt the same Customer Route and properties: ```csharp [Route("/bookings/search")] public class SeachBookings : QueryDb { public DateTime BookedAfter { get; set; } } ``` But no implementation is needed as AutoQuery automatically creates the optimal implementation. AutoQuery also supports [Implicit Conventions](/autoquery/rdbms#implicit-conventions) where you're able to filter by any of `Booking` table columns without any additional code or effort. ### Keep a consistent Nomenclature You should reserve the word **Get** on services which query on unique or Primary Keys fields, i.e. when a supplied value matches a field (e.g. Id) it only **Gets** 1 result. For "Search Services" that acts like a filter and returns multiple matching results which falls within a desired range we recommend using prefixing Services with the **Search** or **Find** verbs to signal the behavior of the Service. ### Self-describing Service Contracts Also try to be descriptive with each of your field names, these properties are part of your **public API** and should be self-describing as to what it does. E.g. By just looking at the Service Contract (e.g. Request DTO) we'd have no idea what a plain **Date** property means, as it could mean either **BookedAfter**, **BookedBefore** or **BookedOn** if it only returned bookings made on that Day. The benefit of this is now the call-sites of your [Typed .NET clients](/csharp-client) become easier to read: ```csharp Product product = client.Get(new GetProduct { Id = 1 }); var response = client.Get(new SearchBookings { BookedAfter = DateTime.Today }); ``` ## Service implementation [Filter Attributes](/filter-attributes) can be applied on either the **class** or **method** level, so when you need to secure all Operations within a given Service you can just annotate the top-level Service class with the `[Authenticate]`, e.g: ```csharp [Authenticate] public class BookingsService : Service { public object Get(GetBooking request) => ...; public object Get(SearchBookings request) => ...; } ``` ## Error Handling and Validation For info on how to add validation you either have the option to just [throw C# exceptions](/error-handling#throwing-c-exceptions) and apply your own customizations to them, in addition you also have the option to use the built-in [Declarative Validator](/declarative-validation) attributes on your Request DTO: ```csharp [ValidateIsAuthenticated] public class CreateBooking : IPost, IReturn { [ValidateNotNull] public DateTime? StartDate { get; set; } [ValidateGreatorThan(0)] public int ShiftId { get; set; } [ValidateGreatorThan(0)] public int Limit { get; set; } } ``` Or for more control you can use custom [Fluent Validation](/validation) validators. Validators are no-touch and invasive free meaning you can add them using a layered approach and maintain them without modifying the service implementation or DTO classes. Since they require an extra class We'd only use them on operations with side-effects e.g. **POST** or **PUT**, as **GET** requests tend to have minimal validation so throwing C# Exceptions typically requires less boilerplate. Here's an example of a validator you could have when creating a Booking: ```csharp public class CreateBookingValidator : AbstractValidator { public CreateBookingValidator() { RuleFor(r => r.StartDate).NotEmpty(); RuleFor(r => r.ShiftId).NotEmpty().GreaterThan(0); RuleFor(r => r.Limit).NotEmpty().GreaterThan(0); } } ``` Depending on the use-case instead of having separate `CreateBooking` and `UpdateBooking` DTOs you could re-use the same `StoreBooking` Request DTO to handle both operations. # Modular Startup Source: https://docs.servicestack.net/modular-startup ::: info For more information on the earlier Modular Startup in ServiceStack **v5.x** see our [Legacy Modular Startup](/modular-startup-legacy) docs ::: Taking advantage of C# 9 top level statements and .NET 10 [WebApplication Hosting Model](https://gist.github.com/davidfowl/0e0372c3c1d895c3ce195ba983b1e03d), ServiceStack templates by utilize both these features to simplify configuring your AppHost in a modular way. `Program.cs` becomes a script-like file since C# 9 top level statements are generating application entry point implicitly. ```csharp var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); app.UseHttpsRedirection(); } app.UseServiceStack(new AppHost()) app.Run(); ``` The application `AppHost` hooks into startup using `HostingStartup` assembly attribute. In ServiceStack templates, this uses the file name prefix of `Configure.*.cs` to help identify these startup modules. All ServiceStack's features are loaded using .NET's `HostingStartup`, including ServiceStack's `AppHost` itself that's now being configured in [Configure.AppHost.cs](https://github.com/NetCoreTemplates/web/blob/master/MyApp/Configure.AppHost.cs), e.g: ```csharp [assembly: HostingStartup(typeof(MyApp.AppHost))] namespace MyApp; public class AppHost() : AppHostBase("MyApp"), IHostingStartup { public void Configure(IWebHostBuilder builder) => builder .ConfigureServices(services => { // Configure ASP.NET Core IOC Dependencies }); public override void Configure() { // Configure ServiceStack, Run custom logic after ASP.NET Core Startup SetConfig(new HostConfig { }); } } ``` The use of Modular Startup does not change the AppHost declaration, but enables the modular grouping of configuration concerns. Different features are encapsulated together allowing them to be more easily updated or replaced, e.g. each feature could be temporarily disabled by commenting out its assembly HostingStartup's attribute: ```csharp //[assembly: HostingStartup(typeof(MyApp.AppHost))] ``` ## Module composition using `mix` This has enabled ServiceStack Apps to be easily composed with the features developers need in mind. Either at project creation with servicestack.net/start page or after a project's creation where features can easily be added and removed using the command-line [mix tool](/mix-tool) where you can view all available mix gists that can be added to projects with: :::sh x mix ::: .NET 10's idiom is incorporated into the [mix gist config files](https://gist.github.com/gistlyn/9b32b03f207a191099137429051ebde8) to adopt its `HostingStartup` which is better able to load modular Startup configuration without assembly scanning. This is a standard ASP .NET Core feature that we can use to configure Mongo DB in any ASP .NET Core App with: :::sh npx add-in mongodb ::: Which adds the `mongodb` gist file contents to your ASP .NET Core Host project: ```csharp using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; [assembly: HostingStartup(typeof(MyApp.ConfigureMongoDb))] namespace MyApp; public class ConfigureMongoDb : IHostingStartup { public void Configure(IWebHostBuilder builder) => builder .ConfigureServices((context, services) => { var mongoClient = new MongoClient(); IMongoDatabase mongoDatabase = mongoClient.GetDatabase("MyApp"); services.AddSingleton(mongoDatabase); }); } ``` As it's not a ServiceStack feature it can be used to configure ASP .NET Core Apps with any feature, e.g. we could also easily configure [Marten](https://martendb.io) in an ASP .NET Core App with: :::sh npx add-in marten ::: The benefit of this approach is entire modules of features can be configured in a single command, e.g. An empty ServiceStack App can be configured with MongoDB, ServiceStack Auth and a MongoDB Auth Repository with a single command: :::sh npx add-in auth auth-mongodb mongodb ::: Likewise, you can replace MongoDB with a completely different PostgreSQL RDBMS implementation by running: :::sh npx add-in auth auth-db postgres ::: ### Services and App Customizations Modular Startup configurations are flexible enough to encapsulate customizing ASP.NET Core's IOC and the built `WebApplication` by registering a `IStartupFilter` which is required by the Open API v3 Modular Configuration: :::sh npx add-in openapi3 ::: ```csharp [assembly: HostingStartup(typeof(MyApp.ConfigureOpenApi))] namespace MyApp; public class ConfigureOpenApi : IHostingStartup { public void Configure(IWebHostBuilder builder) => builder .ConfigureServices((context, services) => { if (context.HostingEnvironment.IsDevelopment()) { services.AddEndpointsApiExplorer(); services.AddSwaggerGen(); services.AddServiceStackSwagger(); services.AddBasicAuth(); //services.AddJwtAuth(); services.AddTransient(); } }); public class StartupFilter : IStartupFilter { public Action Configure(Action next) => app => { app.UseSwagger(); app.UseSwaggerUI(); next(app); }; } } ``` ### ConfigureAppHost Looking deeper, we can see where we're plugins are able to configure ServiceStack via the `.ConfigureAppHost()` extension method to execute custom logic on `AppHost` Startup: ```csharp [assembly: HostingStartup(typeof(MyApp.ConfigureAutoQuery))] namespace MyApp; public class ConfigureAutoQuery : IHostingStartup { public void Configure(IWebHostBuilder builder) => builder .ConfigureServices(services => { // Enable Audit History services.AddSingleton(c => new OrmLiteCrudEvents(c.GetRequiredService())); // For TodosService services.AddPlugin(new AutoQueryDataFeature()); // For Bookings https://docs.servicestack.net/autoquery-crud-bookings services.AddPlugin(new AutoQueryFeature { MaxLimit = 1000, //IncludeTotal = true, }); }) .ConfigureAppHost(appHost => { appHost.Resolve().InitSchema(); }); } ``` ### Customize AppHost at different Startup Lifecycles By default, any AppHost configuration is called before `AppHost.Configure()` is run, but to cater for all plugins, AppHost configurations can be registered at different stages within the AppHost's initialization: ```csharp public void Configure(IWebHostBuilder builder) => builder .ConfigureAppHost( beforeConfigure: appHost => /* fired before AppHost.Configure() */, afterConfigure: appHost => /* fired after AppHost.Configure() */, afterPluginsLoaded: appHost => /* fired after plugins are loaded */, afterAppHostInit: appHost => /* fired after AppHost has initialized */); ``` ### Removing Features The benefits of adopting a modular approach to AppHost configuration is the same as general organizational code structure which results in better decoupling and cohesion where it's easier to determine all the dependencies of a feature, easier to update, less chance of unintended side effects, easier to share standard configuration amongst multiple projects and easier to remove the feature entirely, either temporarily if needing to isolate & debug a runtime issue by: ```csharp // [assembly: HostingStartup(typeof(MyApp.ConfigureAuth))] ``` Or easier to permanently replace or remove features by either directly deleting the isolated `*.cs` source files or by undoing mixing in the feature using `mix -delete`, e.g: :::sh npx add-in -delete auth auth-db postgres ::: Which works similar to package managers where it removes all files contained within each mix gist. ::: info Please see the [Mix HowTo](https://gist.github.com/gistlyn/9b32b03f207a191099137429051ebde8#file-mix_howto-md) to find out how you can contribute your own gist mix features ::: ## Migrating to HostingStartup As we'll be using the new `HostingStartup` model going forward we recommend migrating your existing configuration to use them. To help with this you can refer to the [mix diff](https://github.com/ServiceStack/mix/commit/b56746622aa1879e3e6a8cbf835e634f05db30db) showing how each of the existing mix configurations were converted to the new model. As a concrete example, lets take a look at the steps used to migrate our Chinook example application [from NET5 using the previous `Startup : ModularStartup`, to .NET 6 `HostingStartup`](https://github.com/NetCoreApps/Chinook/commit/2758af9deae9c3aa910a27134f95167f7ec6e541). ### Step 1 Migrate your existing `ConfigureServices` and `Configure(IApplicationBuilder)` from `Startup : ModularStartup` to the top-level host builder in `Program.cs`. Eg ```csharp var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. // You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); app.UseHttpsRedirection(); } app.Run(); ``` ### Step 2 Move your `AppHost` class to a new `Configure.AppHost.cs` file. ### Step 3 Implement `IHostingStartup` on your AppHost with automatic initialization. Eg: ```csharp public void Configure(IWebHostBuilder builder) { builder.ConfigureServices(services => { // Configure ASP.NET Core IOC Dependencies }); } ``` ### Step 4 Declare `assembly: HostingStartup` for your `AppHost` in the same `Configure.AppHost.cs`. Eg: ```csharp [assembly: HostingStartup(typeof(Chinook.AppHost))] ``` ### Step 5 Migrate each existing modular startup class that implements `IConfgiureServices` and/or `IConfigureApp` to use `IHostingStartup`. Eg: ```csharp // net5.0 modular startup using ServiceStack; namespace Chinook; public class ConfigureAutoQuery : IConfigureAppHost { public void Configure(IAppHost appHost) { appHost.Plugins.Add(new AutoQueryFeature { MaxLimit = 1000, IncludeTotal = true }); } } ``` ```csharp // net8.0 modular startup using IHostingStartup using Microsoft.AspNetCore.Hosting; using ServiceStack; [assembly: HostingStartup(typeof(Chinook.ConfigureAutoQuery))] namespace Chinook; public class ConfigureAutoQuery : IHostingStartup { public void Configure(IWebHostBuilder builder) => builder .ConfigureServices(services => { services.AddPlugin(new AutoQueryFeature { MaxLimit = 1000, IncludeTotal = true }); }); } ``` > Remembering also that infrastructure like your `Dockerfile` or host will likely need the runtimes/SDKs updated as well. # ServiceStack's .NET Core Utility Belt Source: https://docs.servicestack.net/dotnet-tool Our `x` and `app` dotnet tools are a versatile invaluable companion for all ServiceStack developers where it's jam packed with functionality to power a number of exciting scenarios where it serves as a [Sharp App](https://sharpscript.net/docs/sharp-apps) delivery platform where they can be run as a .NET Core Windows Desktop App with `app` or as a cross-platform Web App launcher using `web` and we've already how it's now a [`#Script` runner](https://sharpscript.net/docs/sharp-scripts) with `x run` and into a [Live `#Script` playground](https://sharpscript.net/docs/sharp-scripts#live-script-with-web-watch) with `x watch`. These tools contains all the functionality ServiceStack Developers or API consumers need that can be used [Create ServiceStack projects](/dotnet-new), run [Gist Desktop Apps](https://sharpscript.net/sharp-apps/gist-desktop-apps) or generate typed endpoints for consuming ServiceStack Services by either [Add/Update ServiceStack References](/add-servicestack-reference) or by generating [gRPC client proxies](/grpc#grpc-clients). ## Install To access available features, install with: :::sh dotnet tool install --global x ::: ### Update Or if you had a previous version installed, update with: :::sh dotnet tool update -g x ::: ::: info Both `x` and `app` have equivalent base functionality, whilst `app` has superset [Windows-only Desktop features](/netcore-windows-desktop) ::: ::: info To update and download Add ServiceStack Reference dtos without .NET see [npx get-dtos](/npx-get-dtos) ::: ## Usage Then run `x` without any arguments to view Usage: :::sh x ::: ```txt Usage: x new List available Project Templates x new