Skip to content

Commit

Permalink
Merge branch 'main' into chore/nextjs-example
Browse files Browse the repository at this point in the history
  • Loading branch information
7nohe committed May 3, 2024
2 parents d6f75fe + d9be9f0 commit e39bcbf
Show file tree
Hide file tree
Showing 39 changed files with 710 additions and 488 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ jobs:
- name: Run tsc
run: pnpm --filter @7nohe/react-app test:generated

- name: Run biome
run: pnpm biome check .
if: ${{ matrix.os == 'ubuntu-latest' }}

- name: Run test
run: pnpm test

Expand Down
26 changes: 26 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$schema": "https://biomejs.dev/schemas/1.7.2/schema.json",
"organizeImports": {
"enabled": true
},
"files": {
"ignore": [
"dist",
"examples/react-app/openapi",
"coverage",
"examples/nextjs-app/openapi",
"examples/nextjs-app/.next"
]
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
}
}
2 changes: 1 addition & 1 deletion examples/nextjs-app/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Providers from './providers'
import Providers from "./providers";

const inter = Inter({ subsets: ["latin"] });

Expand Down
14 changes: 7 additions & 7 deletions examples/nextjs-app/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { prefetchUseDefaultServiceFindPets } from "@/openapi/queries/prefetch";
import {
dehydrate,
HydrationBoundary,
QueryClient,
} from '@tanstack/react-query'
import { prefetchUseDefaultServiceFindPets } from "@/openapi/queries/prefetch";
dehydrate,
} from "@tanstack/react-query";
import Pets from "./pets";

export default async function Home() {
const queryClient = new QueryClient()
const queryClient = new QueryClient();

await prefetchUseDefaultServiceFindPets(queryClient, {
limit: 10,
tags: [],
})
});

return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<HydrationBoundary state={dehydrate(queryClient)}>
<Pets />
</HydrationBoundary>
<Pets />
</HydrationBoundary>
</main>
);
}
8 changes: 4 additions & 4 deletions examples/nextjs-app/app/pets.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'
"use client";

import { useDefaultServiceFindPets } from "@/openapi/queries";
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

