콘텐츠로 이동

React에서 FastAPI로

connection 생성기는 React 웹사이트를 FastAPI 백엔드와 빠르게 통합할 수 있는 방법을 제공합니다. 클라이언트 및 TanStack Query 훅 생성, AWS IAM 및 Cognito 인증 지원, 적절한 오류 처리를 포함하여 FastAPI 백엔드에 연결하는 데 필요한 모든 구성을 타입 안전한 방식으로 설정합니다.

이 생성기를 사용하기 전에 React 애플리케이션에 다음이 있어야 합니다:

  1. 애플리케이션을 렌더링하는 main.tsx 파일
  2. 작동하는 FastAPI 백엔드 (FastAPI 생성기를 사용하여 생성)
  3. Cognito 또는 IAM 인증을 사용하는 API에 연결하는 경우 ts#website#auth 생성기를 통해 추가된 Cognito Auth
필수 main.tsx 구조 예시
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom/client';
import App from './app/app';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
root.render(
<StrictMode>
<App />
</StrictMode>,
);
Terminal window
pnpm nx g @aws/nx-plugin:connection
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
Terminal window
pnpm nx g @aws/nx-plugin:connection --dry-run
매개변수타입기본값설명
sourceProject 필수string-소스 프로젝트
targetProject 필수string-연결할 대상 프로젝트
sourceComponent string-연결을 시작할 소스 컴포넌트 (컴포넌트 이름, 소스 프로젝트 루트 기준 상대 경로, 또는 generator id). 프로젝트를 소스로 명시적으로 선택하려면 '.'을 사용하세요.
targetComponent string-연결할 대상 컴포넌트 (컴포넌트 이름, 대상 프로젝트 루트 기준 상대 경로, 또는 generator id). 프로젝트를 대상으로 명시적으로 선택하려면 '.'을 사용하세요.
preferInstallDependencies booleantrue생성기 실행 후 의존성 설치를 선호할지 여부입니다. 여러 생성기를 일괄 처리할 때 설치를 연기하려면 false로 설정하세요 (후속 생성기가 Nx 프로젝트 그래프를 계산할 수 있도록 필요한 경우 설치는 여전히 실행됩니다); 마지막에 한 번만 설치합니다.

생성기는 FastAPI 프로젝트의 다음 파일을 변경합니다:

  • 디렉터리scripts
    • generate_open_api.py API에 대한 OpenAPI 사양을 생성하는 스크립트 추가
  • project.json 위의 생성 스크립트를 호출하는 새 타겟이 빌드에 추가됨

생성기는 React 애플리케이션의 다음 파일을 변경합니다:

  • 디렉터리src
    • 디렉터리components
      • <ApiName>Provider.tsx API 클라이언트를 위한 Provider
      • QueryClientProvider.tsx TanStack React Query 클라이언트 provider
    • 디렉터리hooks
      • use<ApiName>.tsx TanStack Query로 상태가 관리되는 API 호출을 위한 훅 추가
      • use<ApiName>Client.tsx API를 호출할 수 있는 바닐라 API 클라이언트를 인스턴스화하는 훅 추가
      • useSigV4.tsx SigV4로 HTTP 요청에 서명하는 훅 추가 (IAM 인증을 선택한 경우)
  • project.json 타입 안전 클라이언트를 생성하는 새 타겟이 빌드에 추가됨
  • .gitignore 생성된 클라이언트 파일은 기본적으로 무시됨

생성기는 또한 아직 없는 경우 웹사이트 인프라에 Runtime Config를 추가하여 FastAPI의 API URL이 웹사이트에서 사용 가능하고 use<ApiName>.tsx 훅에 의해 자동으로 구성되도록 합니다.

빌드 시 FastAPI의 OpenAPI 사양에서 타입 안전 클라이언트가 생성됩니다. 이는 React 애플리케이션에 세 개의 새 파일을 추가합니다:

  • 디렉터리src
    • 디렉터리generated
      • 디렉터리<ApiName>
        • types.gen.ts FastAPI에 정의된 pydantic 모델에서 생성된 타입
        • client.gen.ts API 호출을 위한 타입 안전 클라이언트
        • options-proxy.gen.ts TanStack Query를 사용하여 API와 상호 작용하기 위한 TanStack Query 훅 옵션을 생성하는 메서드 제공

