Skip to content

SDK (.NET)

Apilane offers a .NET SDK that simplifies integration with the Apilane API. It provides a type-safe, builder-pattern interface for all API operations.

NuGet

Installation

Add the latest Apilane.Net NuGet package to your project:

dotnet add package Apilane.Net

Setup

Register the Apilane services in your dependency injection container:

string serverUrl = "https://my.api.server";
string applicationToken = "23d4444f-b56e-4c5a-98ed-ef251796a238";

builder.Services.UseApilane(serverUrl, applicationToken);

Then inject IApilaneService wherever you need it:

private readonly IApilaneService _apilaneService;

public MyController(IApilaneService apilaneService)
{
    _apilaneService = apilaneService;
}

Advanced Setup Options

The UseApilane method accepts optional parameters:

builder.Services.UseApilane(
    serverUrl,
    applicationToken,
    httpClient: customHttpClient,           // Custom HttpClient instance
    apilaneAuthTokenProvider: myProvider,    // Global auth token provider
    serviceKey: "app1"                      // Keyed registration for multi-app scenarios
);

Global Auth Token Provider

Instead of passing an auth token on every request, implement IApilaneAuthTokenProvider to provide it automatically (e.g., from the current HTTP context):

public class HttpContextAuthTokenProvider : IApilaneAuthTokenProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public HttpContextAuthTokenProvider(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public Task<string?> GetAuthTokenAsync()
    {
        var token = _httpContextAccessor.HttpContext?.Request.Headers["Authorization"]
            .ToString()?.Replace("Bearer ", "");
        return Task.FromResult(token);
    }
}

Register it before calling UseApilane:

builder.Services.AddSingleton<IApilaneAuthTokenProvider, HttpContextAuthTokenProvider>();
builder.Services.UseApilane(serverUrl, applicationToken);

Keyed Services (Multi-App)

If your application connects to multiple Apilane applications, use keyed registrations:

builder.Services.UseApilane(serverUrl, tokenApp1, serviceKey: "app1");
builder.Services.UseApilane(serverUrl, tokenApp2, serviceKey: "app2");

// Inject with key
public MyService([FromKeyedServices("app1")] IApilaneService app1Service) { ... }

Error Handling

All SDK methods return an Either<TSuccess, ApilaneError> result. Check for errors before accessing the value:

var response = await _apilaneService.GetDataAsync<Product>(
    DataGetListRequest.New("Products").WithAuthToken(authToken));

if (response.HasError(out var error))
{
    // Handle error — error.Message contains details
    Console.WriteLine($"Error: {error.Message}");
    return;
}

// Access the successful result
List<Product> products = response.Value.Data;

Alternatively, use OnErrorThrowException to throw on errors:

var response = await _apilaneService.GetDataAsync<Product>(
    DataGetListRequest.New("Products")
        .WithAuthToken(authToken)
        .OnErrorThrowException(true));

Account

Login

var loginResponse = await _apilaneService.AccountLoginAsync<AppUser>(
    AccountLoginRequest.New()
        .WithEmail("user@example.com")
        .WithPassword("secret"));

if (!loginResponse.HasError(out var error))
{
    string authToken = loginResponse.Value.AuthToken;
    AppUser user = loginResponse.Value.User;
}

Register

var registerResponse = await _apilaneService.AccountRegisterAsync(
    AccountRegisterRequest.New()
        .WithAuthToken(authToken),
    new { Email = "new@example.com", Username = "newuser", Password = "Pass123!" });

Get User Data

var userDataResponse = await _apilaneService.GetAccountUserDataAsync<AppUser>(
    AccountUserDataRequest.New()
        .WithAuthToken(authToken));

Update User

var updateResponse = await _apilaneService.AccountUpdateAsync<AppUser>(
    AccountUpdateRequest.New()
        .WithAuthToken(authToken),
    new { Firstname = "John", Lastname = "Doe" });

Renew Auth Token

var renewResponse = await _apilaneService.AccountRenewAuthTokenAsync(
    AccountRenewAuthTokenRequest.New()
        .WithAuthToken(authToken));

