Guide
This section explores Swytch's core features and how to use them effectively. You'll learn how to define routes, write handler methods, use middleware, serve static files, configure your application etc. Each topic is explained with examples to help you get started quickly. Whether you're building a simple API, a full web app or even a static site, this section will expose to you everything you need to know to make the most of Swytch.
Routing
Swytch routes incoming requests by identifying its HTTP method and request path, and matching it against already registered routes.
It considers both the HTTP request method and the exact path that came with the request. If no match is found, a 405 Method Not Allowed
response is returned. Swytch also supports dynamic path parameters, which you can define using curly braces like this {name}
in
the route path when registering or adding an action to your application. These parameters capture values from the URL as strings, with no automatic type conversion.
For example, a route like /users/{id}
will match requests paths such as /users/42
, where 42
is parsed, retrieved and
provided(as the value of id
) to you as a string. These captured path values, along with all query parameters, are made available
in the RequestContext
type in the PathParams
and QueryParams
collection properties, allowing easy access.
AddAction
Routes in Swytch are registered using the AddAction
method, which defines how requests should be handled.
It takes three parameters:
- HTTP Methods – A capitalized string representing one or more HTTP methods (e.g.,
"GET"
,"POST"
, or"GET,POST"
). - Request Path – A path relative to the root (e.g.,
"/users/{id}"
). - Action Method – A method that can processes an incoming request.
Note that routes are matched exactly, meaning
"/about/"
is not the same as"/about"
.
An action method, also known as a handler, is a method that processes an incoming request. For a method to be valid and qualify to
process an incoming request, it must match the Action<Task, RequestContext>
delegate, meaning it accepts a RequestContext
type parameter
and returns a Task
. Note that instead inline lambdas like we have been doing all this while, we can also structure the code
differently by defining handler methods in a class and passing them by reference because remember a valid action method just has to
satisfy an Action<Task, RequestContext>
delegate . For example:
public class UserController {
public static async Task GetUser(RequestContext context) {
// Handle request
}
}
//usage
var userController = new UserController();
swytchApp.AddAction("GET", "/users/{id}", userController.GetUser);
This approach offers an alternative code organization approach by keeping route definitions clean while keeping handlers separate.
Multiple HTTP Methods
swytchApp.AddAction("GET,POST", "/", async (context) => {...});
GET
and POST
requests to "/"
are handled by the same action/handler method.
Route Matching Order
Routes are matched in the order they are registered. If you register the same path separately for different HTTP methods, the first one will always take precedence.
For example:
swytchApp.AddAction("GET", "/submit", async (context) => {...}));
swytchApp.AddAction("POST", "/submit", async (context) => {...}));
In this case, a POST
request to "/about"
will still match the first GET
route,
resulting in a 405 Method Not Allowed response.
To prevent this, define both methods in a single AddAction
and make a decision how you handle
a request based on what HTTP method it came with:
app.AddAction("GET,POST", "/submit", async (context) => {
if (context.Request.HttpMethod == "POST")
{
//handle POST request
return;
}
//handle GET request
await context.ServeFile("SubmitPage.html",HttpStatusCode.OK);
});
GET
and POST
requests will correctly match the intended handler.
Middleware
Middleware concept in Swytch is simple and straightforward. Each middleware is queued for execution
before the request reaches the intended handler method. This allows you to do cool stuff like modify the RequestContext
,
inspect, transform the request payload or enforce custom logic before the request is processed by the main handler.
For example, Swytch's AddAuthentication method works by registering a middleware that will execute a user provided authentication method. This ensures that authentication checks are performed before the request reaches the actual handler.
To add custom middleware, you use the AddMiddleware
method, which takes a single parameter, a method
matching the Action<RequestContext,Task>
delegate. The order in which middlewares are registered
determines the order in which they execute.
Example
swytchApp.AddMiddleware(async (context) => {
context.Response.Headers["X-Custom-Header"] = "Middleware Added";
});
In this example, the middleware adds a custom header to every response before the request reaches its handler. Since middlewares execute in the order they are registered, later middlewares or the final handler can rely on modifications made by earlier ones.
Authentication
Swytch provides a mechanism to add authentication across your entire application or leave it completely
open. Authentication is added using the AddAuthentication
method on an instance of SwytchApp
.
By default, authentication is disabled. Calling AddAuthentication
enables authentication, requiring
each request to pass an authentication check before reaching its intended handler.
How It Works?
The AddAuthentication
method takes a delegate of type Action<RequestContext, Task<AuthResponse>>
,
referred to as the AuthHandler. This method can be simply described an asynchronous method
receives the RequestContext
and must return an
AuthResponse
, which determines whether the request is authenticated or not.
AuthResponse
The AuthResponse struct represents the outcome of a request authentication in Swytch.Your
authentication logic should return an instance of AuthResponse, setting the IsAuthenticated
property to true or false
based on whether the request is authenticated.
Additionally, you can attach a ClaimsPrincipal to the AuthResponse instance using the ClaimsPrincipal
property
which contains any claims related to
the authenticated user. These claims can be accessed downstream in your handler via context.User
,
which is of type ClaimsPrincipal.
When authentication is enabled, Swytch automatically queues the AuthHandler as middleware, meaning
it executes on every incoming request. If authentication succeeds, the IsAuthenticated
flag is set to
true
, allowing the request to proceed. If authentication fails, Swytch blocks the request before it
reaches any handler, ensuring unauthorized requests never reach protected resources.
Example
Add A AuthHandler
swytchApp.AddAuthentication(async (context) => {
var token = context.Request.Headers["Authorization"];
// Simulate an async token validation
await Task.Delay(50);
if (token == "valid-token")
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "JohnDoe"),
new Claim(ClaimTypes.Role, "Admin")
};
var identity = new ClaimsIdentity(claims, "Bearer");
var principal = new ClaimsPrincipal(identity);
return new AuthResponse
{
IsAuthenticated = true,
ClaimsPrincipal = principal
};
}
return new AuthResponse { IsAuthenticated = false };
});
In this example, the authentication handler checks for an Authorization
header and validates it against a known token.
If the token is valid, the request is authenticated and can proceed, otherwise it is blocked.
With authentication enabled, every request must pass this check before reaching a registered route handler.
If authentication fails, the request will not be processed further.
Accessing Claims and Authentication Status in a Handler
swytchApp.AddAction("GET", "/profile", async (context) => {
var username = ctx.User?.FindFirst(ClaimTypes.Name)?.Value ?? "Unknown";
await context.ToOK(new { message = $"Welcome, {username}!" });
}
});
Serving Static Content
Swytch provides an extension method on RequestContext
to serve static files from the Statics
directory.
This directory must be located in the root of the application's execution path, meaning it should be
placed in the same directory where your application is running.
The method takes in two parameters:
- File name – The name of the file, including its extension.
- HTTP status code – The response status code, using
System.Net.HttpStatusCode
.
If the specified file is not found in the Statics
directory, a FileNotFoundException
is thrown, caught, and logged.
Swytch then responds with a 404 Not Found
status code and the message "NOT FOUND (404)"
.
Example
await context.ServeFile("logo.png", HttpStatusCode.OK);
Swytch Static File Server
When enabled through the SwytchConfig
type when creating an instance of SwytchApp
, a built-in static
file handler is registered automatically for you. This handler intercepts and serves static files from
the Statics
directory without requiring you to write a custom handler that does this.
How It Works?
Once the static file server is enabled, any request to the path: /swytchserver/static/{filename}
will be intercepted, and the requested file will be retrieved from the Statics
directory and served automatically.
The response will include a Cache-Control
header, with the cache duration set to either the default
value or the one specified in SwytchConfig
.
This is very useful because it makes it easy to serve static files such as images, stylesheets, and JavaScript files without manually handling the request.Let's look at an example.
Example
Enabling the Static File Server
To enable the static file server, configure it when creating an instance of SwytchApp
:
var swytchApp = new SwytchApp(new SwytchConfig
{
EnableStaticFileServer = true,
StaticFileCacheMaxAge = 7200 // Cache files for 2 hours
});
Using Static File Server in HTML
<img src="/swytchserver/static/logo.png" alt="Logo">
Serving Dynamic Templates
Swytch provides built-in support for dynamic content rendering using RazorLight.
Templates are stored in the Templates
directory, where Swytch automatically locates and renders them.
Template Precompilation
Template precompilation can be configured using the SwytchConfig
class.When
- Enabled: Increases startup time relatively but ensures that all template page loads are instantaneous.
- Disabled (Default): Results in a relatively faster startup time, but the first request that renders a template page is slower. However, all other subsequent loads will be instantaneous.
Methods for Rendering Templates
Swytch offers two methods for working with templates on the SwytchApp
class:
GenerateTemplate(string key, T model)
This method generates a template and returns the result as a string.
- Parameters:
key
(string) – The filename of the template (without extension) located in theTemplates
directory.model
(T) – A model to be passed into the template for dynamic content rendering.Pass null if no dynamic data need to be inserted.
Template File Naming and Directory
All template files must be placed inside a directory named Templates
located in the root of your application.
Each template should have the .cshtml
extension.
For example, a template named welcome
must be saved as /Templates/welcome.cshtml
Template Content with Razor Syntax
Inside the welcome.cshtml
file, you can write standard Razor syntax to bind and display data passed to it.
<h1>Welcome, @Model.Name!</h1>
<p>We’re glad to have you here.</p>
You can check out the official Razor syntax documentation to learn how to write and structure your template files using Razor
You can render the above template from a route handler like indicated in the examples:
- Example Usage:
var content = swytchApp.GenerateTemplate("welcome", new { Name = "John Doe" });
Console.WriteLine(content);
This will generate a string content with the welcome.cshtml template with the Name property dynamically inserted.
RenderTemplate(RequestContext context, string key, T? model)
This method works like the GenerateTemplate
method but instead of returning the generated contect , it
directly sends it as an HTTP response to the client.
- Parameters:
context
(RequestContext) – The current request context.key
(string) – The filename of the template (without extension) located in theTemplates
directory.model
(T) – A model to be passed into the template for rendering.
Example Usage:
swytchApp.AddAction("GET", "/welcome", async ctx =>
{
await swytchApp.RenderTemplate(ctx, "welcome", new { Name = "John Doe" });
});
When a client requests /welcome
, Swytch will fetch the contexts of welcome.cshml
, bind it with the provided model and return
the generated content as an HTTP response.
Database
Working with Databases
Swytch includes built-in support for relational databases through the powerful and lightweight Dapper micro-ORM. Dapper was chosen for its simplicity, lightweight footprint, and easy-to-use APIs, making it a great fit for Swytch’s minimalistic approach. Dapper allows you to execute SQL queries directly while taking care of parameter mapping, making it ideal for building data-driven applications without the overhead of a full ORM.
Registering a Database Source
To begin working with a database in Swytch, you must first register it using the AddDatastore
method available on your
ISwytchApp
(SwychApp) instance. This method stores the connection string along with the type of database provider, so Swytch can
later retrieve and create a proper connection when needed.
The AddDatastore
method accepts two parameters:
connectionString
(string): The connection string used to connect to the database.provider
(DatabaseProviders
enum): The type of database provider being registered.
Swytch currently supports the following database providers through the Swytch.Structures.DatabaseProviders
enum:
DatabaseProviders.SqlServer
– Microsoft SQL ServerDatabaseProviders.MySql
– MySQLDatabaseProviders.PostgreSql
– PostgreSQLDatabaseProviders.SQLite
– SQLiteDatabaseProviders.Oracle
– Oracle
When you call AddDatastore
, Swytch will associate the provided connection string with the selected database provider
internally. This registration must occur before attempting to use that provider or perform any database operations
in your app logic.
Retrieving a Connection
Once a datastore has been registered, you can obtain a connection to it using the GetConnection
method. This method is also
defined on the ISwytchApp
interface and is your main entry point for executing SQL commands via Dapper.
The GetConnection
method takes one parameter:
provider
(DatabaseProviders
): The type of database you want to connect to.
It returns a raw connection object (e.g., SqlConnection
, NpgsqlConnection
, etc.) depending on the database provider you
registered earlier.
Some important notes:
-
If you call
GetConnection
with a provider that has not been registered usingAddDatastore
, aKeyNotFoundException
will be thrown. -
The returned connection implements
IDisposable
, meaning you should always dispose of it after use to avoid connection leaks.
You can dispose of the connection using:
- A
using
block (using var conn = ...
) - Or calling
conn.Dispose()
manually
Example
Here's an example showing how to register a PostgreSQL database and use it to retrieve a list of users.
// Register the PostgreSQL database
swytchApp.AddDatastore(
"Host=localhost;Port=5432;Database=mydb;Username=myuser;Password=mypassword;",
DatabaseProviders.PostgreSql
);
// Define a route that retrieves data from the database
swytchApp.AddAction("GET", "/users", async context =>
{
using var conn = swytchApp.GetConnection(DatabaseProviders.PostgreSql);
var users = await conn.QueryAsync<User>("SELECT * FROM users");
await context.ToOk(users);
});