Skip to content

Commit b7871cc

Browse files
committed
chore: migrate from openapi-typescript-codegen to @hey-api/openapi-ts
Replace the unmaintained openapi-typescript-codegen with @hey-api/openapi-ts and @hey-api/client-fetch. - Replace openapi-typescript-codegen with @hey-api/openapi-ts (dev) and @hey-api/client-fetch (runtime) - Add openapi-ts.config.ts with @hey-api/client-fetch, @hey-api/typescript, and @hey-api/sdk plugins - Switch to flat tree-shakeable functions instead of class-based services - Update operation_ids to entity-prefixed names (e.g. createTodo, getAllTodos) - Replace OpenAPI.BASE/TOKEN with client.setConfig({ baseUrl, auth }) - Update useTodoAPI hook to new SDK function signatures - Add 'generate' npm script for running codegen via pinned dependency - Update mise task and documentation
1 parent dce84d0 commit b7871cc

46 files changed

Lines changed: 3071 additions & 950 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.mise/tasks/generate.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ run = "uv run python -m app open-api"
66

77
["generate:api-client-from-spec"]
88
description = "Generate typescript types from the OpenAPI JSON Schema"
9+
depends = ["generate:openapi-spec"]
910
dir = "{{config_root}}/web"
10-
run = "openapi -i ../api/.openapi.json -o './src/api/generated'"
11+
run = "yarn install && yarn generate"

.pre-commit-config.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@ repos:
1818
- id: generate-api-client-from-spec
1919
name: Generate the client for our api
2020
entry: mise run generate:api-client-from-spec
21-
language: node
21+
language: system
2222
files: ^api/.openapi.json$
2323
pass_filenames: false
24-
additional_dependencies:
25-
- openapi-typescript-codegen@0.29.0
2624

2725
- repo: https://github.com/compilerla/conventional-pre-commit
2826
rev: v4.4.0

