实战项目中的GraphQL应用指南
立即解锁
发布时间: 2025-09-04 01:47:17 阅读量: 8 订阅数: 36 AIGC 


React 18设计模式精要
### 实战项目中的 GraphQL 应用指南
在现代 Web 开发中,GraphQL 作为一种强大的数据查询语言,正逐渐成为构建高效、灵活 API 的首选方案。本文将详细介绍如何在一个实际项目中应用 GraphQL,包括前端和后端的配置、用户认证、路由管理等方面,帮助你构建一个完整的登录系统。
#### 1. 项目初始化与基础配置
在开始项目之前,我们需要进行一些基础配置,包括 Webpack 类型定义、HTML 文件创建、TypeScript 配置以及 Express 服务器的搭建。
##### 1.1 Webpack 类型定义
首先,我们需要创建 Webpack 类型文件 `/frontend/webpack/webpack.types.ts`,定义 Webpack 所需的 TypeScript 类型:
```typescript
export type WebpackMode = 'production' | 'development'
export type ConfigArgs = {
mode: WebpackMode
presets: string[]
}
```
##### 1.2 HTML 文件创建
接着,创建初始的 HTML 文件 `/frontend/src/client/index.html`,该文件将由 HtmlWebpackPlugin 处理:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
```
##### 1.3 TypeScript 配置
TypeScript 能够提前识别代码中的错误,在大型项目中尤为有用。我们的 `tsconfig.json` 文件配置如下:
```json
{
"compilerOptions": {
"sourceMap": true,
"target": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "commonjs",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"noImplicitAny": false,
"paths": {
"~/*": ["./src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules", "**/*.test.tsx"]
}
```
##### 1.4 Express 服务器配置
我们的应用需要一个 Express 服务器来进行验证和会话管理。以下是服务器代码 `/frontend/src/server.ts`:
```typescript
import cookieParser from 'cookie-parser'
import cors from 'cors'
import express, { Application, Request, Response } from 'express'
import { resolve } from 'path'
import * as config from '../config'
import html from './html'
import { isConnected } from './lib/middlewares/user'
// Express application
const app: Application = express()
const distDir = resolve('dist')
const staticDir = resolve('src', 'static')
// Middlewares
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(cookieParser(config.security.secretKey))
app.use(cors({ credentials: true, origin: true }))
// Static directories
app.use(express.static(distDir))
app.use(express.static(staticDir))
// Routes
app.get('/login', isConnected(false), (req: Request, res: Response) => {
res.send(html({ title: 'My Website' }))
})
app.get(`/logout`, (req: Request, res: Response) => {
const redirect: any = req.query.redirectTo || '/'
res.clearCookie('at')
res.redirect(redirect)
})
app.get('*', (req: Request, res: Response) => {
res.send(html({ title: 'My Website' }))
})
export default app
```
我们的网站有四个主要路由:
- `/`:主页(由 React 处理)
- `/dashboard`:仪表盘,受保护,仅允许具有 god 或 admin 权限的用户访问(先由 Express 处理,再由 React 处理)
- `/login`:登录页(由 React 处理)
- `/logout`:删除现有会话(由 Express 处理)
#### 2. 前端配置
接下来,我们需要创建前端配置文件 `/frontend/src/config.ts`,用于管理 GraphQL 端口、服务器以及安全配置:
```typescript
// Types
type API = {
uri: string
}
type Security = {
secretKey: string
expiresIn: string
}
// Environment Configuration
export const isProduction: boolean = process.env.NODE_ENV === 'production'
export const isDevelopment: boolean = process.env.NODE_ENV !== 'production'
// Server Configuration
const devUrl = 'localhost'
const prodUrl = 'localhost' // change this to your production url
export const PORT: number = Number(process.env.PORT) || 3000
export const DEV_SERVER_PORT = 3001
export const GRAPHQL_PORT = 4000
export const GRAPHQL_SERVER = isDevelopment ? devUrl : prodUrl
// Paths Configuration
export const domain: string = devUrl
export const baseUrl: string = isProduction
? `https://${domain}:${PORT}`
: `http://${domain}:${PORT}` // Remove port in actual production
export const publicPath: string = isProduction
? ``
: `http://${domain}:${DEV_SERVER_PORT}/`
// API Configuration
export const api: API = {
uri: `http://${GRAPHQL_SERVER}:${GRAPHQL_PORT}/graphql`
}
// Security Configuration
export const security: Security = {
secretKey: process.env.SECURITY_SECRET_KEY || '',
expiresIn: '7d'
}
```
#### 3. 用户中间件与 JWT 函数
为了验证用户是否已连接并具有正确的权限,我们需要创建用户中间件和 JWT 函数。
##### 3.1 用户中间件
创建文件 `/frontend/src/server/lib/middlewares/user.ts`:
```typescript
import { NextFunction, Request, Response } from 'express'
import { getUserData } from '../jwt'
export const isConnected = (isLogged = true, roles = ['user'], redirectTo = '/') =>
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const user = await getUserData(req.cookies.at)
if (!user && !isLogged) {
return next()
}
if (user && isLogged) {
if (roles.includes('god') && user.role === 'god') {
return next()
}
if (roles.includes('admin') && user.role === 'admin') {
return next()
}
if (roles.includes('user') && user.role === 'user') {
return next()
}
res.redirect(redirectTo)
} else {
res.redirect(redirectTo)
}
}
```
这个中间件可以控制是否验证用户是否已连接,验证特定角色,并在用户未连接或角色不正确时重定向用户。
##### 3.2 JWT 函数
创建文件 `/frontend/src/server/lib/jwt.ts`:
```typescript
import { getBase64 } from '@contentpi/lib'
import jwt from 'jsonwebtoken'
import * as config from '~/config'
const { security: { secretKey } } = config
export function jwtVerify(accessToken: string, cb: any) {
jwt.verify(accessToken, secretKey, (error: any, accessTokenData: any = {}) => {
const { data: user } = accessTokenData
if (error || !user) {
return cb(null)
}
const userData = getBase64(user)
return cb(userData)
})
}
export async function getUserData(accessToken: string): Promise<any> {
const UserPromise = new Promise(
(resolve) => jwtVerify(accessToken, (user: any) => resolve(user))
)
const user = await UserPromise
return user
}
```
`getUserData` 函数将使用从 cookie 中获取的访问令牌检索用户数据。
#### 4. GraphQL 查询与突变
我们已经在后端项目中创建了所需的查询和突变,现在需要在前端项目中创建执行它们的文件。
##### 4.1 getUser 查询
0
0
复制全文
相关推荐










