In addition to providing a productive dynamic language for generating HTML pages, Template Pages can also be used to rapidly develop Web APIs which can utilize dynamic page routing to easily create data-driven JSON APIs using optimal pretty URLs, in real-time without any C# classes or compilation in sight!

The only difference between a Template Page that generates HTML or a Template Page that returns an API Response is that API pages use the return filter to return a value.

E.g. To create a Hello World C# ServiceStack Service you would typically create a Request DTO, Response DTO and a Service implementation:

[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
    public string Name { get; set; }
}
public class HelloResponse
{
    public string Result { get; set; }
}
public class HelloService : Service
{
    public object Any(Hello request) => $"Hello, {request.Name}!";
}

/hello API Page

Usage: /hello/{name}

An API which returns the same wire response as above can be implemented in API Pages by creating a page at /hello/_name/index.html with the contents:

{{ { result: `Hello, ${name}!` } | return }}

Which supports the same content negotiation as a ServiceStack Service where calling it in a browser will generate a human-friendly HTML Page:

Where as calling it with a JSON HTTP client containing Accept: application/json HTTP Header or with a ?format=json query string will return the API response in the JSON Format:

Alternatively you can force a JSON Response by specifying it with:

{{ { result: `Hello, ${name}!` } | return({ format: 'json' }) }} 
// Equivalent to:
{{ { result: `Hello, ${name}!` } | return({ contentType: 'application/json' }) }}

/preview API Page

Usage: /preview?content={templates}

The /preview.html page uses this to force a plain-text response with:

{{ content  | evalTemplate({use:{plugins:'MarkdownTemplatePlugin'}}) | assignTo:response }}
{{ response | return({ contentType:'text/plain' }) }}

The preview API above is what provides this Blog's Live Preview feature where it will render any ServiceStack Templates provided in the content Query String or HTTP Post Form Data, e.g:

Which renders the plain text response:

0,1,4,9,16,25,36,49,64,81,

/_user/api Page

Usage: /{user}/api

The /_user/api.html API page shows an example of how easy it is to create data-driven APIs where you can literally return the response of an SQL query by calling the dbSelect filter and returning the results with:

{{ `SELECT * 
      FROM Post p INNER JOIN UserInfo u on p.CreatedBy = u.UserName 
     WHERE UserName = @user 
    ORDER BY p.Created DESC` 
   | dbSelect({ user })
   | return }}

The user argument is populated as a result of dynamic route from the _user directory name which will let you view all @ServiceStack posts with:

Which also benefits from ServiceStack's multiple formats where the same API can be returned in:

Which thanks to the live development workflow provides the most productive development experience to rapidly develop Web APIs or perform common tasks like viewing adhoc SQL queries in Excel which can be further manipulated using the LINQ-like expressiveness and wrist-friendly filters available in Templates.

/posts/_slug/api Page

Usage: /posts/{slug}/api

The /posts/_slug/api.html page shows an example of using the httpResult filter to return a custom HTTP Response where if the post with the specified slug does not exist it will return a 404 Post was not found HTTP Response:

{{ `SELECT * 
      FROM Post p INNER JOIN UserInfo u on p.CreatedBy = u.UserName 
     WHERE Slug = @slug 
     ORDER BY p.Created DESC` 
   | dbSingle({ slug })
   | assignTo: post 
}}
{{ post ?? httpResult({ status:404, statusDescription:'Post was not found' }) 
   | return }}

The httpResult filter returns a ServiceStack HttpResult which allows for the following customization's:

httpResult({ 
  status:            404,
  status:            'NotFound' // can also use .NET HttpStatusCode enum name
  statusDescription: 'Post was not found',
  response:          post,
  format:            'json',
  contentType:       'application/json',
  'X-Powered-By':    'ServiceStack Templates',
}) 

Any other arguments like 'X-Powered-By' are returned as HTTP Response Headers.

This behaves similarly to customizing a response with return arguments:

{{ post | return({ format:'json', 'X-Powered-By':'ServiceStack Templates' }) }}

Using the explicit httpResult filter is useful for returning a custom HTTP Response that doesn't have a Response Body, e.g. the New Post page uses httpFilter to redirect back to the Users posts page after they've successfully created a new Post:

{{#if success}}
    {{ httpResult({ status:301, Location:`/${userName}` }) | return }}
{{/if}}

For more examples and info on API Pages checkout to the API Pages docs.