export default function Pets() {
const { data } = useDefaultServiceFindPets({
Expand All @@ -11,7 +11,7 @@ export default function Pets() {

return (
<>
<h1>Pet List</h1>
<h1>Pet List</h1>
<ul>
{Array.isArray(data) &&
data?.map((pet, index) => (
Expand All @@ -20,5 +20,5 @@ export default function Pets() {
</ul>
<ReactQueryDevtools initialIsOpen={false} />
</>
)
);
}
31 changes: 15 additions & 16 deletions examples/nextjs-app/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client'
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// We can not useState or useRef in a server component, which is why we are
// extracting this part out into it's own file with 'use client' on top
import { useState } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useState } from "react";

function makeQueryClient() {
return new QueryClient({
Expand All @@ -14,33 +14,32 @@ function makeQueryClient() {
staleTime: 60 * 1000,
},
},
})
});
}

let browserQueryClient: QueryClient | undefined = undefined
let browserQueryClient: QueryClient | undefined = undefined;

function getQueryClient() {
if (typeof window === 'undefined') {
if (typeof window === "undefined") {
// Server: always make a new query client
return makeQueryClient()
} else {
// Browser: make a new query client if we don't already have one
// This is very important so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient()
return browserQueryClient
return makeQueryClient();
}
// Browser: make a new query client if we don't already have one
// This is very important so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}

export default function Providers({ children }: { children: React.ReactNode }) {
// NOTE: Avoid useState when initializing the query client if you don't
// have a suspense boundary between this and the code that may
// suspend because React will throw away the client on the initial
// render if it suspends and there is no boundary
const queryClient = getQueryClient()
const queryClient = getQueryClient();

return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)
);
}
47 changes: 25 additions & 22 deletions examples/nextjs-app/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,48 @@ const axiosInstance = axios.create({

// Add a request interceptor
axiosInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
if (!config.url || !config.params) {
return config;
}

Object.entries<any>(config.params).forEach(([key, value]) => {
const stringToSearch = `{${key}}`;
if(config.url !== undefined && config.url.search(stringToSearch) !== -1) {
config.url = config.url.replace(`{${key}}`, encodeURIComponent(value));
delete config.params[key];
}
});
(config) => {
// Do something before request is sent
if (!config.url || !config.params) {
return config;
}

return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
for (const [key, value] of Object.entries<string>(config.params)) {
const stringToSearch = `{${key}}`;
if (
config.url !== undefined &&
config.url.search(stringToSearch) !== -1
) {
config.url = config.url.replace(`{${key}}`, encodeURIComponent(value));
delete config.params[key];
}
}

return config;
},
(error) => {
// Do something with request error
return Promise.reject(error);
},
);

// Add a response interceptor
axiosInstance.interceptors.response.use(
function (response) {
(response) => {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function (error) {
(error) => {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
}
},
);

export const request = <T>(
config: OpenAPIConfig,
options: ApiRequestOptions
options: ApiRequestOptions,
): CancelablePromise<T> => {
return new CancelablePromise((resolve, reject, onCancel) => {
onCancel(() => source.cancel("The user aborted a request."));
Expand Down
4 changes: 2 additions & 2 deletions examples/react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@
"dev:mock": "prism mock ./petstore.yaml --dynamic",
"build": "tsc && vite build",
"preview": "vite preview",
"generate:api": "rimraf ./openapi && node ../../dist/cli.mjs -i ./petstore.yaml -c axios --request ./request.ts",
"generate:api": "rimraf ./openapi && node ../../dist/cli.mjs -i ./petstore.yaml -c axios --request ./request.ts --format=biome --lint=biome",
"test:generated": "tsc -p ./tsconfig.openapi.json --noEmit"
},
"dependencies": {
"@tanstack/react-query": "^5.18.1",
"axios": "^1.6.7",
"form-data": "~4.0.0",
"prettier": "^3.2.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@biomejs/biome": "1.7.2",
"@stoplight/prism-cli": "^5.5.2",
"@types/react": "^18.2.52",
"@types/react-dom": "^18.2.18",
Expand Down
47 changes: 25 additions & 22 deletions examples/react-app/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,48 @@ const axiosInstance = axios.create({

// Add a request interceptor
axiosInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
if (!config.url || !config.params) {
return config;
}

Object.entries<any>(config.params).forEach(([key, value]) => {
const stringToSearch = `{${key}}`;
if(config.url !== undefined && config.url.search(stringToSearch) !== -1) {
config.url = config.url.replace(`{${key}}`, encodeURIComponent(value));
delete config.params[key];
}
});
(config) => {
// Do something before request is sent
if (!config.url || !config.params) {
return config;
}

return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
for (const [key, value] of Object.entries<string>(config.params)) {
const stringToSearch = `{${key}}`;
if (
config.url !== undefined &&
config.url.search(stringToSearch) !== -1
) {
config.url = config.url.replace(`{${key}}`, encodeURIComponent(value));
delete config.params[key];
}
}

return config;
},
(error) => {
// Do something with request error
return Promise.reject(error);
},
);

// Add a response interceptor
axiosInstance.interceptors.response.use(
function (response) {
(response) => {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function (error) {
(error) => {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
}
},
);

export const request = <T>(
config: OpenAPIConfig,
options: ApiRequestOptions
options: ApiRequestOptions,
): CancelablePromise<T> => {
return new CancelablePromise((resolve, reject, onCancel) => {
onCancel(() => source.cancel("The user aborted a request."));
Expand Down
19 changes: 11 additions & 8 deletions examples/react-app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import "./App.css";
import { useState } from "react";
import {
UseDefaultServiceFindPetsKeyFn,
useDefaultServiceAddPet,
useDefaultServiceFindPets,
UseDefaultServiceFindPetsKeyFn,
useDefaultServiceGetNotDefined,
useDefaultServicePostNotDefined,
} from "../openapi/queries";
import { useState } from "react";
import { queryClient } from "./queryClient";
import { SuspenseParent } from "./components/SuspenseParent";
import { queryClient } from "./queryClient";

function App() {
const [tags, _setTags] = useState<string[]>([]);
Expand All @@ -17,7 +17,7 @@ function App() {
const { data, error, refetch } = useDefaultServiceFindPets({ tags, limit });
// This is an example of using a hook that has all parameters optional;
// Here we do not have to pass in an object
const {} = useDefaultServiceFindPets();
const { data: _ } = useDefaultServiceFindPets();

// This is an example of a query that is not defined in the OpenAPI spec
// this defaults to any - here we are showing how to override the type
Expand All @@ -32,20 +32,23 @@ function App() {
return (
<div>
<p>Failed to fetch pets</p>
<button onClick={() => refetch()}>Retry</button>
<button type="button" onClick={() => refetch()}>
Retry
</button>
</div>
);

return (
<div className="App">
<h1>Pet List</h1>
<ul>
{data instanceof Array &&
{Array.isArray(data) &&
data?.map((pet, index) => (
<li key={pet.id + "-" + index}>{pet.name}</li>
<li key={`${pet.id}-${index}`}>{pet.name}</li>
))}
</ul>
<button
type="button"
onClick={() => {
addPet(
{
Expand All @@ -59,7 +62,7 @@ function App() {
console.log("success");
},
onError: (error) => console.error(error),
}
},
);
}}
>
Expand Down
Loading

0 comments on commit e39bcbf

Please sign in to comment.