Swagger UI

What is Swagger UI

Swagger UI is an open-source tool that allows you to visualize and interact with APIs (Application Programming Interfaces) defined using the OpenAPI Specification (formerly known as Swagger Specification). The OpenAPI Specification is a standardized format for describing and documenting APIs, including endpoints, request/response data formats, authentication methods, and more.

swaggerui1

OpenAPI document

In order to generate a swagger UI for our API, we first need an OpenAPI document. Luckily this is very easy to do with ts-rest.

npm install @ts-rest/open-api

Now we can use generateOpenApi function provided by this ts-rest library to generate an OpenAPI document for our contract.

import { generateOpenApi } from '@ts-rest/open-api';
 
const ApiContract = contract.router({ 
	posts : PostContract,
	user : UserContract
});
 
const openApiDocument = generateOpenApi(Contracts.ApiContract, {
  info: {
    title: 'API',
    version: '1.0.0',
    
  },
});

Note that we have combined both of our contracts into a single contract. This is similar to how we can nest routers in express.

swaggerui2

Generating the Swagger UI

For this series, since we are using express, we will use the swagger-ui-express library to generate and serve our UI using our server.

npm i swagger-ui-express

Now pass the generated openAPI document to swagger ui.

import * as swaggerUi from 'swagger-ui-express';
 
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openApiDocument));
 
app.listen(...../*same as before*/

Now simply open your browser, and navigate to http://localhost:<port>/api-docs and see the UI for your api live. You can interact with it, and test it, all from the browser.

swaggerui3

Caveat:

There’s a slight difference in opinion about the way the authorization header is handled in swagger ui, and the way that the ts-rest generates the OpenAPI document. For more information on the issue refer to this github issue.

To fix the issue, you can simply patch the OpenAPI document beforehand. Heres a simple function for the patch:

import { generateOpenApi } from '@ts-rest/open-api';
 
export function patchOpenAPIDocument(openApiDocument : ReturnType<typeof generateOpenApi>){
    openApiDocument.components = openApiDocument.components || {};
    openApiDocument.components.securitySchemes = openApiDocument.components.securitySchemes || {};
    openApiDocument.components.securitySchemes.auth = {
      type : 'apiKey',
      in : 'header',
      name : 'authorization' 
    }
    openApiDocument.security = openApiDocument.security || [];
    openApiDocument.security.push({
      auth : []
    });
    
    Object.entries(openApiDocument.paths).map(([endpoint, methods]) => {
      Object.entries(methods).map(([method, implementation] : [method : any, implementation : any]) => {
        implementation.parameters = implementation.parameters.filter((obj : any) => !(obj.name === "authorization" && obj.in === "header"))
      });
    });
}