string newToken = renewResponse.Value;

Logout

var logoutResponse = await _apilaneService.AccountLogoutAsync(
    AccountLogoutRequest.New()
        .WithAuthToken(authToken));

Data Operations

Define your entity as a class extending DataItem:

public class Product : Apilane.Net.Models.Data.DataItem
{
    public string? Name { get; set; }
    public decimal? Price { get; set; }
    public bool? InStock { get; set; }
}

DataItem provides the system properties: ID, Owner, Created, and Created_Date.

Get Records (Paginated)

var response = await _apilaneService.GetDataAsync<Product>(
    DataGetListRequest.New("Products")
        .WithAuthToken(authToken)
        .WithPageIndex(1)
        .WithPageSize(50)
        .WithProperties("ID", "Name", "Price"));

List<Product> products = response.Value.Data;

Get Records with Total Count

var response = await _apilaneService.GetDataTotalAsync<Product>(
    DataGetListRequest.New("Products")
        .WithAuthToken(authToken));

List<Product> products = response.Value.Data;
long total = response.Value.Total;

Get All Records (No Paging)

var allProducts = await _apilaneService.GetAllDataAsync<Product>(
    DataGetAllRequest.New("Products")
        .WithAuthToken(authToken));

Get Record by ID

var product = await _apilaneService.GetDataByIdAsync<Product>(
    DataGetByIdRequest.New("Products", recordId: 1)
        .WithAuthToken(authToken));

Create Records

var response = await _apilaneService.PostDataAsync(
    DataPostRequest.New("Products")
        .WithAuthToken(authToken),
    new Product { Name = "Widget", Price = 9.99m, InStock = true });

long newId = response.Value.Single();

Update Records

var response = await _apilaneService.PutDataAsync(
    DataPutRequest.New("Products")
        .WithAuthToken(authToken),
    new { ID = 1, Price = 12.99 });

int affectedRecords = response.Value;

Delete Records

var response = await _apilaneService.DeleteDataAsync(
    DataDeleteRequest.New("Products")
        .WithAuthToken(authToken)
        .AddIdToDelete(1));

long[] deletedIds = response.Value;

Filtering & Sorting

See Filtering & Sorting for the full filter/sort reference.

Filter Builder

using Apilane.Net.Models.Data;
using Apilane.Net.Models.Enums;

// Simple filter
var filter = new FilterItem("Price", FilterOperator.less, 100);

// Compound filter (AND/OR)
var filter = new FilterItem(FilterLogic.AND, new List<FilterItem>
{
    new FilterItem("Price", FilterOperator.greaterorequal, 10),
    new FilterItem("InStock", FilterOperator.equal, true)
});

// Nested filter
var filter = new FilterItem(FilterLogic.AND, new List<FilterItem>
{
    new FilterItem("Category", FilterOperator.equal, "Electronics"),
    new FilterItem(FilterLogic.OR, new List<FilterItem>
    {
        new FilterItem("Price", FilterOperator.less, 50),
        new FilterItem("OnSale", FilterOperator.equal, true)
    })
});

Sort

var sort = new SortItem { Property = "Price", Direction = "ASC" };

Combined Example

var response = await _apilaneService.GetDataAsync<Product>(
    DataGetListRequest.New("Products")
        .WithAuthToken(authToken)
        .WithFilter(new FilterItem("InStock", FilterOperator.equal, true))
        .WithSort(new SortItem { Property = "Price", Direction = "ASC" })
        .WithPageSize(25)
        .WithProperties("ID", "Name", "Price"));

Transactions

See Transactions for the full reference.

Grouped Transaction

var data = new InTransactionData
{
    Post = new List<InTransactionData.InTransactionSet>
    {
        new() { Entity = "Orders", Data = new { CustomerName = "John" } }
    },
    Put = new List<InTransactionData.InTransactionSet>
    {
        new() { Entity = "Products", Data = new { ID = 5, Stock = 48 } }
    },
    Delete = new List<InTransactionData.InTransactionDelete>
    {
        new() { Entity = "TempRecords", Ids = "10,11" }
    }
};

