Getting Started with Aperture
Aperture is available as a managed service, Aperture Cloud, or can be self-hosted within your infrastructure.
1. Sign up to Aperture Cloud
For signing up, head over to Aperture Cloud. For detailed instructions, refer to our step-by-step guide.
2. Pick your Integration
These are two main modes on how to get started with Aperture.
- Aperture Serverless
- Aperture for Infrastructure
The Serverless mode is the quickest way to start using Aperture.
3. Get your API key
In Aperture Cloud, authentication for SDK integrations is handled using API keys. These keys can be found in the Aperture Cloud UI. For detailed instructions on locating API Keys, refer to the API Keys section.
4. Install SDK and Define Control Points
Aperture provides SDKs for various languages. You can find the SDKs and installation instructions here.
- TypeScript
- Python
- Go
npm install @fluxninja/aperture-js
pip install aperture-py
go get github.com/fluxninja/aperture-go/v2
Connect to Aperture Cloud by creating an Aperture Client with your organization's address and API Key.
- TypeScript
- Python
- Go
import { ApertureClient } from "@fluxninja/aperture-js";
// Create aperture client
export const apertureClient = new ApertureClient({
address: "ORGANIZATION.app.fluxninja.com:443",
apiKey: "API_KEY",
});
from aperture_sdk.client import ApertureClient, FlowParams
agent_address = os.getenv("APERTURE_AGENT_ADDRESS", default_agent_address)
api_key = os.getenv("APERTURE_API_KEY", "")
insecure = os.getenv("APERTURE_AGENT_INSECURE", "true").lower() == "true"
aperture_client = ApertureClient.new_client(
address=agent_address, insecure=insecure, api_key=api_key
)
// grpcOptions creates a new gRPC client that will be passed in order to initialize the Aperture client.
func grpcOptions(insecureMode, skipVerify bool) []grpc.DialOption {
var grpcDialOptions []grpc.DialOption
grpcDialOptions = append(grpcDialOptions, grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.DefaultConfig,
MinConnectTimeout: time.Second * 10,
}))
grpcDialOptions = append(grpcDialOptions, grpc.WithUserAgent("aperture-go"))
if insecureMode {
grpcDialOptions = append(grpcDialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
} else if skipVerify {
grpcDialOptions = append(grpcDialOptions, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true, //nolint:gosec // For testing purposes only
})))
} else {
certPool, err := x509.SystemCertPool()
if err != nil {
return nil
}
grpcDialOptions = append(grpcDialOptions, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(certPool, "")))
}
return grpcDialOptions
}
opts := aperture.Options{
Address: apertureAgentAddress,
DialOptions: grpcOptions(apertureAgentInsecureBool, apertureAgentSkipVerifyBool),
}
// initialize Aperture Client with the provided options.
apertureClient, err := aperture.NewClient(ctx, opts)
if err != nil {
log.Fatalf("failed to create client: %v", err)
}
Define a Control Point
Once the SDK is connected to Aperture Cloud, you can create a feature control point anywhere within your code. For example, before executing the business logic of a specific API, you can create a feature control point that can control the execution flow of the API and can reject the request based on the policy defined in Aperture.
Let's create a feature control point in the following code snippet.
- TypeScript
- Python
- Go
The code snippet below shows how to wrap your
Control Point within the StartFlow
call while
also passing labels and cacheKey
to Aperture
Agents.
- The function
Flow.ShouldRun()
checks if the flow allows the request. - The
Flow.End()
function is responsible for sending telemetry. - The
flow.CachedValue().GetLookupStatus()
function returns the status of the cache lookup. The status can beHit
orMiss
. - If the status is
Hit
, theflow.CachedValue().GetValue()
function returns the cached response. - The
flow.SetCachedValue()
function is responsible for setting the response in Aperture cache with the specified TTL (time to live).
import { FlowStatus, LookupStatus } from "@fluxninja/aperture-js";
import { Request, Response } from "express";
async function handleRequest(req: Request, res: Response) {
const flow = await apertureClient.startFlow("your_workload", {
labels: {
user: "user1",
tier: "premium",
},
grpcCallOptions: {
deadline: Date.now() + 300, // 300ms deadline
},
resultCacheKey: "cache", // optional, in case caching is needed
});
if (flow.shouldRun()) {
// Check if the response is cached in Aperture from a previous request
if (flow.resultCache().getLookupStatus() === LookupStatus.Hit) {
res.send({ message: flow.resultCache().getValue()?.toString() });
} else {
// Do Actual Work
// After completing the work, you can store the response in the cache and return it, for example
const resString = "foo";
// create a new buffer
const buffer = Buffer.from(resString);
// set cache value
const setResp = await flow.setResultCache({
value: buffer,
ttl: {
seconds: 30,
nanos: 0,
},
});
if (setResp.getError()) {
console.log(`Error setting cache value: ${setResp.getError()}`);
}
res.send({ message: resString });
}
} else {
// Aperture has rejected the request due to a rate limiting policy
res.status(429).send({ message: "Too many requests" });
// Handle flow rejection
flow.setStatus(FlowStatus.Error);
}
flow.end();
}
The code snippet below shows how to wrap your
Control Point within the start_flow
call while
passing labels and cache keys to Aperture.
Caching mechanism allows you to store the response of a request in the Aperture. This feature is useful when you want to cache the response from external or internal services and use it for subsequent requests. There are two types of cache key that can be passed to Aperture:
result_cache_key
- This key is useful to store the response of the request in Aperture. For example, the result of heavy tasks such as database query, a third-party API call which later can be used for subsequent requests if requested within the TTL. This removes the need to perform the same task again.global_cache_keys
- Using these keys, a global cache value can be set up, which can be accessed throughput anywhere in the application.
Each of these keys is associated with a TTL, a cache expiration time.
# business logic produces labels
labels = {
"key": "some-value",
}
flow_params = FlowParams(
check_timeout=timedelta(seconds=200),
explicit_labels=labels,
global_cache_keys=["cache-key"],
result_cache_key="result-key",
)
# start_flow performs a flowcontrol.v1.Check call to Aperture Agent.
# It returns a Flow or raises an error if any.
flow = aperture_client.start_flow(
control_point="super3",
params=flow_params,
)
result_string = None
cache_value = None
# Check if flow check was successful.
if not flow.success:
logger.info("Flow check failed - will fail-open")
# See whether flow was accepted by Aperture Agent.
if flow.should_run():
logging.info("Flow accepted")
# 1. Check if the response is cached in Aperture from a previous request
if flow.result_cache().get_lookup_status() == LookupStatus.MISS:
logging.info("Result Cache Miss, setting result cache")
# Do Actual Work
# After completing the work, you can return store the response in cache and return it, for example:
result_string = "foo"
# save to result cache for 10 seconds
flow.set_result_cache(result_string, timedelta(seconds=10))
else:
result_string = flow.result_cache().get_value()
logging.info("Result Cache Hit: {}".format(result_string))
# 2. Check if the cache for a 'cache-key' is present
if flow.global_cache("cache-key").get_lookup_status() == LookupStatus.MISS:
logging.info(
"Cache Miss, setting global cache for key: '{}'".format("cache-key")
)
# save to global cache for key for 10 seconds
flow.set_global_cache("cache-key", "awesome-value", timedelta(seconds=10))
cache_value = "awesome-value"
else:
logging.info("Cache Hit")
# get value from global cache for 'cache-key'
logging.info(
"Cache Value: {}".format(flow.global_cache("cache-key").get_value())
)
cache_value = flow.global_cache("cache-key").get_value()
else:
# handle flow rejection by Aperture Agent
flow.set_status(FlowStatus.Error)
if flow:
flow.end()
// business logic produces labels
labels := map[string]string{
"userId": "some_user_id",
"userTier": "premium",
"priority": "100",
}
flowParams := aperture.FlowParams{
Labels: labels,
RampMode: false,
}
flow := a.apertureClient.StartFlow(r.Context(), "featureName", flowParams)
// StartFlow performs a flowcontrolv1.Check call to Aperture Agent. It returns a Flow object.
// See whether flow was accepted by Aperture Agent.
if flow.ShouldRun() {
// do actual work
log.Println("Flow Accepted Processing work")
w.WriteHeader(http.StatusAccepted)
} else {
// handle flow rejection by Aperture Agent
log.Println("Flow Rejected")
flow.SetStatus(aperture.Error)
w.WriteHeader(http.StatusForbidden)
}
endResponse := flow.End()
if endResponse.Error != nil {
log.Printf("Failed to end flow: %+v", endResponse.Error)
}
log.Printf("Flow ended with response: %+v", endResponse.FlowEndResponse)
This is how you can create a feature control point in your code. The complete example is available here.
5. Create Your Policy
Within the Aperture UI, navigate to the policy in tab in the sidebar menu, and
click the Create Policy
button in the upper-right corner. There you can pick
the blueprint that best aligns with your needs. After a few clicks, you'll be
directed to a screen where you can input the necessary parameters to generate
the policy.
For more details on how to create a policy, follow our step-by-step guide.
There are two ways to integrate Aperture into your infrastructure. The first option includes a cloud-hosted controller and self-managed agents to ensure minimal performance impact. The second option involves hosting Aperture entirely within your infrastructure, making it perfect for air-gapped environments.
3. Set Up the Environment
The Aperture Agent can be installed in various modes. For installation steps, see Agent docs under Aperture For Infrastructure.
For more details on fully self-hosted installation, refer to the Self-hosted section.
4. Integrate with Aperture
Here are various Integrations methods with Aperture
5. Map to Aperture SaaS Controller
Aperture Cloud authenticates requests from integrations using API keys, which are created for your project and can be found within the Aperture Cloud UI. Copy the API key and save it in a secure location. This key will be used during the configuration of Self-hosted Agents. For detailed instructions on locating API Keys, refer to the API Keys section.
Using the API key, you can map your integration to the Aperture Cloud. See FluxNinja Cloud Extension for more details.
6. Create Your Policy
For more details on how to create a policy, follow our step-by-step guide.