API集成 #
一、REST API 集成 #
1.1 基本 REST API 调用 #
astro
---
// src/pages/users.astro
interface User {
id: number;
name: string;
email: string;
username: string;
}
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const users: User[] = await response.json();
---
<div class="users">
{users.map(user => (
<div key={user.id} class="user-card">
<h3>{user.name}</h3>
<p>@{user.username}</p>
<p>{user.email}</p>
</div>
))}
</div>
1.2 封装 API 客户端 #
typescript
// src/lib/api-client.ts
interface RequestOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
headers?: Record<string, string>;
body?: any;
timeout?: number;
}
class ApiClient {
private baseUrl: string;
private defaultHeaders: Record<string, string>;
constructor(baseUrl: string, defaultHeaders: Record<string, string> = {}) {
this.baseUrl = baseUrl;
this.defaultHeaders = {
'Content-Type': 'application/json',
...defaultHeaders,
};
}
async request<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
const { method = 'GET', headers = {}, body, timeout = 10000 } = options;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method,
headers: { ...this.defaultHeaders, ...headers },
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} finally {
clearTimeout(timeoutId);
}
}
get<T>(endpoint: string, options?: RequestOptions): Promise<T> {
return this.request<T>(endpoint, { ...options, method: 'GET' });
}
post<T>(endpoint: string, body: any, options?: RequestOptions): Promise<T> {
return this.request<T>(endpoint, { ...options, method: 'POST', body });
}
put<T>(endpoint: string, body: any, options?: RequestOptions): Promise<T> {
return this.request<T>(endpoint, { ...options, method: 'PUT', body });
}
delete<T>(endpoint: string, options?: RequestOptions): Promise<T> {
return this.request<T>(endpoint, { ...options, method: 'DELETE' });
}
}
export const api = new ApiClient(
import.meta.env.API_URL || 'https://api.example.com',
{
'Authorization': `Bearer ${import.meta.env.API_KEY}`,
}
);
1.3 使用 API 客户端 #
astro
---
// src/pages/posts.astro
import { api } from '../lib/api-client';
interface Post {
id: number;
title: string;
body: string;
}
const posts = await api.get<Post[]>('/posts');
---
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</article>
))}
</div>
二、GraphQL 集成 #
2.1 GraphQL 客户端 #
typescript
// src/lib/graphql-client.ts
interface GraphQLResponse<T> {
data: T;
errors?: Array<{ message: string }>;
}
class GraphQLClient {
private endpoint: string;
private headers: Record<string, string>;
constructor(endpoint: string, headers: Record<string, string> = {}) {
this.endpoint = endpoint;
this.headers = headers;
}
async query<T>(query: string, variables?: Record<string, any>): Promise<T> {
const response = await fetch(this.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...this.headers,
},
body: JSON.stringify({ query, variables }),
});
const result: GraphQLResponse<T> = await response.json();
if (result.errors && result.errors.length > 0) {
throw new Error(result.errors[0].message);
}
return result.data;
}
}
export const graphql = new GraphQLClient(
import.meta.env.GRAPHQL_URL || 'https://api.example.com/graphql',
{
'Authorization': `Bearer ${import.meta.env.API_KEY}`,
}
);
2.2 使用 GraphQL #
astro
---
// src/pages/blog.astro
import { graphql } from '../lib/graphql-client';
interface BlogData {
posts: Array<{
id: string;
title: string;
excerpt: string;
author: {
name: string;
};
}>;
}
const data = await graphql.query<BlogData>(`
query GetPosts {
posts {
id
title
excerpt
author {
name
}
}
}
`);
---
<div>
{data.posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p class="author">作者: {post.author.name}</p>
<p>{post.excerpt}</p>
</article>
))}
</div>
2.3 带变量的查询 #
astro
---
// src/pages/blog/[slug].astro
import { graphql } from '../../lib/graphql-client';
interface PostData {
post: {
id: string;
title: string;
content: string;
author: { name: string };
};
}
const { slug } = Astro.params;
const data = await graphql.query<PostData>(`
query GetPost($slug: String!) {
post(slug: $slug) {
id
title
content
author {
name
}
}
}
`, { slug });
const { post } = data;
---
<article>
<h1>{post.title}</h1>
<p>作者: {post.author.name}</p>
<div set:html={post.content} />
</article>
三、CMS 集成 #
3.1 Contentful 集成 #
typescript
// src/lib/contentful.ts
interface ContentfulConfig {
space: string;
accessToken: string;
}
class ContentfulClient {
private config: ContentfulConfig;
constructor(config: ContentfulConfig) {
this.config = config;
}
async getEntries<T>(contentType: string, options: Record<string, any> = {}) {
const params = new URLSearchParams({
access_token: this.config.accessToken,
content_type: contentType,
...options,
});
const response = await fetch(
`https://cdn.contentful.com/spaces/${this.config.space}/entries?${params}`
);
const data = await response.json();
return data.items as Array<{ fields: T; sys: { id: string } }>;
}
async getEntry<T>(id: string) {
const response = await fetch(
`https://cdn.contentful.com/spaces/${this.config.space}/entries/${id}?access_token=${this.config.accessToken}`
);
const data = await response.json();
return data.fields as T;
}
}
export const contentful = new ContentfulClient({
space: import.meta.env.CONTENTFUL_SPACE,
accessToken: import.meta.env.CONTENTFUL_TOKEN,
});
3.2 Strapi 集成 #
typescript
// src/lib/strapi.ts
class StrapiClient {
private baseUrl: string;
private token: string;
constructor(baseUrl: string, token: string) {
this.baseUrl = baseUrl;
this.token = token;
}
async get<T>(endpoint: string, params: Record<string, any> = {}) {
const url = new URL(`${this.baseUrl}/api${endpoint}`);
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
const response = await fetch(url.toString(), {
headers: {
'Authorization': `Bearer ${this.token}`,
},
});
const data = await response.json();
return data.data as T;
}
}
export const strapi = new StrapiClient(
import.meta.env.STRAPI_URL || 'https://api.strapi.com',
import.meta.env.STRAPI_TOKEN
);
3.3 使用 CMS 数据 #
astro
---
// src/pages/blog/index.astro
import { contentful } from '../../lib/contentful';
interface BlogPost {
title: string;
slug: string;
excerpt: string;
publishedAt: string;
featuredImage: {
fields: {
file: {
url: string;
};
};
};
}
const posts = await contentful.getEntries<BlogPost>('blogPost', {
order: '-fields.publishedAt',
limit: 10,
});
---
<div class="blog-posts">
{posts.map(({ fields, sys }) => (
<article key={sys.id} class="post">
<img
src={`https:${fields.featuredImage.fields.file.url}`}
alt={fields.title}
/>
<h2>{fields.title}</h2>
<p>{fields.excerpt}</p>
<a href={`/blog/${fields.slug}`}>阅读更多</a>
</article>
))}
</div>
四、第三方服务集成 #
4.1 GitHub API #
astro
---
// src/pages/projects.astro
interface GitHubRepo {
id: number;
name: string;
description: string;
html_url: string;
stargazers_count: number;
forks_count: number;
language: string;
}
const response = await fetch('https://api.github.com/users/username/repos', {
headers: {
'Authorization': `token ${import.meta.env.GITHUB_TOKEN}`,
'Accept': 'application/vnd.github.v3+json',
},
});
const repos: GitHubRepo[] = await response.json();
---
<div class="projects">
{repos.map(repo => (
<div key={repo.id} class="project-card">
<h3>
<a href={repo.html_url} target="_blank" rel="noopener">
{repo.name}
</a>
</h3>
<p>{repo.description}</p>
<div class="stats">
<span>⭐ {repo.stargazers_count}</span>
<span>🍴 {repo.forks_count}</span>
{repo.language && <span>{repo.language}</span>}
</div>
</div>
))}
</div>
4.2 Twitter API #
typescript
// src/lib/twitter.ts
interface Tweet {
id: string;
text: string;
created_at: string;
public_metrics: {
like_count: number;
retweet_count: number;
};
}
async function getTweets(userId: string, maxResults = 10): Promise<Tweet[]> {
const response = await fetch(
`https://api.twitter.com/2/users/${userId}/tweets?max_results=${maxResults}&tweet.fields=created_at,public_metrics`,
{
headers: {
'Authorization': `Bearer ${import.meta.env.TWITTER_BEARER_TOKEN}`,
},
}
);
const data = await response.json();
return data.data;
}
export { getTweets };
4.3 天气 API #
astro
---
// src/pages/weather.astro
interface WeatherData {
name: string;
main: {
temp: number;
humidity: number;
};
weather: Array<{
description: string;
icon: string;
}>;
}
const city = 'Beijing';
const apiKey = import.meta.env.OPENWEATHER_API_KEY;
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric&lang=zh_cn`
);
const weather: WeatherData = await response.json();
---
<div class="weather">
<h2>{weather.name}</h2>
<img
src={`https://openweathermap.org/img/wn/${weather.weather[0].icon}@2x.png`}
alt={weather.weather[0].description}
/>
<p class="temp">{Math.round(weather.main.temp)}°C</p>
<p>{weather.weather[0].description}</p>
<p>湿度: {weather.main.humidity}%</p>
</div>
五、认证 API #
5.1 OAuth 集成 #
astro
---
// src/pages/auth/callback.astro
const code = Astro.url.searchParams.get('code');
if (!code) {
return Astro.redirect('/login');
}
const tokenResponse = await fetch('https://oauth-provider.com/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: import.meta.env.OAUTH_CLIENT_ID,
client_secret: import.meta.env.OAUTH_CLIENT_SECRET,
redirect_uri: `${Astro.site}auth/callback`,
}),
});
const { access_token } = await tokenResponse.json();
const userResponse = await fetch('https://oauth-provider.com/user', {
headers: {
'Authorization': `Bearer ${access_token}`,
},
});
const user = await userResponse.json();
---
<h1>欢迎, {user.name}!</h1>
5.2 JWT 认证 #
typescript
// src/lib/auth.ts
interface User {
id: string;
email: string;
name: string;
}
async function verifyToken(token: string): Promise<User | null> {
try {
const response = await fetch(`${import.meta.env.API_URL}/auth/verify`, {
headers: {
'Authorization': `Bearer ${token}`,
},
});
if (!response.ok) return null;
return await response.json();
} catch {
return null;
}
}
export { verifyToken };
六、Webhook 集成 #
6.1 接收 Webhook #
astro
---
// src/pages/api/webhook.astro
if (Astro.request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
const body = await Astro.request.json();
const signature = Astro.request.headers.get('x-webhook-signature');
const expectedSignature = createHmac('sha256', import.meta.env.WEBHOOK_SECRET)
.update(JSON.stringify(body))
.digest('hex');
if (signature !== expectedSignature) {
return new Response('Invalid signature', { status: 401 });
}
console.log('Webhook received:', body);
return new Response(JSON.stringify({ success: true }), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
---
七、错误处理与重试 #
7.1 重试机制 #
typescript
// src/lib/retry.ts
interface RetryOptions {
maxRetries?: number;
delay?: number;
backoff?: boolean;
}
async function fetchWithRetry<T>(
url: string,
options: RequestInit = {},
retryOptions: RetryOptions = {}
): Promise<T> {
const { maxRetries = 3, delay = 1000, backoff = true } = retryOptions;
let lastError: Error | null = null;
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
lastError = error as Error;
if (i < maxRetries - 1) {
const waitTime = backoff ? delay * Math.pow(2, i) : delay;
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
}
throw lastError;
}
export { fetchWithRetry };
7.2 使用重试 #
astro
---
import { fetchWithRetry } from '../lib/retry';
const posts = await fetchWithRetry<Post[]>(
'https://api.example.com/posts',
{},
{ maxRetries: 3, delay: 1000 }
);
---
<div>
{posts.map(post => (
<article key={post.id}>{post.title}</article>
))}
</div>
八、总结 #
API 集成核心要点:
text
┌─────────────────────────────────────────────────────┐
│ API 集成要点 │
├─────────────────────────────────────────────────────┤
│ │
│ 🌐 REST API 封装客户端,统一管理请求 │
│ │
│ 📊 GraphQL 类型安全的查询语言 │
│ │
│ 📝 CMS Contentful、Strapi 等内容管理 │
│ │
│ 🔌 第三方 GitHub、Twitter 等服务集成 │
│ │
│ 🔐 认证 OAuth、JWT 认证方案 │
│ │
│ 🔄 重试 错误处理与自动重试 │
│ │
└─────────────────────────────────────────────────────┘
下一步,让我们学习 静态数据,了解本地数据的管理方法!
最后更新:2026-03-28