var result = await _apilaneService.TransactionDataAsync(
    DataTransactionRequest.New().WithAuthToken(authToken), data);

Ordered Transaction with Cross-Referencing

using Apilane.Net.Models.Data;

var transaction = new TransactionBuilder()
    .Post("Orders", new { CustomerName = "John" }, out var orderRef)
    .Post("OrderItems", new { OrderId = orderRef.Id(), Product = "Widget" })
    .Put("Orders", new { ID = orderRef.Id(), Status = "Active" })
    .Build();

var result = await _apilaneService.TransactionOperationsAsync(
    DataTransactionOperationsRequest.New().WithAuthToken(authToken),
    transaction);

Files

// Upload
byte[] fileBytes = File.ReadAllBytes("photo.jpg");
var uploadResult = await _apilaneService.PostFileAsync(
    FilePostRequest.New()
        .WithAuthToken(authToken)
        .WithFileName("photo.jpg")
        .WithPublicFlag(false)
        .WithFileUID("user-avatar-123"),
    fileBytes);

// List files
var files = await _apilaneService.GetFilesAsync<MyFile>(
    FileGetListRequest.New().WithAuthToken(authToken));

// Get file by ID
var file = await _apilaneService.GetFileByIdAsync<MyFile>(
    FileGetByIdRequest.New(fileId: 1).WithAuthToken(authToken));

// Delete files
var deleted = await _apilaneService.DeleteFileAsync(
    FileDeleteRequest.New()
        .WithAuthToken(authToken)
        .AddIdToDelete(1));

Stats & Aggregation

Aggregate

using static Apilane.Net.Request.StatsAggregateRequest;

var result = await _apilaneService.GetStatsAggregateAsync<MyAggResult>(
    StatsAggregateRequest.New("Orders")
        .WithAuthToken(authToken)
        .WithProperty("Total", DataAggregates.Sum)
        .WithProperty("Total", DataAggregates.Avg)
        .WithProperty("ID", DataAggregates.Count)
        .WithGroupBy("Status")
        .WithSort(ascending: false));

Distinct

var result = await _apilaneService.GetStatsDistinctAsync<string[]>(
    StatsDistinctRequest.New("Products")
        .WithAuthToken(authToken)
        .WithProperty("Category"));

Schema

Retrieve the application schema at runtime:

var schema = await _apilaneService.GetApplicationSchemaAsync(
    DataGetSchemaRequest.New().WithAuthToken(authToken));

var entities = schema.Value.Entities;
foreach (var entity in entities)
{
    Console.WriteLine($"{entity.Name}: {entity.Properties.Count} properties");
}

Custom Endpoints

// Raw JSON response
var json = await _apilaneService.GetCustomEndpointAsync(
    CustomEndpointRequest.New("MyEndpoint")
        .WithAuthToken(authToken)
        .WithParameter("UserID", 42));

// Typed response
var result = await _apilaneService.GetCustomEndpointAsync<List<UserSummary>>(
    CustomEndpointRequest.New("GetUserSummary")
        .WithAuthToken(authToken)
        .WithParameter("UserID", 42));

// Multi-result set (multiple SELECT statements)
var (orders, items) = await _apilaneService.GetCustomEndpointAsync<List<Order>, List<OrderItem>>(
    CustomEndpointRequest.New("GetOrderDetails")
        .WithAuthToken(authToken)
        .WithParameter("OrderID", 100));

URL Helpers

The SDK provides URL builders for Apilane pages:

// Forgot password page URL
string forgotPasswordUrl = _apilaneService.UrlFor_Account_Manage_ForgotPassword();

// API endpoint: send forgot password email
string sendResetEmailUrl = _apilaneService.UrlFor_Email_ForgotPassword("user@example.com");

// API endpoint: send confirmation email
string confirmEmailUrl = _apilaneService.UrlFor_Email_RequestConfirmation("user@example.com");

Health Check

Verify the API is running:

var health = await _apilaneService.HealthCheckAsync();