Important:Before running this generator, you must create an application using the application generator.Then you must run the command from the root directory of the application.

Synopsis

Generates artifacts from an OpenAPI spec into a LoopBack application.

  1. lb4 openapi [<url>] [options]

Options

  • —url: URL or file path of the OpenAPI spec.
  • —validate: Validate the OpenAPI spec. Default: false.
  • —promote-anonymous-schemas: Promote anonymous schemas as models classes.Default: false.

Arguments

<url>: URL or file path of the OpenAPI spec. Type: String. Required: false.

Supported OpenAPI spec versions

Interactive Prompts

The tool will prompt you for:

  • URL or file path of the OpenAPI spec If the url or file path is suppliedfrom the command line, the prompt is skipped.
  • Select controllers to be generated You can select what controllers will begenerated based on OpenAPI tags.

Generated artifacts

The command generates the following artifacts:

  • For each schema under components.schemas, a model class or typedeclaration is generated as src/models/<model-or-type-name>.model.ts.Simple types, array types, composite types (allOf/anyOf/oneOf) are mapped toTypeScript type declarations. Object types are mapped to TypeScript classes.

For example,

src/models/message.model.ts

  1. export type Message = string;

src/models/order-enum.model.ts

  1. export type OrderEnum = 'ascending' | 'descending';

src/models/comments.model.ts

  1. import {Comment} from './comment.model';
  2. export type Comments = Comment[];

src/models/cart.model.ts

  1. import {model, property} from '@loopback/repository';
  2. import {CartShippingZone} from './cart-shipping-zone.model';
  3. import {CartStoreInfo} from './cart-store-info.model';
  4. import {CartWarehouse} from './cart-warehouse.model';
  5. /**
  6. * The model class is generated from OpenAPI schema - Cart
  7. * Cart
  8. */
  9. @model({name: 'Cart'})
  10. export class Cart {
  11. constructor(data?: Partial<Cart>) {
  12. if (data != null && typeof data === 'object') {
  13. Object.assign(this, data);
  14. }
  15. }
  16. @property({name: 'additional_fields'})
  17. additional_fields?: {};
  18. @property({name: 'custom_fields'})
  19. custom_fields?: {};
  20. @property({name: 'db_prefix'})
  21. db_prefix?: string;
  22. @property({name: 'name'})
  23. name?: string;
  24. @property({name: 'shipping_zones'})
  25. shipping_zones?: CartShippingZone[];
  26. @property({name: 'stores_info'})
  27. stores_info?: CartStoreInfo[];
  28. @property({name: 'url'})
  29. url?: string;
  30. @property({name: 'version'})
  31. version?: string;
  32. @property({name: 'warehouses'})
  33. warehouses?: CartWarehouse[];
  34. }

src/models/id-type.model.ts

  1. export type IdType = string | number;

src/models/pet.model.ts

  1. import {NewPet} from './new-pet.model.ts';
  2. export type Pet = NewPet & {id: number};
  • Anonymous schemas of object/array types are generated as inline TypeScripttype literals or separate model classes/types depending on—promote-anonymous-schemas flag (default to false).For example, the following OpenAPI spec snippet uses anonymous schemas forrequest and response body objects.
  1. openapi: 3.0.0
  2. // ...
  3. paths:
  4. // ...
  5. /{dataset}/{version}/records:
  6. post:
  7. // ...
  8. operationId: perform-search
  9. parameters:
  10. // ...
  11. responses:
  12. '200':
  13. description: successful operation
  14. content:
  15. application/json:
  16. schema:
  17. type: array
  18. items:
  19. type: object
  20. additionalProperties:
  21. type: object
  22. '404':
  23. description: No matching record found for the given criteria.
  24. requestBody:
  25. content:
  26. application/x-www-form-urlencoded:
  27. schema:
  28. type: object
  29. properties:
  30. criteria:
  31. description: >-
  32. Uses Lucene Query Syntax in the format of
  33. propertyName:value, propertyName:[num1 TO num2] and date
  34. range format: propertyName:[yyyyMMdd TO yyyyMMdd]. In the
  35. response please see the 'docs' element which has the list of
  36. record objects. Each record structure would consist of all
  37. the fields and their corresponding values.
  38. type: string
  39. default: '*:*'
  40. start:
  41. description: Starting record number. Default value is 0.
  42. type: integer
  43. default: 0
  44. rows:
  45. description: >-
  46. Specify number of rows to be returned. If you run the search
  47. with default values, in the response you will see 'numFound'
  48. attribute which will tell the number of records available in
  49. the dataset.
  50. type: integer
  51. default: 100
  52. required:
  53. - criteria

Without —promote-anonymous-schemas, no separate files are generated foranonymous schemas. The controller class uses inline TypeScript type literals asshown below.

src/controllers/search.controller.ts

  1. @operation('post', '/{dataset}/{version}/records')
  2. async performSearch(
  3. @requestBody()
  4. body: {
  5. criteria: string;
  6. start?: number;
  7. rows?: number;
  8. },
  9. @param({name: 'version', in: 'path'}) version: string,
  10. @param({name: 'dataset', in: 'path'}) dataset: string,
  11. ): Promise<
  12. {
  13. [additionalProperty: string]: {};
  14. }[]
  15. > {
  16. throw new Error('Not implemented');
  17. }

On contrast, if lb4 openapi —promote-anonymous-schemas is used, twoadditional model files are generated:

src/models/perform-search-body.model.ts

  1. /* eslint-disable @typescript-eslint/no-explicit-any */
  2. import {model, property} from '@loopback/repository';
  3. /**
  4. * The model class is generated from OpenAPI schema - performSearchBody
  5. * performSearchBody
  6. */
  7. @model({name: 'performSearchBody'})
  8. export class PerformSearchBody {
  9. constructor(data?: Partial<PerformSearchBody>) {
  10. if (data != null && typeof data === 'object') {
  11. Object.assign(this, data);
  12. }
  13. }
  14. /**
  15. * Uses Lucene Query Syntax in the format of propertyName:value, propertyName:[num1 TO num2] and date range format: propertyName:[yyyyMMdd TO yyyyMMdd]. In the response please see the 'docs' element which has the list of record objects. Each record structure would consist of all the fields and their corresponding values.
  16. */
  17. @property({name: 'criteria'})
  18. criteria: string = '*:*';
  19. /**
  20. * Starting record number. Default value is 0.
  21. */
  22. @property({name: 'start'})
  23. start?: number = 0;
  24. /**
  25. * Specify number of rows to be returned. If you run the search with default values, in the response you will see 'numFound' attribute which will tell the number of records available in the dataset.
  26. */
  27. @property({name: 'rows'})
  28. rows?: number = 100;
  29. }

src/models/perform-search-response-body.model.ts

  1. export type PerformSearchResponseBody = {
  2. [additionalProperty: string]: {};
  3. }[];
  • The generator groups operations (paths.<path>.<verb>) by tags. If no tag ispresent, it defaults to OpenApi. For each tag, a controller class isgenerated as src/controllers/<tag-name>.controller.ts to hold alloperations with the same tag.Controller class names are derived from tag names. The x-controller-nameproperty of an operation can be used to customize the controller name. Methodnames are derived from operationIds. They can be configured usingx-operation-name.

For example,

  1. import {operation, param} from '@loopback/rest';
  2. import {DateTime} from '../models/date-time.model';
  3. /**
  4. * The controller class is generated from OpenAPI spec with operations tagged
  5. * by account
  6. *
  7. */
  8. export class AccountController {
  9. constructor() {}
  10. /**
  11. * Get list of carts.
  12. */
  13. @operation('get', '/account.cart.list.json')
  14. async accountCartList(
  15. @param({name: 'params', in: 'query'}) params: string,
  16. @param({name: 'exclude', in: 'query'}) exclude: string,
  17. @param({name: 'request_from_date', in: 'query'}) request_from_date: string,
  18. @param({name: 'request_to_date', in: 'query'}) request_to_date: string,
  19. ): Promise<{
  20. result?: {
  21. carts?: {
  22. cart_id?: string;
  23. id?: string;
  24. store_key?: string;
  25. total_calls?: string;
  26. url?: string;
  27. }[];
  28. carts_count?: number;
  29. };
  30. return_code?: number;
  31. return_message?: string;
  32. }> {
  33. throw new Error('Not implemented');
  34. }
  35. /**
  36. * Update configs in the API2Cart database.
  37. */
  38. @operation('put', '/account.config.update.json')
  39. async accountConfigUpdate(
  40. @param({name: 'db_tables_prefix', in: 'query'}) db_tables_prefix: string,
  41. @param({name: 'client_id', in: 'query'}) client_id: string,
  42. @param({name: 'bridge_url', in: 'query'}) bridge_url: string,
  43. @param({name: 'store_root', in: 'query'}) store_root: string,
  44. @param({name: 'shared_secret', in: 'query'}) shared_secret: string,
  45. ): Promise<{
  46. result?: {
  47. updated_items?: number;
  48. };
  49. return_code?: number;
  50. return_message?: string;
  51. }> {
  52. throw new Error('Not implemented');
  53. }
  54. /**
  55. * List webhooks that was not delivered to the callback.
  56. */
  57. @operation('get', '/account.failed_webhooks.json')
  58. async accountFailedWebhooks(
  59. @param({name: 'count', in: 'query'}) count: number,
  60. @param({name: 'start', in: 'query'}) start: number,
  61. @param({name: 'ids', in: 'query'}) ids: string,
  62. ): Promise<{
  63. result?: {
  64. all_failed_webhook?: string;
  65. webhook?: {
  66. entity_id?: string;
  67. time?: DateTime;
  68. webhook_id?: number;
  69. }[];
  70. };
  71. return_code?: number;
  72. return_message?: string;
  73. }> {
  74. throw new Error('Not implemented');
  75. }
  76. }

OpenAPI Examples

Try out the following specs or your own with lb4 openapi: