Skip to main content
phase-6-ui-integration Implement frontend UI and integrate with backend APIs — state management and API clients.
Triggers: UI integration, frontend-backend, API client
default: bkit:pipeline-guide
frontend: bkit:frontend-architect
インストールへ移動 Skills Marketplace コミュニティが作成したAIスキルを発見・探索
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/popup-studio-ai/bkit-claude-code --skill phase-6-ui-integrationコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
Zipをダウンロード ダウンロード中... name phase-6-ui-integration classification capability classification-reason Pattern guidance may overlap with model's built-in knowledge as it improves deprecation-risk medium effort medium description Implement frontend UI and integrate with backend APIs — state management and API clients.
Triggers: UI integration, frontend-backend, API client
default: bkit:pipeline-guide
frontend: bkit:frontend-architect
allowed-tools ["Read","Write","Edit","Glob","Grep","Bash"] user-invocable false next-skill phase-7-seo-security pdca-phase do task-template [Phase-6] {feature}
Phase 6: UI Implementation + API Integration
Actual UI implementation and API integration
Purpose
Implement actual screens using design system components and integrate with APIs.
What to Do in This Phase
Page Implementation : Develop each screen
State Management : Handle client state
API Integration : Call backend APIs
Error Handling : Handle loading and error states
Deliverables
src/
├── pages/ # Page components
│ ├── index.tsx
│ ├── login.tsx
│ └── ...
├── features/ # Feature-specific components
│ ├── auth/
│ ├── product/
│ └── ...
└── hooks/ # API call hooks
├── useAuth.ts
└── useProducts.ts
docs/03-analysis/
└── ui-qa.md # QA results
PDCA Application
Plan : Define screens/features to implement
Design : Component structure, state management design
Do : UI implementation + API integration
Check : Zero Script QA
Act : Fix bugs and proceed to Phase 7
Level-wise Application
Level Application Method Starter Static UI only (no API integration) Dynamic Full integration Enterprise Full integration + optimization
API Client Architecture
Why is a Centralized API Client Needed?
Problem (Scattered API Calls) Solution (Centralized Client) Duplicate error handling logic Common error handler Distributed auth token handling Automatic token injection Inconsistent response formats Standardized response types Multiple changes when endpoint changes Single point of management Difficult testing/mocking Easy mock replacement
3-Layer API Client Structure
┌─────────────────────────────────────────────────────────┐
│ UI Components │
│ (pages, features, hooks) │
├─────────────────────────────────────────────────────────┤
│ Service Layer │
│ (Domain-specific API call functions) │
│ authService, productService, orderService, ... │
├─────────────────────────────────────────────────────────┤
│ API Client Layer │
│ (Common settings, interceptors, error handling) │
│ apiClient (axios/fetch wrapper) │
└─────────────────────────────────────────────────────────┘
Folder Structure src/
├── lib/
│ └── api/
│ ├── client.ts # API client (axios/fetch wrapper)
│ ├── interceptors.ts # Request/response interceptors
│ └── error-handler.ts # Error handling logic
├── services/
│ ├── auth.service.ts # Auth-related APIs
│ ├── product.service.ts # Product-related APIs
│ └── order.service.ts # Order-related APIs
├── types/
│ ├── api.types.ts # Common API types
│ ├── auth.types.ts # Auth domain types
│ └── product.types.ts # Product domain types
└── hooks/
├── useAuth.ts # Hooks using Service
└── useProducts.ts
API Client Implementation
1. Basic API Client (lib/api/client.ts)
import { ApiError , ApiResponse } from '@/types/api.types' ;
const BASE_URL = process.env .NEXT_PUBLIC_API_URL || '/api' ;
interface RequestConfig extends RequestInit {
params ?: Record <string , string >;
}
class ApiClient {
private baseUrl : string ;
constructor (baseUrl : string ) {
this .baseUrl = baseUrl;
}
private async request<T>(
endpoint : string ,
config : RequestConfig = {}
): Promise <ApiResponse <T>> {
const { params, ...init } = config;
const url = new URL (`${this .baseUrl} ${endpoint} ` );
if (params) {
Object .entries (params).forEach (([key, value] ) => {
url.searchParams .append (key, value);
});
}
const headers = new Headers (init.headers );
if (!headers.has ('Content-Type' )) {
headers.set ('Content-Type' , 'application/json' );
}
const token = this .getAuthToken ();
if (token) {
headers.set ('Authorization' , `Bearer ${token} ` );
}
try {
const response = await fetch (url.toString (), {
...init,
headers,
});
return this .handleResponse <T>(response);
} catch (error) {
throw this .handleNetworkError (error);
}
}
private async handleResponse<T>(response : Response ): Promise <ApiResponse <T>> {
const data = await response.json ();
if (!response.ok ) {
throw new ApiError (
data.error ?.code || 'UNKNOWN_ERROR' ,
data.error ?.message || 'An error occurred' ,
response.status ,
data.error ?.details
);
}
return data as ApiResponse <T>;
}
private handleNetworkError (error : unknown ): ApiError {
if (error instanceof TypeError && error.message === 'Failed to fetch' ) {
return new ApiError ('NETWORK_ERROR' , 'Please check your network connection.' , 0 );
}
return new ApiError ('UNKNOWN_ERROR' , 'An unknown error occurred.' , 0 );
}
private getAuthToken (): string | null {
if (typeof window === 'undefined' ) return null ;
return localStorage .getItem ('auth_token' );
}
get<T>(endpoint : string , params ?: Record <string , string >) {
return this .request <T>(endpoint, { method : 'GET' , params });
}
post<T>(endpoint : string , body ?: unknown ) {
return this .request <T>(endpoint, {
method : 'POST' ,
body : JSON .stringify (body),
});
}
put<T>(endpoint : string , body ?: unknown ) {
return this .request <T>(endpoint, {
method : 'PUT' ,
body : JSON .stringify (body),
});
}
patch<T>(endpoint : string , body ?: unknown ) {
return this .request <T>(endpoint, {
method : 'PATCH' ,
body : JSON .stringify (body),
});
}
delete <T>(endpoint : string ) {
return this .request <T>(endpoint, { method : 'DELETE' });
}
}
export const apiClient = new ApiClient (BASE_URL );
2. Common Type Definitions (types/api.types.ts)
export interface ApiResponse <T> {
data : T;
meta ?: {
timestamp : string ;
requestId ?: string ;
};
}
export interface PaginatedResponse <T> extends ApiResponse <T[]> {
pagination : {
page : number ;
limit : number ;
total : number ;
totalPages : number ;
};
}
export interface ApiErrorResponse {
error : {
code : string ;
message : string ;
details ?: Array <{
field : string ;
message : string ;
}>;
};
}
export class ApiError extends Error {
constructor (
public code : string ,
message : string ,
public status : number ,
public details ?: Array <{ field: string ; message: string }>
) {
super (message);
this .name = 'ApiError' ;
}
isValidationError (): boolean {
return this .code === 'VALIDATION_ERROR' && !!this .details ;
}
isAuthError (): boolean {
return this .status === 401 || this .code === 'UNAUTHORIZED' ;
}
isForbiddenError (): boolean {
return this .status === 403 || this .code === 'FORBIDDEN' ;
}
isNotFoundError (): boolean {
return this .status === 404 || this .code === 'NOT_FOUND' ;
}
}
export const ERROR_CODES = {
VALIDATION_ERROR : 'VALIDATION_ERROR' ,
UNAUTHORIZED : 'UNAUTHORIZED' ,
FORBIDDEN : 'FORBIDDEN' ,
NOT_FOUND : 'NOT_FOUND' ,
CONFLICT : 'CONFLICT' ,
INTERNAL_ERROR : 'INTERNAL_ERROR' ,
SERVICE_UNAVAILABLE : 'SERVICE_UNAVAILABLE' ,
NETWORK_ERROR : 'NETWORK_ERROR' ,
TIMEOUT_ERROR : 'TIMEOUT_ERROR' ,
} as const ;
export type ErrorCode = typeof ERROR_CODES [keyof typeof ERROR_CODES ];
Service Layer Pattern
Domain-specific Service Separation
import { apiClient } from '@/lib/api/client' ;
import { User , LoginRequest , LoginResponse , SignupRequest } from '@/types/auth.types' ;
export const authService = {
login (credentials : LoginRequest ) {
return apiClient.post <LoginResponse >('/auth/login' , credentials);
},
signup (data : SignupRequest ) {
return apiClient.post <User >('/auth/signup' , data);
},
logout ( ) {
return apiClient.post <void >('/auth/logout' );
},
getMe ( ) {
return apiClient.get <User >('/auth/me' );
},
refreshToken ( ) {
return apiClient.post <LoginResponse >('/auth/refresh' );
},
};
import { apiClient } from '@/lib/api/client' ;
import { Product , ProductFilter , CreateProductRequest } from '@/types/product.types' ;
import { PaginatedResponse } from '@/types/api.types' ;
export const productService = {
getList (filter ?: ProductFilter ) {
const params = filter ? {
page : String (filter.page || 1 ),
limit : String (filter.limit || 20 ),
...(filter.category && { category : filter.category }),
...(filter.search && { search : filter.search }),
} : undefined ;
return apiClient.get <PaginatedResponse <Product >>('/products' , params);
},
getById (id : string ) {
return apiClient.get <Product >(`/products/${id} ` );
},
create (data : CreateProductRequest ) {
return apiClient.post <Product >('/products' , data);
},
update (id : string , data : Partial <CreateProductRequest > ) {
return apiClient.patch <Product >(`/products/${id} ` , data);
},
delete (id : string ) {
return apiClient.delete <void >(`/products/${id} ` );
},
};
Error Handling Pattern
Global Error Handler
import { ApiError , ERROR_CODES } from '@/types/api.types' ;
import { toast } from 'sonner' ;
interface ErrorHandlerOptions {
showToast ?: boolean ;
redirectOnAuth ?: boolean ;
customMessages ?: Record <string , string >;
}
export function handleApiError (
error : unknown ,
options : ErrorHandlerOptions = {}
): void {
const { showToast = true , redirectOnAuth = true , customMessages = {} } = options;
if (!(error instanceof ApiError )) {
console .error ('Unexpected error:' , error);
if (showToast) {
toast.error ('An unknown error occurred.' );
}
return ;
}
const message = customMessages[error.code ] || error.message ;
switch (error.code ) {
case ERROR_CODES .UNAUTHORIZED :
if (redirectOnAuth && typeof window !== 'undefined' ) {
localStorage .removeItem ('auth_token' );
window .location .href = '/login' ;
}
break ;
case ERROR_CODES .FORBIDDEN :
if (showToast) toast.error ('You do not have permission.' );
break ;
case ERROR_CODES .NOT_FOUND :
if (showToast) toast.error ('The requested resource was not found.' );
break ;
case ERROR_CODES .VALIDATION_ERROR :
break ;
case ERROR_CODES .NETWORK_ERROR :
if (showToast) toast.error ('Please check your network connection.' );
break ;
default :
if (showToast) toast.error (message);
}
if (process.env .NODE_ENV === 'development' ) {
console .error (`[API Error] ${error.code} :` , {
message : error.message ,
status : error.status ,
details : error.details ,
});
}
}
Error Handling in Hooks
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' ;
import { productService } from '@/services/product.service' ;
import { handleApiError } from '@/lib/api/error-handler' ;
import { ProductFilter } from '@/types/product.types' ;
export function useProducts (filter ?: ProductFilter ) {
return useQuery ({
queryKey : ['products' , filter],
queryFn : () => productService.getList (filter),
throwOnError : false ,
meta : {
errorHandler : (error : unknown ) => handleApiError (error),
},
});
}
export function useCreateProduct ( ) {
const queryClient = useQueryClient ();
return useMutation ({
mutationFn : productService.create ,
onSuccess : () => {
queryClient.invalidateQueries ({ queryKey : ['products' ] });
},
onError : (error ) => {
handleApiError (error, {
customMessages : {
CONFLICT : 'Product name already exists.' ,
},
});
},
});
}
Client-Server Type Sharing
Methods for Type Consistency Method 1: Shared Package (Monorepo)
├── packages/
│ └── shared-types/ # Common types
│ ├── api.types.ts
│ ├── auth.types.ts
│ └── product.types.ts
├── apps/
│ ├── web/ # Frontend
│ └── api/ # Backend
Method 2: Auto-generate Types from API Spec
├── openapi.yaml # OpenAPI spec
└── scripts/
└── generate-types.ts # Type auto-generation script
Method 3: tRPC / GraphQL CodeGen
└── Auto-infer types from schema
Type Definition Example
export interface User {
id : string ;
email : string ;
name : string ;
role : 'user' | 'admin' ;
createdAt : string ;
updatedAt : string ;
}
export interface LoginRequest {
email : string ;
password : string ;
}
export interface LoginResponse {
user : User ;
token : string ;
expiresAt : string ;
}
export interface SignupRequest {
email : string ;
password : string ;
name : string ;
termsAgreed : boolean ;
}
API Integration Patterns
Basic Pattern (fetch) async function getProducts ( ) {
const response = await fetch ('/api/products' );
if (!response.ok ) throw new Error ('Failed to fetch' );
return response.json ();
}
React Query Pattern function useProducts ( ) {
return useQuery ({
queryKey : ['products' ],
queryFn : getProducts,
});
}
SWR Pattern function useProducts ( ) {
return useSWR ('/api/products' , fetcher);
}
State Management Guide Server state (API data) → React Query / SWR
Client state (UI state) → useState / useReducer
Global state (auth, etc.) → Context / Zustand
Form state → React Hook Form
Zero Script QA Application Validate UI behavior with logs:
[UI] Login button clicked
[STATE] isLoading: true
[API] POST /api/auth/login
[RESPONSE] { token: "...", user: {...} }
[STATE] isLoading: false, isLoggedIn: true
[NAVIGATE] → /dashboard
[RESULT] ✅ Login successful
API Integration Checklist
Architecture
Error Handling
Coding Conventions
Template See templates/pipeline/phase-6-ui.template.md
Next Phase Phase 7: SEO/Security → Features are complete, now optimize and strengthen security