api/.openapi.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"todo"
1717
],
1818
"summary": "Get Todo All",
19-
"operationId": "get_all",
19+
"operationId": "getAllTodos",
2020
"responses": {
2121
"200": {
2222
"description": "Successful Response",
@@ -27,7 +27,7 @@
2727
"$ref": "#/components/schemas/GetTodoAllResponse"
2828
},
2929
"type": "array",
30-
"title": "Response Get All"
30+
"title": "Response Getalltodos"
3131
}
3232
}
3333
}
@@ -140,7 +140,7 @@
140140
"todo"
141141
],
142142
"summary": "Add Todo",
143-
"operationId": "create",
143+
"operationId": "createTodo",
144144
"requestBody": {
145145
"content": {
146146
"application/json": {
@@ -272,7 +272,7 @@
272272
"todo"
273273
],
274274
"summary": "Get Todo By Id",
275-
"operationId": "get_by_id",
275+
"operationId": "getTodoById",
276276
"security": [
277277
{
278278
"OAuth2AuthorizationCodeBearer": []
@@ -403,7 +403,7 @@
403403
"todo"
404404
],
405405
"summary": "Delete Todo By Id",
406-
"operationId": "delete_by_id",
406+
"operationId": "deleteTodoById",
407407
"security": [
408408
{
409409
"OAuth2AuthorizationCodeBearer": []
@@ -534,7 +534,7 @@
534534
"todo"
535535
],
536536
"summary": "Update Todo",
537-
"operationId": "update_by_id",
537+
"operationId": "updateTodoById",
538538
"security": [
539539
{
540540
"OAuth2AuthorizationCodeBearer": []

api/src/app/features/todo/todo_feature.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
router = APIRouter(tags=["todo"], prefix="/todos", route_class=ExceptionHandlingRoute)
2222

2323

24-
@router.post("", operation_id="create")
24+
@router.post("", operation_id="createTodo")
2525
def add_todo(
2626
data: AddTodoRequest,
2727
user: User = Depends(auth_with_jwt),
@@ -30,7 +30,7 @@ def add_todo(
3030
return add_todo_use_case(data=data, user_id=user.user_id, todo_repository=todo_repository)
3131

3232

33-
@router.get("/{id}", operation_id="get_by_id")
33+
@router.get("/{id}", operation_id="getTodoById")
3434
def get_todo_by_id(
3535
id: str,
3636
user: User = Depends(auth_with_jwt),
@@ -39,7 +39,7 @@ def get_todo_by_id(
3939
return get_todo_by_id_use_case(id=id, user_id=user.user_id, todo_repository=todo_repository)
4040

4141

42-
@router.delete("/{id}", operation_id="delete_by_id")
42+
@router.delete("/{id}", operation_id="deleteTodoById")
4343
def delete_todo_by_id(
4444
id: str,
4545
user: User = Depends(auth_with_jwt),
@@ -48,14 +48,14 @@ def delete_todo_by_id(
4848
return delete_todo_use_case(id=id, user_id=user.user_id, todo_repository=todo_repository)
4949

5050

51-
@router.get("", operation_id="get_all")
51+
@router.get("", operation_id="getAllTodos")
5252
def get_todo_all(
5353
user: User = Depends(auth_with_jwt), todo_repository: TodoRepositoryInterface = Depends(get_todo_repository)
5454
) -> list[GetTodoAllResponse]:
5555
return get_todo_all_use_case(user_id=user.user_id, todo_repository=todo_repository) # type: ignore
5656

5757

58-
@router.put("/{id}", operation_id="update_by_id")
58+
@router.put("/{id}", operation_id="updateTodoById")
5959
def update_todo(
6060
id: str,
6161
data: UpdateTodoRequest,
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Generate API clients
22

3-
To generate typescript client for API run:
3+
To generate the typescript client for the API, run:
44

55
```shell
66
cd web
7-
./generate-api-client.sh
7+
yarn generate
88
```
99

10-
This will populate `web/src/api/generated` with new typescript files that matches the API OpenAPI specification.
10+
This uses the configuration in `web/openapi-ts.config.ts` and populates `web/src/api/generated` with new typescript files that match the API's OpenAPI specification.

web/openapi-ts.config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineConfig } from '@hey-api/openapi-ts'
2+
3+
export default defineConfig({
4+
input: '../api/.openapi.json',
5+
output: './src/api/generated',
6+
plugins: [
7+
{
8+
name: '@hey-api/client-fetch',
9+
},
10+
'@hey-api/typescript',
11+
'@hey-api/sdk',
12+
],
13+
})

web/package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
"build": "tsc && vite build",
88
"serve": "vite preview",
99
"compile": "tsc --noEmit",
10+
"generate": "openapi-ts",
1011
"test": "vitest",
1112
"lint": "biome check --write --no-errors-on-unmatched"
1213
},
1314
"dependencies": {
1415
"@equinor/eds-core-react": "^2.4.1",
1516
"@equinor/eds-icons": "^1.3.0",
17+
"@hey-api/client-fetch": "^0.7.0",
1618
"react": "^19.2.5",
1719
"react-dom": "^19.2.5",
1820
"react-oauth2-code-pkce": "^1.24.0",
@@ -21,6 +23,7 @@
2123
},
2224
"devDependencies": {
2325
"@biomejs/biome": "^2.4.10",
26+
"@hey-api/openapi-ts": "^0.82.0",
2427
"@testing-library/dom": "^10.4.1",
2528
"@testing-library/jest-dom": "^6.9.1",
2629
"@testing-library/react": "^16.3.2",
@@ -29,7 +32,6 @@
2932
"@types/react-dom": "^19.2.3",
3033
"@vitejs/plugin-react": "^6.0.1",
3134
"jsdom": "^29.0.2",
32-
"openapi-typescript-codegen": "^0.30.0",
3335
"typescript": "~6.0.2",
3436
"vite": "^8.0.8",
3537
"vite-plugin-checker": "^0.12.0",
@@ -38,5 +40,10 @@
3840
"vite-tsconfig-paths": "^6.1.1",
3941
"vitest": "^4.1.4"
4042
},
41-
"packageManager": "yarn@4.12.0"
43+
"packageManager": "yarn@4.12.0",
44+
"dependenciesMeta": {
45+
"@hey-api/openapi-ts@0.82.5": {
46+
"unplugged": true
47+
}
48+
}
4249
}

web/src/App.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Button, Progress, Typography } from '@equinor/eds-core-react'
2-
import { useContext } from 'react'
2+
import { useContext, useEffect } from 'react'
33
import { AuthContext } from 'react-oauth2-code-pkce'
44
import { RouterProvider } from 'react-router-dom'
55
import styled from 'styled-components'
6-
import { OpenAPI } from './api/generated'
6+
import { client } from './api/generated/client.gen'
77
import Header from './common/components/Header'
88
import { router } from './router'
99

@@ -22,7 +22,11 @@ const CenterContainer = styled.div`
2222
function App() {
2323
const { token, error, logIn, loginInProgress } = useContext(AuthContext)
2424

25-
OpenAPI.TOKEN = token
25+
useEffect(() => {
26+
client.setConfig({
27+
auth: hasAuthConfig && token ? token : undefined,
28+
})
29+
}, [token])
2630

2731
if (hasAuthConfig && error) {
2832
return <CenterContainer>{error}</CenterContainer>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This file is auto-generated by @hey-api/openapi-ts
2+
3+
import type { ClientOptions } from './types.gen';
4+
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client';
5+
6+
/**
7+
* The `createClientConfig()` function will be called on client initialization
8+
* and the returned object will become the client's initial configuration.
9+
*
10+
* You may want to initialize your client this way instead of calling
11+
* `setConfig()`. This is useful for example if you're using Next.js
12+
* to ensure your client always has the correct values.
13+
*/
14+
export type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> = (override?: Config<DefaultClientOptions & T>) => Config<Required<DefaultClientOptions> & T>;
15+
16+
export const client = createClient(createConfig<ClientOptions>());

0 commit comments

Comments
 (0)