Creating API Factories

This how-to guide introduces the concept of an API Factory. API Factories can be used to create an API on the fly, when the Endpoint is initialized.

Introduction

By following this guide you will learn:

  • What is an API Factory
  • When to use API Factories
  • How to create an API Factory

The guide will first introduce the idea behind API Factory and then will proceed to show how one can be created.

What is an API Factory

The two main concepts of the API Framework are APIs and Endpoints. These are introduced in the how-to guide "Creating APIs and Endpoints".

Endpoint requires two things: The route (for example /mytest) and the API (for example Weikio.ApiFramework.Plugins.SqlServer). In addition to these two things, endpoint specific configuration is used (for example the connection string).

What API Factory brings to the table is that instead of defining the API of the Endpoint, you define an API Factory. When the Endpoint is initialized, the factory is run and it creates the API on the fly. As API is just a .NET type, the factory will return a type (or types) when it is initialized and these types are used as the APIs.

When to use API Factories

The usual scenario when an API Factory is needed is when the endpoint's configuration affects the functionality of an Endpoint. For example the Sql Server plugin uses an API Factory. The endpoint provides different GET-methods based on the tables of the database. This is achieved using an API Factory:

  1. API Factory uses the endpoint's configuration to connect to the database
  2. It then scans the tables which we want to access through our Endpoint (again based on the configuration of the endpoint)
  3. Then code is generated. API Factory creates types on the fly based on the tables.

The generated types are the APIs and are returned by the API Factory.

API Factories can also be used to run initialization code. The browser plugin uses API Factory to download the latest version of Chrome which the API can then command headlessly.

API Factory in this guide

This how-to guide shows how to build a Calculator endpoint which provides different functionality based on the configuration.

Creating the project

Api Framework can be installed into an existing ASP.NET Core app. It works together with Controllers and Actions. For a new backend, Web Api template is a good starting point. ConfigureServices is used to add Api Framework into app.

This guide uses the ASP.NET Core Web Application and its API template as the starting point:

Please note: API Framework currently target the LTS version of .NET Core, meaning .NET Core 3.1. Please select that version as the SDK:

This guide uses API Framework's Starter Kit. For an explanation of the differences between Weikio.ApiFramework.Core, Weikio.ApiFramework.AspNetCore and Weikio.ApiFramework.AspNetCoreStarterKit, please see the FAQ.

Starter Kit is available as a Nuget package:

Nuget Version

Start by installing the package and then adding API Framework into your application using ConfigureServices in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddApiFrameworkStarterKit();
}

AddApiFrameworkStarterKit is available from namespace Weikio.ApiFramework.AspNetCore.StarterKit:

using Weikio.ApiFramework.AspNetCore.StarterKit;

As explained in FAQ, the Starter Kit installs NSwag. If you now start the application and navigate to /swagger, you should see the Swagger UI and three different documents: Api, Admin and All:

Now it's time to start building the API Factory.

Creating the Calculator Operations

This how-to guide builds the API Factory in three phases: First we create the functionality/operations (sum, multiply...) of the calculator as separate classes. In the next phase we will create a configuration for defining which operations the endpoint should provide. And then we create an API Factory which will decide on the fly, based on the endpoint's configuration, which of these operations the endpoint will provide.

First, we create few different calculator operations.

public class SumOp
{
    public int Sum(int x, int y)
    {
        return x + y;
    }
}

public class MinusOp
{
    public int Minus(int x, int y)
    {
        return x - y;
    }
}

public class MultiplyOp
{
    public int Multiply(int x, int y)
    {
        return x * y;
    }
}

Note: If needed, the operations could access the endpoint configuration by defining a public Get/Set property called Configuration.

Defining the Configuration

Next we need to create a configuration. This configuration is provided to the endpoint and the API Factory uses it to determine which functionality the endpoint should provide. In our case our configuration only needs few boolean flags:

public class CalcConfiguration
{
    public bool CanSum { get; set; } = true;
    public bool CanMinus { get; set; } = true;
    public bool CanMultiply { get; set; } = true;
}

Creating the API Factory

As a last step (before creating the actual endpoints) we create the API Factory. API Factory is executed when the endpoint is initialized and it provides the endpoint's functionality in the form of .NET types.

The API Factory usually takes the endpoint configuration as a parameter and then uses this information to create and to shape the provided endpoint. You can use DI to inject services (for example ILogger) into the API Factory. Here's a common structure of an API Factory:

public class OurApiFactory
{
    private readonly ILogger<OurApiFactory> _logger;

    public OurApiFactory(ILogger<OurApiFactory> logger)
    {
        _logger = logger;
    }

    public Task<List<Type>> Create(OurConfig configuration)
    {
        // 1. Fetch some external resources
        // var databaseTables = await GetTables(configuration);
        
        // 2. Create or select types
        var result = new List<Type>();

        // 3. Return
        return Task.FromResult(result);
    }
}

The example above is given for a scenario where async/await is used to download/read resources which then are used to create the API. You can also create API Factories without async/await, as we will do in this how-to guide.

Here's the code for our CalculatorApiFactory:

public class CalcApiFactory
{
    public List<Type> Create(CalcConfiguration configuration)
    {
        var result = new List<Type>();

        if (configuration.CanSum)
        {
            result.Add(typeof(SumOp));
        }

        if (configuration.CanMinus)
        {
            result.Add(typeof(MinusOp));
        }
        
        if (configuration.CanMultiply)
        {
            result.Add(typeof(MultiplyOp));
        }

        return result;
    }
}

That should be it for now. It's time to create some endpoints.

Creating Endpoints

First, we will test that things work as expected by creating a single new endpoint. When creating an endpoint, the API Factory is used just like one would use API:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddApiFrameworkStarterKit()
        .AddApi<CalcApiFactory>("/calc", new CalcConfiguration());
}

As the CalcConfiguration defaults to including the operations, we should see the following result:

The operations should work as expected:

As things are working as expected, we can start flexing the strengths of API Factories. We will change the initialization so that we create two endpoints:

  1. First endpoint only provides the sum operation
  2. The second endpoint provides the sum and multiple operations
services.AddApiFrameworkStarterKit()
    .AddApi<CalcApiFactory>()
    .AddEndpoint("/sumcalc", "ApiFactory.CalcApiFactory", new CalcConfiguration()
    {
        CanSum = true, 
        CanMinus = false, 
        CanMultiply = false
    })
    .AddEndpoint("/sumandmultiplecalc", "ApiFactory.CalcApiFactory", new CalcConfiguration() 
    { 
        CanSum = true, 
        CanMinus = false, 
        CanMultiply = true 
    });

We should see two endpoints, both offering different functionality:

Source Code

Source code for this sample is available from GitHub: https://github.com/weikio/ApiFramework.Samples/tree/main/howtos/4_ApiFactory

Next Steps

This guide introduced the following concepts:

  • What is an API Factory
  • When to use API Factories
  • How to create an API Factory

API Factories can be used to define runtime what functionality an endpoint should provide. This if often but now always combined which code generation. In this how-to guide we avoided the code generation by creating the calculator operation's as separate classes.