Skip to content

Commit 6deb05f

Browse files
committed
feat: generate OpenAPI TypeScript client for Next.js frontend
1 parent 4d59428 commit 6deb05f

File tree

10 files changed

+814
-693
lines changed

10 files changed

+814
-693
lines changed

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ SENTRY_DSN=
4343
# Configure these with your own Docker registry images
4444
DOCKER_IMAGE_BACKEND=backend
4545
DOCKER_IMAGE_FRONTEND=frontend
46+
DOCKER_IMAGE_FRONTEND_NEXTJS=frontend-nextjs
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { ApiRequestOptions } from './ApiRequestOptions';
2-
import type { ApiResult } from './ApiResult';
1+
import type { ApiRequestOptions } from "./ApiRequestOptions";
2+
import type { ApiResult } from "./ApiResult";
33

44
export class ApiError extends Error {
55
public readonly url: string;
@@ -8,14 +8,18 @@ export class ApiError extends Error {
88
public readonly body: unknown;
99
public readonly request: ApiRequestOptions;
1010

11-
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
11+
constructor(
12+
request: ApiRequestOptions,
13+
response: ApiResult,
14+
message: string,
15+
) {
1216
super(message);
1317

14-
this.name = 'ApiError';
18+
this.name = "ApiError";
1519
this.url = response.url;
1620
this.status = response.status;
1721
this.statusText = response.statusText;
1822
this.body = response.body;
1923
this.request = request;
2024
}
21-
}
25+
}

frontend-nextjs/src/client/core/ApiRequestOptions.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ export type ApiRequestOptions<T = unknown> = {
66
readonly headers?: Record<string, unknown>;
77
readonly mediaType?: string;
88
readonly method:
9-
| 'DELETE'
10-
| 'GET'
11-
| 'HEAD'
12-
| 'OPTIONS'
13-
| 'PATCH'
14-
| 'POST'
15-
| 'PUT';
9+
| "DELETE"
10+
| "GET"
11+
| "HEAD"
12+
| "OPTIONS"
13+
| "PATCH"
14+
| "POST"
15+
| "PUT";
1616
readonly path?: Record<string, unknown>;
1717
readonly query?: Record<string, unknown>;
1818
readonly responseHeader?: string;
1919
readonly responseTransformer?: (data: unknown) => Promise<T>;
2020
readonly url: string;
21-
};
21+
};

frontend-nextjs/src/client/core/ApiResult.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ export type ApiResult<TData = any> = {
44
readonly status: number;
55
readonly statusText: string;
66
readonly url: string;
7-
};
7+
};

frontend-nextjs/src/client/core/CancelablePromise.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export class CancelError extends Error {
22
constructor(message: string) {
33
super(message);
4-
this.name = 'CancelError';
4+
this.name = "CancelError";
55
}
66

77
public get isCancelled(): boolean {
@@ -30,8 +30,8 @@ export class CancelablePromise<T> implements Promise<T> {
3030
executor: (
3131
resolve: (value: T | PromiseLike<T>) => void,
3232
reject: (reason?: unknown) => void,
33-
onCancel: OnCancel
34-
) => void
33+
onCancel: OnCancel,
34+
) => void,
3535
) {
3636
this._isResolved = false;
3737
this._isRejected = false;
@@ -64,15 +64,15 @@ export class CancelablePromise<T> implements Promise<T> {
6464
this.cancelHandlers.push(cancelHandler);
6565
};
6666

67-
Object.defineProperty(onCancel, 'isResolved', {
67+
Object.defineProperty(onCancel, "isResolved", {
6868
get: (): boolean => this._isResolved,
6969
});
7070

71-
Object.defineProperty(onCancel, 'isRejected', {
71+
Object.defineProperty(onCancel, "isRejected", {
7272
get: (): boolean => this._isRejected,
7373
});
7474

75-
Object.defineProperty(onCancel, 'isCancelled', {
75+
Object.defineProperty(onCancel, "isCancelled", {
7676
get: (): boolean => this._isCancelled,
7777
});
7878

@@ -86,13 +86,13 @@ export class CancelablePromise<T> implements Promise<T> {
8686

8787
public then<TResult1 = T, TResult2 = never>(
8888
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
89-
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
89+
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
9090
): Promise<TResult1 | TResult2> {
9191
return this.promise.then(onFulfilled, onRejected);
9292
}
9393

9494
public catch<TResult = never>(
95-
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
95+
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
9696
): Promise<T | TResult> {
9797
return this.promise.catch(onRejected);
9898
}
@@ -112,15 +112,15 @@ export class CancelablePromise<T> implements Promise<T> {
112112
cancelHandler();
113113
}
114114
} catch (error) {
115-
console.warn('Cancellation threw an error', error);
115+
console.warn("Cancellation threw an error", error);
116116
return;
117117
}
118118
}
119119
this.cancelHandlers.length = 0;
120-
if (this._reject) this._reject(new CancelError('Request aborted'));
120+
if (this._reject) this._reject(new CancelError("Request aborted"));
121121
}
122122

123123
public get isCancelled(): boolean {
124124
return this._isCancelled;
125125
}
126-
}
126+
}
Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
1-
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
2-
import type { ApiRequestOptions } from './ApiRequestOptions';
1+
import type { AxiosRequestConfig, AxiosResponse } from "axios";
2+
import type { ApiRequestOptions } from "./ApiRequestOptions";
33

44
type Headers = Record<string, string>;
55
type Middleware<T> = (value: T) => T | Promise<T>;
66
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
77

88
export class Interceptors<T> {
9-
_fns: Middleware<T>[];
9+
_fns: Middleware<T>[];
1010

11-
constructor() {
12-
this._fns = [];
13-
}
11+
constructor() {
12+
this._fns = [];
13+
}
1414

15-
eject(fn: Middleware<T>): void {
16-
const index = this._fns.indexOf(fn);
17-
if (index !== -1) {
18-
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
19-
}
20-
}
15+
eject(fn: Middleware<T>): void {
16+
const index = this._fns.indexOf(fn);
17+
if (index !== -1) {
18+
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
19+
}
20+
}
2121

22-
use(fn: Middleware<T>): void {
23-
this._fns = [...this._fns, fn];
24-
}
22+
use(fn: Middleware<T>): void {
23+
this._fns = [...this._fns, fn];
24+
}
2525
}
2626

2727
export type OpenAPIConfig = {
2828
BASE: string;
29-
CREDENTIALS: 'include' | 'omit' | 'same-origin';
29+
CREDENTIALS: "include" | "omit" | "same-origin";
3030
ENCODE_PATH?: ((path: string) => string) | undefined;
3131
HEADERS?: Headers | Resolver<Headers> | undefined;
3232
PASSWORD?: string | Resolver<string> | undefined;
@@ -41,17 +41,17 @@ export type OpenAPIConfig = {
4141
};
4242

4343
export const OpenAPI: OpenAPIConfig = {
44-
BASE: '',
45-
CREDENTIALS: 'include',
44+
BASE: "",
45+
CREDENTIALS: "include",
4646
ENCODE_PATH: undefined,
4747
HEADERS: undefined,
4848
PASSWORD: undefined,
4949
TOKEN: undefined,
5050
USERNAME: undefined,
51-
VERSION: '0.1.0',
51+
VERSION: "0.1.0",
5252
WITH_CREDENTIALS: false,
5353
interceptors: {
5454
request: new Interceptors(),
5555
response: new Interceptors(),
5656
},
57-
};
57+
};

0 commit comments

Comments
 (0)