Oxpecker Documentation

repository·develop·Indexed 19 days ago

https://github.com/lanayx/oxpecker

A high-performance, fullstack F# web framework built on ASP.NET Core. It provides a functional experience for building modern web applications with native ASP.NET Core Endpoint routing integration, a fast ViewEngine with an HTML DSL, and support for HTMX, Solid.js, and Alpine.js. The ecosystem includes specialized NuGet packages such as Oxpecker.ViewEngine, Oxpecker.Htmx, Oxpecker.Solid, and Oxpecker.Alpine.

Tokens
25.9K
Snippets
85
Records
107
Agent score
64%

What's inside Oxpecker

  1. Overview of Oxpecker

    develop

    Oxpecker is a high-performance F# library designed for fullstack web development using ASP.NET Core, HTMX, and Solid.js. It serves as a functional wrapper for ASP.NET Core Endpoint routing (similar to F#-friendly 'Minimal APIs') and is a refined, optimized evolution of the Giraffe framework.

    Key features include:

    • Native ASP.NET Core Endpoint routing integration.
    • A fast ViewEngine with an HTML DSL.
    • Integration with HTMX, Solid.js, and OpenAPI.
    • Strongly typed route parameters and simplified binding (JSON, Form, and URL parameters).
    • Support for streaming, response caching, authorization, and eTags.
  2. Features of the MCP example

    develop

    The MCP example implementation includes the following architectural components:

    • SSE-based MCP server: Hosted over ASP.NET Core.
    • Semantic Kernel: Used on the client side for orchestration.
    • Local LLM: Powered by Ollama.
    • Logging: Integrated throughout the example.
  3. Tech stack used in the TodoList example

    develop

    The TodoList example utilizes the following technologies:

    • F#: Compiled via Fable and the ViewEngine plugin.
    • Solid.js: UI library.
    • Solid router: For client-side routing.
    • Oxpecker ViewEngine: A modified version for frontend use.
    • Tailwind CSS: For styling.
    • Vite: As the build tool/dev server.
    • HMR: Hot Module Replacement is supported.
  4. Overview of Oxpecker Core Concepts

    develop

    Oxpecker is an F# framework built on top of ASP.NET Core Endpoint Routing. It is designed with an easy-to-comprehend API, drawing inspiration from the Giraffe framework. It functions as a competitor to ASP.NET Core Minimal APIs by providing a functional approach to web development.

    Key abstractions include:

    • EndpointHandler: The primary unit for handling specific endpoint logic.
    • EndpointMiddleware: Logic that sits within the Oxpecker pipeline to process requests or responses.
    • Oxpecker Pipeline: A specialized pipeline that operates alongside or within the standard ASP.NET Core pipeline.
  5. Dependency requirements for CRUD example

    develop

    When working with the CRUD example, note the following regarding project references:

    • Main branch: Referenced projects are not required as they are provided via NuGet.
    • Develop branch: You may need to manually include/reference the local projects to build the example.
  6. Understand the HtmlElement hierarchy

    develop

    The core of the engine is the HtmlElement interface. It is extended by two specialized interfaces:

    • HtmlTag: Adds the ability to manage attributes via AddAttribute.
    • HtmlContainer: Adds the ability to manage children via AddChild.

    There are five internal node types:

    1. RegularNode: Standard elements with children and attributes.
    2. VoidNode: Elements that only support attributes (e.g., <br>).
    3. FragmentNode: Elements that only support children.
    4. RegularTextNode: Escaped text.
    5. RawTextNode: Unescaped text.

    You can create custom tags by inheriting from RegularNode or VoidNode.

    type myTag() =
        inherit RegularNode("myTag") // will render <myTag></myTag>
  7. How to define components using the SolidComponent attribute

    develop

    To transform an F# function into Solid-compatible JSX during build time, you must decorate it with the [<SolidComponent>] attribute. This attribute has no effect at runtime but is required for the compiler plugin to work.

    In Oxpecker.Solid, you do not need to use a special 'props' object; you can use regular F# function arguments (records, functions, etc.) directly.

    [<SolidComponent>]
    let MyComponent (name: string) (children: #HtmlElement) = 
        div() {
            h1() { name }
            children
        }
  8. Avoid resource disposal issues with Deferred Task execution

    develop

    Because an EndpointHandler returns a Task, simply returning a task from a function (like text "Hello" ctx) does not mean the function waits for that task to finish before executing the rest of the code in the scope.

    The Problem: If you use the use keyword for an IDisposable resource, the resource might be disposed before the response is actually sent to the client because the handler returned the task immediately.

    The Solution: Always wrap your logic in a task {} computation expression and use return! to ensure the handler awaits the completion of the response task before proceeding to the end of the scope.

    // BAD: Resource might be disposed before response is sent
    let doSomething : EndpointHandler = 
        fun ctx -> 
            use __ = somethingToBeDisposedAtTheEndOfTheRequest
            text "Hello" ctx
    
    // GOOD: Ensures task completes before disposal
    let doSomething : EndpointHandler = 
        fun (ctx: HttpContext) -> 
            task {
                use __ = somethingToBeDisposedAtTheEndOfTheRequest
                return! text "Hello" ctx
            }
  9. Continue vs. Return early in the pipeline

    develop

    When implementing EndpointMiddleware or EndpointHandler, you have two choices for flow control:

    1. Continue: The component performs an action and then invokes the next handler (for middleware) or allows the response to proceed (for handlers).

      • For middleware: Call next ctx.
      • For handlers: Return Task.CompletedTask or start the response.
    2. Return early (Short-circuit): The component stops the pipeline and returns a response immediately without calling next.

      • For middleware: Return a completed task (e.g., setStatusCode 401 ctx) instead of calling next.
      • For handlers: Explicitly start the response (e.g., text "Unauthorized" ctx).
    // Middleware: Continue
    let setHttpHeader key value : EndpointMiddleware = 
        fun (next: HttpFunc) (ctx: HttpContext) -> 
            ctx.SetHttpHeader key value
            next ctx
    
    // Middleware: Return early (Short-circuit)
    let checkUserIsLoggedIn : EndpointMiddleware = 
        fun (next: EndpointHandler) (ctx: HttpContext) -> 
            task {
                if isNotNull ctx.User && ctx.User.Identity.IsAuthenticated then
                    return! next ctx
                else
                    return ctx.SetStatusCode 401
            }
  10. Understand EndpointHandler and EndpointMiddleware

    develop

    Oxpecker uses two primary building blocks to define web logic:

    1. EndpointHandler: A terminal function that takes an HttpContext and returns a Task. It is responsible for processing the request and typically writing a response.

      • Signature: type EndpointHandler = HttpContext -> Task
      • Use this when you want to execute logic that serves as the end of a request's processing.
    2. EndpointMiddleware: A function that wraps an EndpointHandler. It allows you to process a request before passing it to the next handler or short-circuiting the pipeline.

      • Signature: type EndpointMiddleware = EndpointHandler -> HttpContext -> Task
      • Use this if you need to conditionally proceed (e.g., authentication), execute logic after the next handler completes, or intercept the request/response.

    Relationship to ASP.NET Core

    The Oxpecker pipeline is a functional equivalent to the ASP.NET Core middleware pipeline. It is plugged into the wider ASP.NET Core pipeline via OxpeckerMiddleware, meaning you can combine Oxpecker's functional DSL with standard ASP.NET Core middleware (like static files).

    type EndpointHandler = HttpContext -> Task
    
    type EndpointMiddleware = EndpointHandler -> HttpContext -> Task