apifire

Generates express validators/routers/controllers in Typescript using an OpenAPI 3 spec.

Usage no npm install needed!

<script type="module">
  import apifire from 'https://cdn.skypack.dev/apifire';
</script>

README

apifire

A support library designed to work with the yeoman generator, apifire-server.

It takes an OpenAPI 3 spec, and generates routes, controllers, and validators for express in Typescript.

This library is a work-in-progress (alpha level, in my opinion), and works specifically with the author's use-cases.

It's been tested with the following files:

  • tests/openapi3/petstore.yaml
  • tests/openapi3/bt-example.yaml
  • tests/openapi3/spotlight-example.yaml

Notes:

  • For responses, only 200 responses are used
  • Cookies / headers / auth definitions are not used
  • $ref is supported in responses and parameters
  • Use parameters to define non-post body params, use requestBody for body params
  • Content type application/json is only supported for requestBody and responses
  • Very basic securitySchemes support - only the first security definition is used to protect an endpoint

Table of Contents

Fork notice

This library is a fork of the openapi3-generator project.

Changes

  • Uses a custom json-schema-ref-parser-alt build that adds a x-original-ref property as a reference to the original $ref to the output schema, which is useful for static analysis when trying to generate types.
  • The templates that came with openapi3-generator were removed.
  • A new set of templates were added to work with apifire-server.
  • A prettifier was introduced to prettify the generated Typescript files.

Install

To use it from the CLI:

npm install -g apifire

Usage

Quick usage

In the directory where the apifire-server generated service is, run:

$ apifire api.yaml api-server

Where api.yaml is the OpenAPI 3 spec file. You should always run this command whenever the spec file changes.

This will generate the following structure:

/<service root>
├── src/
|   ├── controllers-generated/
|   |   └──  <operation>.ts
|   ├── interfaces/
|   |   └──  api.ts
|   ├── routers/
|   |   ├── <operation>.router.ts
|   |   ├── validators/
|   |   |   └── <operation>.validator.ts

In day-to-day usage, the controllers-generated/ directory contains the files that you may edit / pluck to the service's controllers/ directory as you will implement your business logic in them.

The other directories and their files should never be modified.

CLI reference

  Usage: apifire [options] <openapiFileOrURL> <template>

  Options:

    -V, --version                  output the version number
    -o, --output <outputDir>       directory where to put the generated files (defaults to current directory)
    -t, --templates <templateDir>  directory where templates are located (defaults to internal templates directory)
    -b, --basedir <baseDir>        directory to use as the base when resolving local file references (defaults to OpenAPI file directory)
    -h, --help                     output usage information

Sample output

Stoplight Studio is a recommended way to design your OpenAPI spec.

The following items were generated using the tests/openapi3/stoplight-example.yaml file.

Interfaces

export interface CreateAccountParams {
  /**
   * Account email
   */
  email: string
  /**
   * Hashed password
   */
  passHash: string
  /**
   * Authentication type
   */
  authType: string
  /**
   * Code to verify account
   */
  verifyCode: string

  /**
   * Account id in path
   */
  pAccountId: string
}

export interface CreateAccountResponse {
  status?: number
  /**
   * Created account id
   */
  id?: string
}

Router

/**
 * Creates a new account
 */
router.post(
  '/:pAccountId',
  async (req: IRequest, res: Response, next: NextFunction) => {
    const params: ApiInterfaces.CreateAccountParams = {
      pAccountId: (req.params.pAccountId as unknown) as string,

      email: (req.body.email as unknown) as string,
      passHash: (req.body.passHash as unknown) as string,
      authType: (req.body.authType as unknown) as string,
      verifyCode: (req.body.verifyCode as unknown) as string
    }

    try {
      validateCreateAccountParams(params)

      const result = await createAccount(req.context, params)
      res.status(result.status || 200)

      delete result.status

      res.send(result)
    } catch (err) {
      next(err)
    }
  }
)

Validator

apifire generates validators that validate the incoming request parameters.

const createAccountValidator = ajv.compile({
  type: 'object',
  required: ['pAccountId', 'email', 'passHash', 'authType', 'verifyCode'],
  properties: {
    pAccountId: { type: 'string' },
    email: {
      type: 'string',
    },
    passHash: {
      type: 'string',
    },
    authType: {
      type: 'string',
    },
    verifyCode: {
      type: 'string',
    }
  }
})

export function validateCreateAccountParams (params) {
  const valid = createAccountValidator(params)

  if (!valid) {
    throw getErrRegistry()
      .newError('VALIDATION_FAILURE', 'INVALID_REQ_PARAMS')
      .withSafeMetadata({
        validations: createAccountValidator.errors
      })
  }
}

Controller

You fill in your business logic in a controller, which is called by the router.

/**
 * @param {IRequestContext} context
 * @param {Object} params
 * @throws {Error}
 * @return {Promise}
 */
export async function createAccount (
  context: IRequestContext,
  params: ApiInterfaces.CreateAccountParams
): Promise<ApiInterfaces.CreateAccountResponse> {

  return {
    id: ''
  }
}

Authors