从零到部署:GraphQL全栈Airbnb克隆项目实战指南

从零到部署:GraphQL全栈Airbnb克隆项目实战指南

【免费下载链接】fullstack-graphql-airbnb-clone A Fullstack GraphQL Airbnb Clone with React and React Native 【免费下载链接】fullstack-graphql-airbnb-clone 项目地址: https://gitcode.com/gh_mirrors/fu/fullstack-graphql-airbnb-clone

你还在为全栈项目架构烦恼?30分钟掌握企业级GraphQL应用开发

你是否曾面临这些挑战:前后端数据交互复杂、多端应用状态同步困难、API版本管理混乱?本文将带你从零构建一个功能完整的全栈式GraphQL Airbnb克隆项目,融合React、React Native、TypeScript和PostgreSQL等主流技术,掌握现代全栈开发的核心方法论。

读完本文你将获得:

  • 多包管理项目架构设计能力
  • GraphQL API设计与实现技巧
  • TypeORM数据库模型设计实践
  • JWT认证与权限控制方案
  • React/React Native代码复用策略
  • 全栈应用部署最佳实践

项目架构概览

本项目采用Monorepo架构,通过Yarn Workspaces管理5个功能包,实现代码最大化复用:

mermaid

技术栈选型对比

技术领域选型优势
后端框架GraphQL Yoga高性能、支持订阅、开箱即用
ORMTypeORMTypeScript优先、多数据库支持
数据库PostgreSQL强大的关系型数据库、JSON支持
缓存Redis会话管理、发布订阅系统
前端框架React组件化、虚拟DOM、生态丰富
移动端React Native代码复用、原生体验
类型系统TypeScript静态类型检查、提升可维护性

环境准备与项目初始化

系统依赖清单

# 必装依赖
sudo apt-get install postgresql redis-server nodejs yarn

# 验证安装
node -v  # v14+
yarn -v  # v1.22+
psql --version  # 12+
redis-server --version  # 6+

项目克隆与安装

# 获取项目代码
git clone https://gitcode.com/gh_mirrors/fu/fullstack-graphql-airbnb-clone.git
cd fullstack-graphql-airbnb-clone

# 安装依赖(会自动安装所有子包依赖)
yarn

# 构建共享包
yarn build:common
yarn build:controller

数据库设计与实现

核心数据模型

mermaid

数据库配置(ormconfig.json)

{
  "name": "development",
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "postgres",
  "password": "postgres",
  "database": "graphql-ts-server-boilerplate",
  "synchronize": true,
  "logging": true,
  "entities": ["src/entity/**/*.ts"]
}

初始化数据库

# 进入PostgreSQL终端
sudo -u postgres psql

# 在PostgreSQL终端执行
CREATE DATABASE graphql-ts-server-boilerplate;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
\q  # 退出终端

# 启动Redis服务
redis-server --daemonize yes

后端GraphQL服务实现

项目结构(server包)

src/
├── entity/           # 数据库实体
├── modules/          # GraphQL模块
│   ├── user/         # 用户相关操作
│   ├── listing/      # 房源相关操作
│   └── message/      # 消息相关操作
├── index.ts          # 入口文件
└── startServer.ts    # 服务器配置

启动服务器

# 进入server包目录
cd packages/server

# 创建环境变量文件
echo "FRONTEND_HOST=http://localhost:3000" > .env

# 启动开发服务器
yarn start

用户注册 resolver 实现

// src/modules/user/register/resolvers.ts
import { ResolverMap } from "../../../types/graphql-utils";
import { User } from "../../../entity/User";
import { formatYupError } from "../../../utils/formatYupError";
import { duplicateEmail } from "./errorMessages";
import { createConfirmEmailLink } from "./createConfirmEmailLink";
import { sendEmail } from "../../../utils/sendEmail";