생성된 타입 안전 클라이언트를 사용하여 React 애플리케이션에서 FastAPI를 호출할 수 있습니다. TanStack Query 훅을 통해 클라이언트를 사용하는 것이 권장되지만, 원하는 경우 바닐라 클라이언트를 사용할 수도 있습니다.

파일 감시자 의존성

watch-generate:<ApiName>-clientnx watch 명령에 의존하며, 이는 Nx Daemon이 실행 중이어야 합니다. 따라서 데몬을 비활성화한 경우 FastAPI를 변경할 때 클라이언트가 자동으로 재생성되지 않습니다.

생성기는 TanStack Query로 API를 호출하는 데 사용할 수 있는 use<ApiName> 훅을 제공합니다.

queryOptions 메서드를 사용하여 TanStack Query의 useQuery 훅을 사용하여 API를 호출하는 데 필요한 옵션을 검색할 수 있습니다:

import { useQuery } from '@tanstack/react-query';
import { useState, useEffect } from 'react';
import { useMyApi } from './hooks/useMyApi';
function MyComponent() {
const api = useMyApi();
const item = useQuery(api.getItem.queryOptions({ itemId: 'some-id' }));
if (item.isLoading) return <div>Loading...</div>;
if (item.isError) return <div>Error: {item.error.message}</div>;
return <div>Item: {item.data.name}</div>;
}
바닐라 클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

생성된 훅에는 TanStack Query의 useMutation 훅을 사용한 뮤테이션 지원이 포함되어 있습니다. 이는 로딩 상태, 오류 처리 및 낙관적 업데이트를 통해 생성, 업데이트 및 삭제 작업을 처리하는 깔끔한 방법을 제공합니다.

import { useMutation } from '@tanstack/react-query';
import { useMyApi } from './hooks/useMyApi';
function CreateItemForm() {
const api = useMyApi();
// Create a mutation using the generated mutation options
const createItem = useMutation(api.createItem.mutationOptions());
const handleSubmit = (e) => {
e.preventDefault();
createItem.mutate({ name: 'New Item', description: 'A new item' });
};
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button
type="submit"
disabled={createItem.isPending}
>
{createItem.isPending ? 'Creating...' : 'Create Item'}
</button>
{createItem.isSuccess && (
<div className="success">
Item created with ID: {createItem.data.id}
</div>
)}
{createItem.isError && (
<div className="error">
Error: {createItem.error.message}
</div>
)}
</form>
);
}

다양한 뮤테이션 상태에 대한 콜백 추가도 가능합니다:

const createItem = useMutation({
...api.createItem.mutationOptions(),
onSuccess: (data) => {
// This will run when the mutation succeeds
console.log('Item created:', data);
// You can navigate to the new item
navigate(`/items/${data.id}`);
},
onError: (error) => {
// This will run when the mutation fails
console.error('Failed to create item:', error);
},
onSettled: () => {
// This will run when the mutation completes (success or error)
// Good place to invalidate queries that might be affected
queryClient.invalidateQueries({ queryKey: api.listItems.queryKey() });
}
});
클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

파일 업로드를 허용하는 엔드포인트의 경우 생성된 클라이언트는 요청을 FormData로 전송합니다. FastAPI에서 multipart/form-data 본문을 사용하는 작업을 정의하세요. 예를 들어 UploadFile을 사용:

from fastapi import UploadFile
@app.post("/files")
async def upload_file(file: UploadFile, description: str = "") -> FileMetadata:
contents = await file.read()
...

바이너리 필드는 생성된 클라이언트에서 Blob 타입으로 지정되며, 다른 필드(위의 description 등)는 모델링된 타입을 유지합니다. Blob 또는 File을 전달하세요 — 예를 들어 <input type="file">에서 얻은 것:

import { useMutation } from '@tanstack/react-query';
import { useMyApi } from './hooks/useMyApi';
function UploadForm() {
const api = useMyApi();
const uploadFile = useMutation(api.uploadFile.mutationOptions());
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
uploadFile.mutate({ file, description: file.name });
}
};
return <input type="file" onChange={handleChange} />;
}

