{"id":15625631,"url":"https://github.com/daniel15/fdk-dotnet","last_synced_at":"2025-04-28T15:52:52.947Z","repository":{"id":47063529,"uuid":"178724907","full_name":"Daniel15/fdk-dotnet","owner":"Daniel15","description":"FnProject FDK for C# / .NET Core","archived":false,"fork":false,"pushed_at":"2021-09-15T06:21:24.000Z","size":74,"stargazers_count":13,"open_issues_count":5,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-28T15:52:46.328Z","etag":null,"topics":["docker","faas","fn","fnproject","serverless"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Daniel15.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-31T18:15:30.000Z","updated_at":"2023-09-08T17:52:12.000Z","dependencies_parsed_at":"2022-09-23T11:00:46.962Z","dependency_job_id":null,"html_url":"https://github.com/Daniel15/fdk-dotnet","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel15%2Ffdk-dotnet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel15%2Ffdk-dotnet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel15%2Ffdk-dotnet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daniel15%2Ffdk-dotnet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Daniel15","download_url":"https://codeload.github.com/Daniel15/fdk-dotnet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251342718,"owners_count":21574243,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","faas","fn","fnproject","serverless"],"created_at":"2024-10-03T10:01:50.530Z","updated_at":"2025-04-28T15:52:52.922Z","avatar_url":"https://github.com/Daniel15.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fn Function Developer Kit for C\\#\n\n[![Build Status](https://dev.azure.com/daniel15/fdk-dotnet/_apis/build/status/Daniel15.fdk-dotnet?branchName=master)](https://dev.azure.com/daniel15/fdk-dotnet/_build/latest?definitionId=5\u0026branchName=master)\u0026nbsp;\n[![NuGet version](http://img.shields.io/nuget/v/FnProject.Fdk.svg)](https://www.nuget.org/packages/FnProject.Fdk/)\n\nThis Function Developer Kit makes it easy to deploy .NET Core functions to Fn.\n\n# Quick Start\n\nBefore starting, ensure you have installed Fn, and started a local Fn server by running `fn start`.\n\n```sh\n# Create new function\nmkdir MyFunction\ncd MyFunction\nfn init --init-image daniel15/fn-dotnet-init\n\n# Deploy it to local Fn server (make sure you've ran \"fn start\" first)\nfn deploy --app Test --create-app --local\n\n# Test it out!\necho \"Daniel\" | fn invoke Test MyFunction\n# Should output \"Hello Daniel\"\n```\n\n# API\n\nThere are three possible ways to configure Fn functions. These are similar to how [ASP.NET Core Middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write) can be written:\n\n## Anonymous Function\n\nOne approach is to call `FdkHandler.Handle` with a [lambda expression](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions). The function takes two arguments: A context containing information about the request (for example, HTTP headers, app name, function name, etc), and the input to the function. The function can be either a regular function, or an asynchronous function using the `async` keyword.\n\n```csharp\nFdkHandler.Handle((ctx, input) =\u003e\n{\n    var name = input.AsString();\n    return $\"Hello {name}\";\n});\n```\n\n## Class\n\nAlternatively, the function can be configured as a class. This is what `fn init` will give you out of the box, and is generally the recommended approach. Instead of taking the `IInput`, you can just have a `string` argument directly. Your class must have either an `InvokeAsync` or `Invoke` method.\n\n```csharp\nclass MyFunction\n{\n    public async Task\u003cstring\u003e InvokeAsync(string input, CancellationToken timedOut)\n    {\n        // ...you could do some async stuff here...\n        return Task.FromResult($\"Hello {input}\");\n    }\n}\n```\n\nThe `CancellationToken` is important to ensure the function is properly aborted if it exceeds [the configured function call timeout](https://github.com/fnproject/docs/blob/master/fn/develop/function-timeouts.md#function-call-timeout). You should pass it to all async methods called by your function.\n\nFor functions that do not do any async operations, you can implement `Invoke` instead:\n\n```csharp\nclass MyFunction\n{\n    public string Invoke(string input)\n    {\n        return $\"Hello {input}\";\n    }\n}\n```\n\n## `IFunction` Interface\n\nYou can also implement the `IFunction` interface directly. However, this is not recommended as it has extra boilerplate:\n\n```csharp\npublic class FooFunction : IFunction\n{\n    public Task\u003cobject\u003e InvokeAsync(IContext ctx, IInput input, IServiceProvider services)\n    {\n        var name = input.AsString();\n        return $\"Hello {name}\";\n    }\n}\n```\n\n## Input\n\n### JSON\n\nTo get the input as a strongly-typed JSON object, just add the object as a parameter to your function:\n\n```csharp\npublic class MyInput\n{\n    public string Name { get; set; }\n}\n\nclass MyFunction\n{\n    public string Invoke(MyInput input)\n    {\n        return $\"Hello {input.Name}!\";\n    }\n}\n```\n\nAlternatively, you can use `dynamic` to get the data as a [dynamic object](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/walkthrough-creating-and-using-dynamic-objects):\n\n```csharp\nclass MyFunction\n{\n    public string Invoke(dynamic input)\n    {\n        return $\"Hello {input.Name}!\";\n    }\n}\n```\n\nIf using the lambda expression syntax, use the `AsJson` method on the `input` argument:\n\n```csharp\nFdkHandler.Handle((ctx, input) =\u003e\n{\n    var inputObj = input.AsJson\u003cMyInput\u003e();\n    return $\"Hello {input.Name}!\";\n});\n```\n\n### Headers\n\nRequest headers can be obtained from the `Headers` property on the `IContext`:\n\n```csharp\nclass MyFunction\n{\n    public string Invoke(IContext ctx)\n    {\n        var foo = ctx.Headers[\"X-Foo\"];\n        // ...\n    }\n}\n```\n\n`IContext` also contains other information about the request, such as the call ID (which is unique per call), the HTTP method, the system configuration, etc.\n\n## Output\n\n### Raw\n\nTo return \"raw\" data from your function, either return a string (as shown above), or a stream:\n\n```csharp\nclass MyFunction\n{\n    public Stream Invoke()\n    {\n        return File.Open(\"/tmp/test.txt\", FileMode.Open, FileAccess.Read);\n    }\n}\n```\n\nA stream is useful when returning a large response, as the entire response does not need to be cached in RAM beforehand. The stream will be automatically closed and disposed once the request completes.\n\n### JSON\nAny POCOs (Plain Old C# Objects) returned from the function will be automatically serialized as JSON. This can be an anonymous type:\n\n```csharp\nclass MyFunction\n{\n    public object Invoke()\n    {\n        return new { Message = \"Hello World!\" };\n    }\n}\n```\n\nExample of invoking this function:\n\n```sh\n$ fn invoke Test myfunction\n{\"Message\":\"Hello World!\"}\n```\n\n## Using HTTP headers and setting HTTP status codes\n\nTo set custom status codes or headers, you need to return a subclass of `FnResult` (`RawResult`, `JsonResult` or `StreamResult`, depending on the type you're returning, as documented above). This contains `HttpStatus` and `Headers` properties:\n\n```csharp\nclass MyFunction\n{\n    public object Invoke(string input)\n    {\n        return new RawResult($\"Hello {input}!\")\n        {\n            // Example of custom status code\n            HttpStatus = StatusCodes.Status202Accepted,\n            // Example of custom headers\n            Headers =\n            {\n                [\"X-Some-Header\"] = \"foo\"\n            }\n        };\n    }\n}\n```\n\n## Timeouts\n\nTo properly handle [function call timeouts](https://github.com/fnproject/docs/blob/master/fn/develop/function-timeouts.md#function-call-timeout), any functions that perform I/O (database operations, downloading/uploading, etc) should be `async` functions, and use the `CancellationToken` provided as an argument (or in `ctx.TimedOut` for the lambda expression syntax) for all their async function calls. For example:\n\n```csharp\nvar client = new HttpClient();\nvar response = await client.GetAsync(\"https://www.example.com/\", ctx.TimedOut);\n```\n\nThis will ensure that the operation is correctly cancelled if the function times out.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaniel15%2Ffdk-dotnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaniel15%2Ffdk-dotnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaniel15%2Ffdk-dotnet/lists"}