export const resolvers: ResolverMap = {
  Mutation: {
    register: async (_, args: GQL.IRegisterOnMutationArguments, { redis, url }) => {
      try {
        await validUserSchema.validate(args, { abortEarly: false });
      } catch (err) {
        return formatYupError(err);
      }

      const { email, password } = args;

      const userAlreadyExists = await User.findOne({
        where: { email },
        select: ["id"]
      });

      if (userAlreadyExists) {
        return [
          {
            path: "email",
            message: duplicateEmail
          }
        ];
      }

      const user = User.create({ email, password });
      await user.save();

      if (process.env.NODE_ENV !== "test") {
        await sendEmail(
          email,
          await createConfirmEmailLink(url, user.id, redis),
          "confirm email"
        );
      }

      return null;
    }
  }
};

房源创建 GraphQL Schema

# src/modules/listing/create/schema.graphql
scalar Upload

input CreateListingInput {
  name: String!
  picture: Upload
  category: String!
  description: String!
  price: Int!
  beds: Int!
  guests: Int!
  latitude: Float!
  longitude: Float!
  amenities: [String!]!
}

type Mutation {
  createListing(input: CreateListingInput!): Boolean!
}

前端React应用开发

Apollo客户端配置

// web/src/apollo.ts
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { createUploadLink } from "apollo-upload-client";
import { split } from "apollo-link";
import { WebSocketLink } from "apollo-link-ws";
import { getMainDefinition } from "apollo-utilities";

const httpLink = createUploadLink({
  uri: process.env.REACT_APP_SERVER_URL || "http://localhost:4000",
  credentials: "include"
});

const wsLink = new WebSocketLink({
  uri: process.env.REACT_APP_SERVER_WS_URL || "ws://localhost:4000",
  options: { reconnect: true }
});

const link = split(
  ({ query }) => {
    const { kind, operation } = getMainDefinition(query) as any;
    return kind === "OperationDefinition" && operation === "subscription";
  },
  wsLink,
  httpLink
);

export const client = new ApolloClient({ link, cache: new InMemoryCache() });

登录组件实现

// web/src/modules/login/LoginConnector.tsx
import * as React from "react";
import { LoginController } from "@abb/controller";
import { RouteComponentProps } from "react-router-dom";
import { LoginView } from "./ui/LoginView";

export class LoginConnector extends React.PureComponent<RouteComponentProps<{}>> {
  onFinish = () => {
    const { history, location: { state } } = this.props;
    if (state && state.next) {
      return history.push(state.next);
    }
    history.push("/");
  };

  render() {
    return (
      <LoginController>
        {({ submit }) => <LoginView onFinish={this.onFinish} submit={submit} />}
      </LoginController>
    );
  }
}

启动Web应用

# 进入web包目录
cd packages/web

# 启动开发服务器
yarn start
# 访问 http://localhost:3000

移动端React Native应用

项目配置

# 进入app包目录
cd packages/app

# 安装依赖
yarn

# 启动开发服务器
yarn start

核心功能模块

移动端与Web端共享大部分业务逻辑和UI组件,通过@abb/controller包实现跨端复用:

// app/src/modules/login/LoginConnector.tsx
import * as React from "react";
import { LoginController } from "@abb/controller";
import { NavigationScreenProps } from "react-navigation";
import { LoginView } from "./ui/LoginView";

export class LoginConnector extends React.PureComponent<NavigationScreenProps<{}>> {
  onFinish = () => {
    this.props.navigation.navigate("Home");
  };

  render() {
    return (
      <LoginController>
        {({ submit, isLoading }) => (
          <LoginView 
            onFinish={this.onFinish} 
            submit={submit} 
            isLoading={isLoading} 
          />
        )}
      </LoginController>
    );
  }
}

核心功能实现详解

房源创建流程

mermaid

房源实体定义:

// server/src/entity/Listing.ts
import { Entity, Column, BaseEntity, PrimaryGeneratedColumn, ManyToOne } from "typeorm";
import { User } from "./User";

@Entity("listings")
export class Listing extends BaseEntity {
  @PrimaryGeneratedColumn("uuid") id: string;

  @Column("varchar", { length: 100 }) name: string;
  
  @Column("varchar", { length: 100 }) category: string;
  
  @Column("text", { nullable: true }) pictureUrl: string;
  