클라이언트는 FormData 본문을 빌드하고 fetchContent-Type(multipart boundary 포함)을 자동으로 설정하도록 합니다.

무한 쿼리를 사용한 페이지네이션

섹션 제목: “무한 쿼리를 사용한 페이지네이션”

입력으로 cursor 매개변수를 허용하는 엔드포인트의 경우 생성된 훅은 TanStack Query의 useInfiniteQuery 훅을 사용한 무한 쿼리 지원을 제공합니다. 이를 통해 “더 보기” 또는 무한 스크롤 기능을 쉽게 구현할 수 있습니다.

import { useInfiniteQuery } from '@tanstack/react-query';
import { useMyApi } from './hooks/useMyApi';
function ItemList() {
const api = useMyApi();
const items = useInfiniteQuery({
...api.listItems.infiniteQueryOptions({
limit: 10, // Number of items per page
}, {
// Make sure you define a getNextPageParam function to return
// the parameter that should be passed as the 'cursor' for the
// next page
getNextPageParam: (lastPage) =>
lastPage.nextCursor || undefined
}),
});
if (items.isLoading) {
return <LoadingSpinner />;
}
if (items.isError) {
return <ErrorMessage message={items.error.message} />;
}
return (
<div>
{/* Flatten the pages array to render all items */}
<ul>
{items.data.pages.flatMap(page =>
page.items.map(item => (
<li key={item.id}>{item.name}</li>
))
)}
</ul>
<button
onClick={() => items.fetchNextPage()}
disabled={!items.hasNextPage || items.isFetchingNextPage}
>
{items.isFetchingNextPage
? 'Loading more...'
: items.hasNextPage
? 'Load More'
: 'No more items'}
</button>
</div>
);
}

생성된 훅은 API가 지원하는 경우 커서 기반 페이지네이션을 자동으로 처리합니다. nextCursor 값은 응답에서 추출되어 다음 페이지를 가져오는 데 사용됩니다.

클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

통합에는 타입이 지정된 오류 응답과 함께 내장된 오류 처리가 포함되어 있습니다. OpenAPI 사양에 정의된 가능한 오류 응답을 캡슐화하는 <operation-name>Error 타입이 생성됩니다. 각 오류에는 statuserror 속성이 있으며, status 값을 확인하여 특정 오류 타입으로 좁힐 수 있습니다.

import { useMutation } from '@tanstack/react-query';
function MyComponent() {
const api = useMyApi();
const createItem = useMutation(api.createItem.mutationOptions());
const handleClick = () => {
createItem.mutate({ name: 'New Item' });
};
if (createItem.error) {
switch (createItem.error.status) {
case 400:
// error.error is typed as CreateItem400Response
return (
<div>
<h2>Invalid input:</h2>
<p>{createItem.error.error.message}</p>
<ul>
{createItem.error.error.validationErrors.map((err) => (
<li key={err.field}>{err.message}</li>
))}
</ul>
</div>
);
case 403:
// error.error is typed as CreateItem403Response
return (
<div>
<h2>Not authorized:</h2>
<p>{createItem.error.error.reason}</p>
</div>
);
case 500:
case 502:
// error.error is typed as CreateItem5XXResponse
return (
<div>
<h2>Server error:</h2>
<p>{createItem.error.error.message}</p>
<p>Trace ID: {createItem.error.error.traceId}</p>
</div>
);
}
}
return <button onClick={handleClick}>Create Item</button>;
}
바닐라 클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

FastAPI가 응답을 스트리밍하도록 구성한 경우, useQuery 훅은 스트림의 새 청크가 도착할 때마다 자동으로 데이터를 업데이트합니다.

예시:

function MyStreamingComponent() {
const api = useMyApi();
const stream = useQuery(api.myStream.queryOptions());
return (
<ul>
{(stream.data ?? []).map((chunk) => (
<li>
{chunk.timestamp.toISOString()}: {chunk.message}
</li>
))}
</ul>
);
}

