Oryx
.NET Cross platform and highly composable middleware for building web request handlers in F#
Install / Use
/learn @cognitedata/OryxREADME
Oryx
Oryx is a high-performance .NET cross-platform functional HTTP request handler library for writing HTTP clients and orchestrating web requests in F#.
An SDK for writing HTTP web clients and orchestrating web requests.
This library enables you to write Web and REST clients and SDKs for various APIs and is currently used by the .NET SDK for Cognite Data Fusion (CDF).
Oryx is heavily inspired by the AsyncRx and Giraffe frameworks and applies the same ideas to the client making the web requests. You can think of Oryx as the client equivalent of Giraffe, where the HTTP request processing pipeline starting at the client, going all the way to the server and back again.
Installation
Oryx is available as a NuGet package. To install:
Using Package Manager:
Install-Package Oryx
Using .NET CLI:
dotnet add package Oryx
Getting Started
open System.Net.Http
open System.Text.Json
open FSharp.Control.TaskBuilder
open Oryx
open Oryx.SystemTextJson.ResponseReader
[<Literal>]
let Url = "https://en.wikipedia.org/w/api.php"
let options = JsonSerializerOptions()
let query term = [
struct ("action", "opensearch")
struct ("search", term)
]
let asyncMain argv = task {
use client = new HttpClient ()
let request term =
httpRequest
|> GET
|> withHttpClient client
|> withUrl Url
|> withQuery (query term)
|> fetch
|> json options
let! result = request "F#" |> runAsync
printfn "Result: %A" result
}
[<EntryPoint>]
let main argv =
asyncMain().GetAwaiter().GetResult()
0 // return an integer exit code
Fundamentals
The main building blocks in Oryx are the HttpContext and the HttpHandler. The context contains all the state needed
for making the request, and also contains any response metadata such as headers, response code, etc received from the
remote server:
type Context = {
Request: HttpRequest
Response: HttpResponse
}
The HttpContext is constructed using a pipeline of asynchronous HTTP handlers.
type IHttpNext<'TSource> =
abstract member OnSuccessAsync: ctx: HttpContext * content: 'TSource -> Task<unit>
abstract member OnErrorAsync: ctx: HttpContext * error: exn -> Task<unit>
abstract member OnCancelAsync: ctx: HttpContext -> Task<unit>
type HttpHandler<'TSource> = IHttpNext<'TSource> -> Task<unit>
The relationship can be seen as:
do! handler success error cancel
An HTTP handler (HttpHandler) is a pipeline that uses or subscribes handler success error cancel the given
continuations success, error and cancel, and return a Task of unit.
Each HttpHandler usually transforms the HttpRequest, HttpResponse or the content before passing it down the
pipeline by invoking the next success continuation. It may also signal an error by invoking error with an
exception to fail the processing of the pipeline.
The easiest way to get your head around the Oryx HttpHandler is to think of it as a functional web request processing
pipeline. Each handler has the HttpContext and content at its disposal and can decide whether it wants to fail the
request calling error, or continue the request by calling the success handler.
HTTP Handlers
The context and content may then be transformed for individual requests using a series of HTTP handlers. HTTP handlers are like lego bricks and may be composed into more complex HTTP handlers. The HTTP handlers included with Oryx are:
cache- Caches the last result of a given handler, both the context and the content.catch- Catches errors and continues using another handler.choose- Choose the first handler that succeeds in a list of handlers.chunk- Chunks a sequence of HTTP handlers into sequential and concurrent batches.concurrent- Runs a sequence of HTTP handlers concurrently.empty- Creates a default empty request. You would usually start the chain with this handler.fail- Fails the pipeline and pushes an exception downstream.fetch- Fetches from remote using the current contextskip- Handler that skips (ignores) the content and outputs unit.get- Retrieves the content (for use inhttpbuilder)log- Log information about the given request.map- Map the content of the HTTP handler.panic- Fails the pipeline and pushes an exception downstream. This error cannot be catched or skipped.parse- Parse response stream to a user-specified type synchronously.parseAsync- Parse response stream to a user-specified type asynchronously.sequential- Runs a sequence of HTTP handlers sequentially.singleton- Handler that produces a single content value.validate- Validate content using a predicate function.withBearerToken- Adds anAuthorizationheader withBearertoken.withCancellationToken- Adds a cancellation token to use for the context. This is particularly useful when using Oryx together with C# client code that supplies a cancellation token.withContent- Add HTTP content to the fetch requestwithMetrics- Add andIMetricsinterface to produce metrics info.withError- Detect if the HTTP request failed, and then fail processing.withHeader- Adds a header to the context.withHeaders- Adds headers to the context.withHttpClient- Adds theHttpClientto use for making requests using thefetchhandler.withHttpClientFactory- Adds anHttpClientfactory function to use for producing theHttpClient.withLogger- Adds anILoggerfor logging requests and responses.withLogLevel- The log level (LogLevel) that the logging should be performed at. Oryx will disable logging forLogLevel.Noneand this is also the default log level.withLogFormat- Specify the log format of the log messages written.withLogMessage- Log information about the given request supplying a user-specified message.withMethod- with HTTP method. You can use GET, PUT, POST instead.withQuery- Add URL query parameterswithResponseType- Sets the Accept header of the request.withTokenRenewer- Enables refresh of bearer tokens without building a new context.withUrl- Use the given URL for the request.withUrlBuilder- Use the given URL builder for the request.withUrlBuilder- Adds the URL builder to use. An URL builder constructs the URL for theRequestpart of the context.
In addition there are several extension for decoding JSON and Protobuf responses:
json- Decodes the givenapplication/jsonresponse into a user-specified type.protobuf- - Decodes the givenapplication/protobufresponse into a Protobuf specific type.
See JSON and Protobuf Content Handling for more information.
HTTP verbs
The HTTP verbs are convenience functions using the withMethod under the hood:
GET- HTTP get requestPUT- HTTP put requestPOST- HTTP post requestDELETE- HTTP delete requestOPTIONS- HTTP options request
Composition
The real magic of Oryx is composition. The fact that everything is an HttpHandler makes it easy to compose HTTP
handlers together. You can think of them as Lego bricks that you can fit together. Two or more HttpHandler functions
may be composed together using the pipelining, i.e using the |> operator. This enables you to compose your
web requests and decode the response, e.g as we do when listing Assets in the Cognite Data Fusion
SDK:
let list (query: AssetQuery) (source: HttpHandler<unit>) : HttpHandler<ItemsWithCursor<AssetReadDto>> =
let url = Url +/ "list"
source
|> POST
|> withVersion V10
|> withResource url
|> withContent (() -> new JsonPushStreamContent<AssetQuery>(query, jsonOptions))
|> fetch
|> withError decodeError
|> json jsonOptions
The function list is now also an HttpHandler and may be composed with other handlers to create complex chains
for doing multiple sequential or concurrent requests to a web service. And you can do this without having to worry
about error handling.
Retrying Requests
Since Oryx is based on HttpClient from System.Net.Http, you may also use Polly
for handling resilience.
Concurrent and Sequential Handlers
A sequential operator for running a list of HTTP handlers in sequence.
val sequential:
handlers : seq<HttpHandler<'TResult>>
-> HttpHandler<list<'TResult>>
And a concurrent operator that runs a list of HTTP handlers in parallel.
val concurrent:
handlers : seq<HttpHandler<'TResult>>
-> HttpHandler<list<'TResult>>
You can also combine sequential and concurrent requests by chunking the request. The chunk handler uses chunkSize
and maxConcurrency to decide how much will be done in parallel. It takes a list of items and a handler that transform
Related Skills
node-connect
341.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
341.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.5kCommit, push, and open a PR