  @Column("varchar", { length: 255 }) description: string;
  
  @Column("int") price: number;
  
  @Column("int") beds: number;
  
  @Column("int") guests: number;
  
  @Column("double precision") latitude: number;
  
  @Column("double precision") longitude: number;
  
  @Column("text", { array: true }) amenities: string[];
  
  @Column("uuid") userId: string;
  
  @ManyToOne(() => User, user => user.listings) user: User;
}

实时消息功能

// server/src/entity/Message.ts
import { Entity, Column, BaseEntity, PrimaryGeneratedColumn, ManyToOne } from "typeorm";
import { User } from "./User";
import { Listing } from "./Listing";

@Entity("messages")
export class Message extends BaseEntity {
  @PrimaryGeneratedColumn("uuid") id: string;
  
  @Column("text") text: string;
  
  @Column("uuid") userId: string;
  
  @ManyToOne(() => User) user: User;
  
  @Column("uuid") listingId: string;
  
  @ManyToOne(() => Listing) listing: Listing;
}

部署实战

后端部署(Ubuntu服务器)

# 安装PM2进程管理
npm install -g pm2

# 构建项目
cd /path/to/project
yarn build:server

# 启动服务
cd packages/server
pm2 start dist/src/index.js --name "airbnb-server"

# 设置开机自启
pm2 startup
pm2 save

数据库配置(生产环境)

// ormconfig.json 生产环境配置
{
  "name": "production",
  "type": "postgres",
  "host": "your-postgres-host",
  "port": 5432,
  "username": "db-user",
  "password": "secure-password",
  "database": "airbnb-clone",
  "synchronize": false,
  "logging": true,
  "entities": ["dist/src/entity/**/*.js"],
  "migrations": ["dist/src/migration/**/*.js"]
}

前端部署(Netlify/Vercel)

# 构建前端资产
cd /path/to/project
yarn build:web

# 部署 packages/web/build 目录到Netlify

性能优化与扩展

数据库索引优化

-- 添加常用查询字段索引
CREATE INDEX idx_listing_user ON listings(userId);
CREATE INDEX idx_listing_location ON listings(latitude, longitude);
CREATE INDEX idx_message_listing ON messages(listingId);

缓存策略实现

// server/src/loaders/UserLoader.ts
import DataLoader from "dataloader";
import { User } from "../entity/User";

export const createUserLoader = () => 
  new DataLoader<string, User>(async (userIds) => {
    const users = await User.findByIds(userIds as string[]);
    const userIdToUser: Record<string, User> = {};
    
    users.forEach(user => {
      userIdToUser[user.id] = user;
    });
    
    return userIds.map(userId => userIdToUser[userId]);
  });

总结与下一步

通过本项目,你已掌握构建企业级全栈GraphQL应用的核心技术:

  1. 多包架构:使用Yarn Workspaces实现代码复用
  2. 类型安全:TypeScript贯穿前后端开发流程
  3. 数据建模:TypeORM实体设计与关系映射
  4. API设计:GraphQL查询与变更优化
  5. 认证授权:JWT+Redis实现会话管理
  6. 跨端开发:React与React Native代码共享

进阶方向

  1. 支付集成:Stripe支付API接入
  2. 搜索优化:Elasticsearch实现全文搜索
  3. 图片处理:AWS S3+Lambda图片优化
  4. CI/CD:GitHub Actions自动化部署
  5. 监控告警:Prometheus+Grafana监控系统

关注作者获取更多全栈开发实战教程,点赞收藏本文,下次开发全栈项目不迷路!


技术支持:遇到问题?加入项目Discord社区获取帮助
代码更新:关注GitCode仓库获取最新代码
下期预告:GraphQL订阅与实时协作功能深度优化

【免费下载链接】fullstack-graphql-airbnb-clone A Fullstack GraphQL Airbnb Clone with React and React Native 【免费下载链接】fullstack-graphql-airbnb-clone 项目地址: https://gitcode.com/gh_mirrors/fu/fullstack-graphql-airbnb-clone

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值