필요한 경우 isLoadingfetchStatus 속성을 사용하여 스트림의 현재 상태를 확인할 수 있습니다. 스트림은 다음 수명 주기를 따릅니다:

  1. 스트리밍을 시작하기 위한 HTTP 요청이 전송됨

    • isLoadingtrue
    • fetchStatus'fetching'
    • dataundefined
  2. 스트림의 첫 번째 청크가 수신됨

    • isLoadingfalse가 됨
    • fetchStatus'fetching'으로 유지
    • data는 첫 번째 청크를 포함하는 배열이 됨
  3. 후속 청크가 수신됨

    • isLoadingfalse로 유지
    • fetchStatus'fetching'으로 유지
    • data는 수신되는 즉시 각 후속 청크로 업데이트됨
  4. 스트림이 완료됨

    • isLoadingfalse로 유지
    • fetchStatus'idle'이 됨
    • data는 수신된 모든 청크의 배열
바닐라 클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

기본적으로 HTTP 메서드 PUT, POST, PATCHDELETE를 사용하는 FastAPI의 작업은 뮤테이션으로 간주되고, 다른 모든 작업은 쿼리로 간주됩니다.

x-queryx-mutation을 사용하여 이 동작을 변경할 수 있습니다.

@app.post(
"/items",
openapi_extra={
"x-query": True
}
)
def list_items():
# ...

생성된 훅은 POST HTTP 메서드를 사용하더라도 queryOptions를 제공합니다:

const items = useQuery(api.listItems.queryOptions());
@app.get(
"/start-processing",
openapi_extra={
"x-mutation": True
}
)
def start_processing():
# ...

생성된 훅은 GET HTTP 메서드를 사용하더라도 mutationOptions를 제공합니다:

// Generated hook will include the custom options
const startProcessing = useMutation(api.startProcessing.mutationOptions());

사용자 정의 페이지네이션 커서

섹션 제목: “사용자 정의 페이지네이션 커서”

기본적으로 생성된 훅은 cursor라는 매개변수를 사용한 커서 기반 페이지네이션을 가정합니다. x-cursor 확장을 사용하여 이 동작을 사용자 정의할 수 있습니다:

@app.get(
"/items",
openapi_extra={
# Specify a different parameter name for the cursor
"x-cursor": "page_token"
}
)
def list_items(page_token: str = None, limit: int = 10):
# ...
return {
"items": items,
"page_token": next_page_token
}

작업에 대해 infiniteQueryOptions를 생성하지 않으려면 x-cursorFalse로 설정할 수 있습니다:

@app.get(
"/items",
openapi_extra={
# Disable cursor-based pagination for this endpoint
"x-cursor": False
}
)
def list_items(page: int = 1, limit: int = 10):
# ...
return {
"items": items,
"total": total_count,
"page": page,
"pages": total_pages
}

생성된 훅과 클라이언트 메서드는 FastAPI 엔드포인트의 OpenAPI 태그를 기반으로 자동으로 구성됩니다. 이를 통해 API 호출을 체계적으로 유지하고 관련 작업을 쉽게 찾을 수 있습니다.

예시:

items.py
@app.get(
"/items",
tags=["items"],
)
def list():
# ...
@app.post(
"/items",
tags=["items"],
)
def create(item: Item):
# ...
users.py
@app.get(
"/users",
tags=["users"],
)
def list():
# ...

생성된 훅은 태그별로 그룹화됩니다:

