From Code to OpenAPI spec with Ease using SkimX
Tags: typescript, api, webdev, express
Published: 2024-04-06 09:44:01

## Introduction

In the vibrant ecosystem of web development, full of complicated tasks and tools, SkimX stands out as a simple solution.. A humble yet transformative tool, SkimX revolutionizes how developers approach API development. Built upon the robust foundations of Zod and Express, SkimX is a wrapper that streamlines request validation and automates one of the most time-consuming and complicated aspects of API development — documentation.

## The Heart of SkimX

SkimX tackles a critical, often tedious aspect of API development head-on: keeping documentation perfectly in sync with your code. It flips the script on the traditional OpenAPI workflow. Instead of starting with documentation and shaping your code to fit, SkimX encourages you to let your code lead the way. As you build and modify, SkimX quietly crafts up-to-date, accurate OpenAPI documentation in the background. This approach ensures your documentation is an exact mirror of your API, leaving no room for discrepancies.

The OpenAPI specification comes straight from the Zod schema, using the wonderful zod-openapi library as an extension. Zod handles request validations (both inbound and outbound) and infers the correct types of the Express Request and Response types.

## Install SkimX

Leaping into SkimX’s world is a breeze. Ready to simplify your API development process? Here’s how you start:

`npm install skimx`

## A SkimX Example to Get You Started

Let’s dive deeper with a comprehensive example, illustrating a SkimX-enabled server and router setup:

```ts
import { Server, z } from 'skimx';
import cors from 'cors';
import helmet from 'helmet';

// server is a wrapper around an express instance
const server = new Server();

// you can use every express middleware as you want
server.use(cors(), helmet());

// Define Schemas with Zod and zod-openapi
// Used to validate an incoming request, infer request type and generate the docs
const PetSchema = z
  .object({
    name: id z.number().openapi({
      description: 'The id of the pet',
      example: 123,
    }),
    name: z.string().openapi({
      description: 'The name of the pet',
      example: 'Sabo',
    }),
    breed: z.string().openapi({
      description: 'The breed of the pet',
      example: 'Keeshond',
    }),
  })
  .openapi({ ref: 'PetSchema' });

const ParamsSchema = z.object({
  id: z.coerce.number().openapi({
    description: 'The id of the pet',
    example: '123',
  }),
});

// router is a wrapper around an express router instance
const router = new Router();

router
  .get('/v1/pets/:id', {
    params: ParamsSchema,
    responses: {
      200: {
        description: 'The pet',
        applicationJson: PetSchema
      },
      404: {
        description: 'Pet not found',
        applicationJson: z.object({message: z.string()})
      }
    }
  },
  [], // add multiple express middleware, if you want
  async (res, res) => {
     const params = req.params; // is of type ParamsSchema
     const pet = await petService.getById(params.id);
     res.json(pet)
  })
  .post('/v1/pets/', {
    body: {
      applicationJson: PetSchema
    },
    responses: {
      201: {
        description: 'The created pet',
        applicationJson: PetSchema,
      },
      400: {
        description: 'Bad request',
        applicationJson: z.object({message: z.string()})
      }
    }
  },
  [],
  async (res, res) => {
     const body = req.body; // is of type PetSchema
     const pet = await petService.save(body);
     res.status(201).json(pet)
  })

// Register and attach the router
server.useRouter(router)

// Start the server
server.listen(3000, () => console.log('Server up on port 3000'));
```

## Generate the OpenAPI specification

By simply writing your server and your Zod schemas, SkimX compiles a complete, accurate documentation spec that represents your API’s current state. This automatic generation transforms a manual, error-prone process into an effortless reflection of your code.

Here’s how SkimX makes documentation a breeze:

```ts
// Retrieve the server with all the attached routers
import server from './my-server';
import { write } from 'skimx/generator';

const schema = {
  schema: {
    info: {
      title: 'My Spec',
      version: '1.0.0',
    },
    // ....
  },
};

write({ server, schema, filename: 'myspec.yaml', format: 'yaml' });
```
The above script can be automated, for example using a Github action.

## Conclusion

SkimX transcends being merely a tool; it’s a fresh approach to API development. It emphasizes that documentation should not be an afterthought but a natural product of the development process. With SkimX, the leap from code to comprehensive OpenAPI documentation is seamless, making API development a straightforward, error-free experience.

As SkimX takes its first steps into the developer community, it extends an open invitation for adoption and contributions: Your insights and feedback can help sculpt SkimX into a cornerstone of API development.

## Reference
[SkimX documentation](https://ootkin.github.io/skimx/)
[SkimX Repository](https://github.com/ootkin/skimx/)