4

Generating TypeScript typings for your C# Azure Functions

 2 years ago
source link: https://www.codingwithmiszu.com/2021/12/05/generating-typescript-typings-for-your-c-azure-functions/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Exposing Azure Functions API models as a npm TypeScript package
1wGjz9sjhyH_9nGPO2dzn9g.jpeg

Cloud

Generating TypeScript typings for your C# Azure Functions

By Michał Żołnieruk December 5, 2021

8 Mins Read

Share

TL;DR — https://github.com/miszu/csharp-azure-functions-typescript-types

So you’ve created your C# backend using neat Azure Functions and everything seems to work perfectly. It’s time to share the results of your work with whoever is going to work on the frontend site. Regardless of whether it’s web or mobile app, chances are it’ll be written using TypeScript.

TypeScript is used to basically add strong types to JavaScript, thanks to which you gain a lot of confidence while developing your app — any trivial (or not) type mistake can be found instantly, even before running the application.

A common issue in frontend-backend communication is that the API contract changes silently — anything from method name update, parameter type change or adding an additional property to the DTO can break the frontend application with noone noticing.

Wouldn’t it be cool if backend would continuously publish the API contract in a way that the frontend app can immediately spot any breaking changes? In this article, we’ll achieve exactly that, as there’s no reason why we shouldn’t include API communication in the wonderful world of type safety ✨

Disclaimer: This article is merely an explanation how to connect few existing components and code pieces— some of them are already described n times in various places, but I haven’t seen any article about integrating all of them together— I hope it’ll be as useful to someone as it is to me 😇

General flow of generating TypeScript typings from C# Azure Functions

  1. Describe your C# Azure Functions and models using attributes (you can provide info about which model is used as function’s input/output, which parameter is required etc)
  2. Generate OpenAPI document describing your API
  3. Based on OpenAPI document, generate TypeScript typings
  4. Publish these typings as an npm package as part of Github Actions CI/CD

Prerequisites

This article assumes you have currently configured the following things (if not, I’ve provided links to get you started):

C# Azure Function project in Azure

You can either create it using Azure Portal and then export the code to your GitHub repository, or generate the code using templates (there are few ways to do it depending on your IDE — check out the tutorial for VS Codeor regular Visual Studio).

Continuous deployment using GitHub Actions

To achieve this, you’ll need to have your code hosted in the GitHub repository. After you’ve pushed it to the master branch, you need to add a GitHub action to deploy it continuously. There’s a handy GitHub action sample maintained by the Azure team. If something doesn’t work for you, check out my repo.

Generating OpenAPI document

Now that our C# Function App is up and running 🤞🏻, we should take a step towards documenting the API. Based on my experience, using Swagger UI is a great way to do so — not only it lists all methods, parameters of our API, it also allows to test everything right in the browser.

Swagger generates its Swagger UI interface (AppCenter’s example) based on a JSON (AppCenter’s example), which describes an API in accordance with OpenAPI specification. We’ll generate TypeScript typings based on the same JSON document, so let’s find out how to get it.

To document our API in OpenAPI format, we need to use an additional NuGet package (sadly it doesn’t have a lot of community behind it at the moment, as Swagger is not as well adopted in Azure Functions world as it is in for example Net Core Web API apps).

Check out the details regarding the NuGet which we’ll use in the article from yu_ka1984 here — it contains some additional explanations regarding OpenAPI documentation.

As some parts of this article are no longer up to date (and there are various issues with new versions of target framework/azure functions), below I’ll refer to the pieces of code in my repository — so we’re sure the whole solution works together nicely. To generate OpenAPI for your functions, you need to:

  1. Add AzureExtensions.Swashbuckle NuGet to your project
  2. Add attributes to each function to describe them — these will be used in the OpenAPI document (start with setting the ProducesResponseTypeattribute to define which class will be returned as a result)
[FunctionName("DummyFunction")]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(DummyResponseDto))]
public IActionResult Run([HttpTrigger(AuthorizationLevel.Admin, "get", Route = "dummyFunction")] HttpRequest req)
{
    return new ObjectResult(new DummyResponseDto
    {
        Greeting = "Hey!",
        Emoji = "",
        NestedThingies = Enumerable.Range(0, 5).Select(x => new DummyNestedDto
        {
            ImportantNumber = x
        }).ToArray()
    });
}

3. Add swagger functions (first generates OpenAPI JSON, second renders it using Swagger UI):