import { useQuery, useMutation } from '@tanstack/react-query';
import { useMyApi } from './hooks/useMyApi';
function ItemsAndUsers() {
const api = useMyApi();
// Items operations are grouped under api.items
const items = useQuery(api.items.list.queryOptions());
const createItem = useMutation(api.items.create.mutationOptions());
// Users operations are grouped under api.users
const users = useQuery(api.users.list.queryOptions());
// Usage example
const handleCreateItem = () => {
createItem.mutate({ name: 'New Item' });
};
return (
<div>
<h2>Items</h2>
<ul>
{items.data?.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
<button onClick={handleCreateItem}>Add Item</button>
<h2>Users</h2>
<ul>
{users.data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}

이 그룹화를 통해 API 호출을 더 쉽게 구성하고 IDE에서 더 나은 코드 완성을 제공합니다.

클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

사용자 정의 예외 클래스, 예외 핸들러를 정의하고 다양한 오류 상태 코드에 대한 응답 모델을 지정하여 FastAPI에서 오류 응답을 사용자 정의할 수 있습니다. 생성된 클라이언트는 이러한 사용자 정의 오류 타입을 자동으로 처리합니다.

먼저 Pydantic을 사용하여 오류 모델을 정의합니다:

models.py
from pydantic import BaseModel
class ErrorDetails(BaseModel):
message: str
class ValidationError(BaseModel):
message: str
field_errors: list[str]

그런 다음 다양한 오류 시나리오에 대한 사용자 정의 예외 클래스를 생성합니다:

exceptions.py
class NotFoundException(Exception):
def __init__(self, message: str):
self.message = message
class ValidationException(Exception):
def __init__(self, details: ValidationError):
self.details = details

예외를 HTTP 응답으로 변환하기 위해 예외 핸들러를 등록합니다:

main.py
from fastapi import Request
from fastapi.responses import JSONResponse
@app.exception_handler(NotFoundException)
async def not_found_handler(request: Request, exc: NotFoundException):
return JSONResponse(
status_code=404,
content=exc.message,
)
@app.exception_handler(ValidationException)
async def validation_error_handler(request: Request, exc: ValidationException):
return JSONResponse(
status_code=400,
content=exc.details.model_dump(),
)

마지막으로 엔드포인트 정의에서 다양한 오류 상태 코드에 대한 응답 모델을 지정합니다:

main.py
@app.get(
"/items/{item_id}",
responses={
404: {"model": str}
500: {"model": ErrorDetails}
}
)
def get_item(item_id: str) -> Item:
item = find_item(item_id)
if not item:
raise NotFoundException(message=f"Item with ID {item_id} not found")
return item
@app.post(
"/items",
responses={
400: {"model": ValidationError},
403: {"model": str}
}
)
def create_item(item: Item) -> Item:
if not is_valid(item):
raise ValidationException(
ValidationError(
message="Invalid item data",
field_errors=["name is required"]
)
)
return save_item(item)

React에서 사용자 정의 오류 타입 사용

섹션 제목: “React에서 사용자 정의 오류 타입 사용”

생성된 클라이언트는 이러한 사용자 정의 오류 타입을 자동으로 처리하여 다양한 오류 응답을 타입 체크하고 처리할 수 있습니다:

import { useMutation, useQuery } from '@tanstack/react-query';
function ItemComponent() {
const api = useMyApi();
// Query with typed error handling
const getItem = useQuery({
...api.getItem.queryOptions({ itemId: '123' }),
onError: (error) => {
// Error is typed based on the responses in your FastAPI
switch (error.status) {
case 404:
// error.error is a string as specified in the responses
console.error('Not found:', error.error);
break;
case 500:
// error.error is typed as ErrorDetails
console.error('Server error:', error.error.message);
break;
}
}
});
// Mutation with typed error handling
const createItem = useMutation({
...api.createItem.mutationOptions(),
onError: (error) => {
switch (error.status) {
case 400:
// error.error is typed as ValidationError
console.error('Validation error:', error.error.message);
console.error('Field errors:', error.error.field_errors);
break;
case 403:
// error.error is a string as specified in the responses
console.error('Forbidden:', error.error);
break;
}
}
});
// Component rendering with error handling
if (getItem.isError) {
if (getItem.error.status === 404) {
return <NotFoundMessage message={getItem.error.error} />;
} else {
return <ErrorMessage message={getItem.error.error.message} />;
}
}
return (
<div>
{/* Component content */}
</div>
);
}
클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

더 나은 사용자 경험을 위해 항상 로딩 및 오류 상태를 처리하세요:

import { useQuery } from '@tanstack/react-query';
function ItemList() {
const api = useMyApi();
const items = useQuery(api.listItems.queryOptions());
if (items.isLoading) {
return <LoadingSpinner />;
}
if (items.isError) {
const err = items.error;
switch (err.status) {
case 403:
// err.error is typed as ListItems403Response
return <ErrorMessage message={err.error.reason} />;
case 500:
case 502:
// err.error is typed as ListItems5XXResponse
return (
<ErrorMessage
message={err.error.message}
details={`Trace ID: ${err.error.traceId}`}
/>
);
default:
return <ErrorMessage message="An unknown error occurred" />;
}
}
return (
<ul>
{items.data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
바닐라 클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

더 나은 사용자 경험을 위해 낙관적 업데이트를 구현하세요:

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
function ItemList() {
const api = useMyApi();
const queryClient = useQueryClient();
// Query to fetch items
const itemsQuery = useQuery(api.listItems.queryOptions());
// Mutation for deleting items with optimistic updates
const deleteMutation = useMutation({
...api.deleteItem.mutationOptions(),
onMutate: async (itemId) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({ queryKey: api.listItems.queryKey() });
// Snapshot the previous value
const previousItems = queryClient.getQueryData(api.listItems.queryKey());
// Optimistically update to the new value
queryClient.setQueryData(
api.listItems.queryKey(),
(old) => old.filter((item) => item.id !== itemId)
);
// Return a context object with the snapshot
return { previousItems };
},
onError: (err, itemId, context) => {
// If the mutation fails, use the context returned from onMutate to roll back
queryClient.setQueryData(api.listItems.queryKey(), context.previousItems);
console.error('Failed to delete item:', err);
},
onSettled: () => {
// Always refetch after error or success to ensure data is in sync with server
queryClient.invalidateQueries({ queryKey: api.listItems.queryKey() });
},
});
if (itemsQuery.isLoading) {
return <LoadingSpinner />;
}
if (itemsQuery.isError) {
return <ErrorMessage message="Failed to load items" />;
}
return (
<ul>
{itemsQuery.data.map((item) => (
<li key={item.id}>
{item.name}
<button
onClick={() => deleteMutation.mutate(item.id)}
disabled={deleteMutation.isPending}
>
{deleteMutation.isPending ? 'Deleting...' : 'Delete'}
</button>
</li>
))}
</ul>
);
}
바닐라 클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

통합은 완전한 엔드 투 엔드 타입 안전성을 제공합니다. IDE는 모든 API 호출에 대해 완전한 자동 완성 및 타입 체크를 제공합니다:

import { useMutation } from '@tanstack/react-query';
function ItemForm() {
const api = useMyApi();
// Type-safe mutation for creating items
const createItem = useMutation({
...api.createItem.mutationOptions(),
// ✅ Type error if onSuccess callback doesn't handle the correct response type
onSuccess: (data) => {
// data is fully typed based on your API's response schema
console.log(`Item created with ID: ${data.id}`);
},
});
const handleSubmit = (data: CreateItemInput) => {
// ✅ Type error if input doesn't match schema
createItem.mutate(data);
};
// Error UI can use type narrowing to handle different error types
if (createItem.error) {
const error = createItem.error;
switch (error.status) {
case 400:
// error.error is typed as CreateItem400Response
return (
<FormError
message="Invalid input"
errors={error.error.validationErrors}
/>
);
case 403:
// error.error is typed as CreateItem403Response
return <AuthError reason={error.error.reason} />;
default:
// error.error is typed as CreateItem5XXResponse for 500, 502, etc.
return <ServerError message={error.error.message} />;
}
}
return (
<form onSubmit={(e) => {
e.preventDefault();
handleSubmit({ name: 'New Item' });
}}>
{/* Form fields */}
<button
type="submit"
disabled={createItem.isPending}
>
{createItem.isPending ? 'Creating...' : 'Create Item'}
</button>
</form>
);
}
바닐라 클라이언트를 직접 사용하는 예시를 보려면 여기를 클릭하세요.

타입은 FastAPI의 OpenAPI 스키마에서 자동으로 생성되므로 API에 대한 모든 변경 사항이 빌드 후 프론트엔드 코드에 반영됩니다.

FastAPI가 Custom 인증(Lambda Authorizer)을 사용하는 경우, 생성된 클라이언트 provider를 편집하여 authorizer가 예상하는 인증 헤더를 추가해야 합니다. 생성된 <ApiName>Provider.tsx에서 fetch 구성을 찾아 요청 헤더에 토큰 또는 API 키를 추가하세요.