1.0.12 • Published 8 months ago

@devlearning/swagger-generator v1.0.12

Weekly downloads
-
License
ISC
Repository
-
Last release
8 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

9 months ago

1.0.1

9 months ago

1.0.0

9 months ago

1.0.9

8 months ago

1.0.8

8 months ago

1.0.7

8 months ago

1.0.6

8 months ago

1.0.5

8 months ago

1.0.4

9 months ago

1.0.3

9 months ago

0.0.37

9 months ago

0.0.39

9 months ago

0.0.33

9 months ago

0.0.34

9 months ago

0.0.35

9 months ago

0.0.36

9 months ago

1.0.11

8 months ago

1.0.10

8 months ago

1.0.12

8 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