[SwaggerIgnore]
[FunctionName("Swagger")]
public Task<HttpResponseMessage> Swagger(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "swagger/json")]
    HttpRequestMessage req,
    [SwashBuckleClient] ISwashBuckleClient swashBuckleClient)
{
    return Task.FromResult(swashBuckleClient.CreateSwaggerDocumentResponse(req));
}

[SwaggerIgnore]
[FunctionName("SwaggerUi")]
public Task<HttpResponseMessage> SwaggerUi(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "swagger/ui")]
    HttpRequestMessage req,
    [SwashBuckleClient] ISwashBuckleClient swashBuckleClient)
{
    return Task.FromResult(swashBuckleClient.CreateSwaggerUIResponse(req, "swagger/json"));
}

4. Initialize Swagger:

[assembly: WebJobsStartup(typeof(Configuration))]
namespace DummyFunctionApp
{
    public class Configuration : IWebJobsStartup
    {
        public void Configure(IWebJobsBuilder builder)
        {
            builder.AddSwashBuckle(Assembly.GetExecutingAssembly());
        }
    }
}

These steps should be enough to generate the OpenAPI document and Swagger UI page —you can validate it by running your app and opening endpoints specified in both new swagger functions. If something is not working, double-check with a working example (take a good luck at versions of functions and target framework).

From OpenApi to TypeScript

Great, we now have our API defined nicely as an OpenAPI JSON. How to create TS typings from it? Luckily, there are few node packages available in the open-source world to help us with this task. I’ve given some of them a go and the best one for our use-case seems to be typescript-rest-swagger, as it can generate the typings based on URL to OpenAPI document (so URL to our function) — perfect!

We’ll add the process of generating new typings to our GitHub deployment action— so we’ll release a new package whenever we make any changes to the API. For versioning, we’ll use semantic release. Thanks to that, we’ll be able to control when exactly are we generating a new package version (and which version number, x.y.z, are we bumping).

Below is a GitHub action to generate TypeScript typing into a file and to push it to npm with version set by semantic release. The snippet doesn’t contain building and deploying Azure Functions, so make sure to keep it in mind — there’s a full yml with NET part here

Before running the script, make sure to:

  • add npm token with write capabilities as NPM_TOKEN to the secrets of your GitHub repository
  • update the link to the OpenAPI document for your API
  • create /Types folder in your repo — it should contain packages.json and packages-lock.json files where we specify package dependencies and provide metadata like package name and author
name: Generate typescript package based on swagger link

on:
  [push]
  
env:
  SWAGGER_LINK: https://mediumfunctionapp.azurewebsites.net/api/swagger/json   # swagger document link (not UI)
  TYPES_FILENAME: index.d.ts                                                   # file with types
  TYPES_FOLDER: ./Types                                                        # subfolder for npm package generation

jobs:
  release:
   name: release
   runs-on: ubuntu-18.04
   steps:
   - uses: actions/checkout@v2
  
  - name: Get package to generate types
     run: npm i swagger-typescript-api

   - name: Wait to make sure new swagger document is up
     uses: jakejarvis/[email protected]
     with:
        time: '30s'

   - name: Generate types
     run: npx swagger-typescript-api -p ${{ env.SWAGGER_LINK }} -o ./ -n ${{ env.TYPES_FILENAME }}
     working-directory: ${{ env.TYPES_FOLDER }}
       
   - name: Setup Node.js
     uses: actions/setup-node@v1
     with:
        node-version: 12
        
   - name: Install type package dependencies
     run: npm ci
     working-directory: ${{ env.TYPES_FOLDER }}

   - name: Publish package with types
     env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 
        NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
     run: npx semantic-release
     working-directory: ${{ env.TYPES_FOLDER }}

After incorportating above pieces of code into your GitHub workflow, make sure to push the changes with a commit name which is in line with Angular guidelines regarding commits —this will trigger the semantic release logic to update the version, try “fix: added npm types publishing” to bump the patch component of package version.

C# -> TypeScript ✅🎉🎉🎉

Hopefully, your package should be published to npm and available to download from the frontend project. All of the methods and DTOs should be usable, thanks to which:

a) frontend developers won’t need to maintain copy models locally
b) TypeScript will complain about any changes in new package versions which would impact existing logic.

Some things to keep in mind regarding this proof of concept:

  • above code publishes the package in the public feed of npm, think twice about doing so, as everyone will have access to your API documentation and endpoints
  • as our typings generation happens right after functions deploy and uses the deployed function with OpenAPI documentation, it might happen that the functions are not deployed before the typings generation takes place — it might be a good idea to generate typings as part of the build

Thanks for reading and make sure to let me know what are your thought about this approach— especially if something is not working. Good luck! 🥳


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK