1.0.12 • Published 9 months ago

@devlearning/swagger-generator v1.0.12

Weekly downloads
-
License
ISC
Repository
-
Last release
9 months ago

Swagger Generator for Angular & Next.js

This tool automates the generation of API clients for Angular and Next.js using a Swagger (OpenAPI) specification. It creates TypeScript models and service classes, making API integration seamless and type-safe.

Features

  • 🚀 Automatic Model Generation – Converts Swagger schemas into TypeScript interfaces or classes.
  • 🔥 API Client Generation – Creates service methods for making API calls with authentication, headers, and request parameters.
  • 🎯 Framework-Specific Output:
    • Angular – Generates injectable services using HttpClient.
    • Next.js – Provides fetch-based or Axios-based API functions, optimized for both server-side and client-side usage.
  • Strong Typing – Ensures type safety for API requests and responses.
  • Customization – Configurable options for method naming, error handling, and request structure.
  • 🔄 Auto-Sync with Backend – Keeps API clients up-to-date with backend changes.

Installation

npm i @devlearning/swagger-generator --save-dev

add npm script:

"swgen": "swgen http://localhost:7550/swagger/ApiGateway/swagger.json src/app/core/autogenerated angular moment"

params:

  • url of swagger.json: not https
  • output path for autogenerated files: src/app/core/autogenerated
  • target framework/library: angular/next
  • type of date management:
    • angular only support momentjs, sorry :(
    • nextjs only support date-fns

create output path like this: src/app/core/autogenerated

Angular configuration

create ApiService like this

import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ApiAutogeneratedService } from '../autogenerated/api.autogenerated';
import { environment } from 'src/environments/environment';
import { ResponseError } from '../models/response-error';
import { throwError } from 'rxjs';
import * as moment from 'moment-timezone';

@Injectable({
  providedIn: 'root',
})
export class ApiService extends ApiAutogeneratedService {

  private _dateTimeFormat = /^((19|20)[0-9][0-9])[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])[T]([01]?[0-9]|[2][0-3])[:]([0-5][0-9])[:]([0-5][0-9])[.]([0-9][0-9][0-9])([+|-]([01][0-9]|[2][0-3])[:]([0-5][0-9])){0,1}$/;
  private _timeFormat = /^([01]?[0-9]|[2][0-3])[:]([0-5][0-9])[:]([0-5][0-9])[.]([0-9][0-9][0-9])$/;
  private _ianaName: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
  private _dateTimeFormatString = "YYYY-MM-DDTHH:mm:ss.SSSZ";
  private _timeFormatString = "HH:mm:ss.SSS";

  public get ianaName() { return this._ianaName; }

  constructor(
    public override _http: HttpClient,
  ) {
    super(_http, environment.BASE_URL);
  }

  protected override _momentToString(moment: moment.Moment) {
    return (<moment.Moment>moment).format(this._dateTimeFormatString);
  }

  protected override _handleRequest<T>(request: T) {
    if (request === null || request === undefined) {
      return request;
    }
    if (typeof request !== 'object') {
      return request;
    }

    var clonedRequest = { ...request };

    for (const key of Object.keys(clonedRequest)) {
      const value = (<any>clonedRequest)[key];
      if (moment.isMoment(value)) {
        (<any>clonedRequest)[key] = this._momentToString(value);
      } else if (typeof value === 'object') {
        this._handleRequest(value);
      }
    }

    return clonedRequest;
  }

  protected override _handleMultipart<T>(request: T): FormData {
    const formData = new FormData();
    if (request === null || request === undefined) {
      return formData;
    }

    for (const key of Object.keys(request)) {
      const value = (<any>request)[key];
      if (value instanceof File) {
        formData.append(key, value, value.name);
      } else {
        formData.append(key, value.toString());
      }
    }

    return formData;
  }

  public override _handleResponse<T>(response: T) {
    if (response === null || response === undefined) {
      return response;
    }
    if (typeof response !== 'object') {
      return response;
    }

    for (const key of Object.keys(response)) {
      const value = (<any>response)[key];
      if (this._isDateString(value)) {
        (<any>response)[key] = moment.tz(value, this._dateTimeFormatString, this._ianaName);
      } else if (this._isTimeString(value)) {
        (<any>response)[key] = moment.tz(value, this._timeFormatString, this._ianaName);
      } else if (typeof value === 'object') {
        this._handleResponse(value);
      }
    }

    return response;
  }

  protected override _handleError(error: any, obs: any) {
    let responseError = new ResponseError();
    if (error.error instanceof Error) {
      console.error('Api - an error occurred:', error.error.message);
      responseError.message = error.error.message;
    } else if (error instanceof HttpErrorResponse) {
      responseError = ResponseError.CreateFromHttpErrorResponse(error);
    }

    console.error(`Api - error code ${error.status} message ${responseError.message} ${responseError.stacktrace}`);
    
    return throwError(() => responseError);
  }

  private _isDateString(value: string) {
    if (value === null || value === undefined) {
      return false;
    }
    if (typeof value === 'string') {
      return this._dateTimeFormat.test(value);
    }
    return false;
  }

  private _isTimeString(value: string) {
    if (value === null || value === undefined) {
      return false;
    }
    if (typeof value === 'string') {
      return this._timeFormat.test(value);
    }
    return false;
  }
}
import { HttpErrorResponse } from '@angular/common/http';

export class ResponseError {
  public url: string | null;
  public status: number | null;
  public message: string | null;
  public stacktrace: string | null;

  constructor() {
    this.url = null;
    this.status = null;
    this.message = null;
    this.stacktrace = null;
  }

  static CreateFromHttpErrorResponse(httpErrorResponse: HttpErrorResponse) {
    let responseError = new ResponseError();

    if (httpErrorResponse.error != null && !(httpErrorResponse.error instanceof ProgressEvent)) {
      if (httpErrorResponse.error.hasOwnProperty('message')) {
        responseError.message = httpErrorResponse.error.message;
      }

      if (httpErrorResponse.error.hasOwnProperty('stacktrace')) {
        responseError.stacktrace = httpErrorResponse.error.stacktrace;
      }
    } else {
      responseError.message = httpErrorResponse.message;
    }

    responseError.status = httpErrorResponse.status;
    responseError.url = httpErrorResponse.url;

    return responseError;
  }
}
1.0.2

10 months ago

1.0.1

10 months ago

1.0.0

10 months ago

1.0.9

9 months ago

1.0.8

9 months ago

1.0.7

9 months ago

1.0.6

9 months ago

1.0.5

9 months ago

1.0.4

10 months ago

1.0.3

10 months ago

0.0.37

11 months ago

0.0.39

11 months ago

0.0.33

11 months ago

0.0.34

11 months ago

0.0.35

11 months ago

0.0.36

11 months ago

1.0.11

9 months ago

1.0.10

9 months ago

1.0.12

9 months ago

0.0.32

2 years ago

0.0.31

2 years ago

0.0.30

2 years ago

0.0.29

2 years ago

0.0.27

2 years ago

0.0.26

2 years ago

0.0.25

2 years ago

0.0.24

2 years ago

0.0.23

2 years ago

0.0.22

2 years ago

0.0.21

2 years ago

0.0.20

2 years ago

0.0.19

2 years ago

0.0.18

2 years ago

0.0.17

2 years ago

0.0.16

2 years ago

0.0.15

2 years ago

0.0.14

2 years ago

0.0.13

2 years ago

0.0.12

2 years ago

0.0.11

2 years ago

0.0.10

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago