[第 1 部分] 使用 GraphQL、Knex、Typescript 和 React 创建 Twitter 克隆版

2025-06-07

[第 1 部分] 使用 GraphQL、Knex、Typescript 和 React 创建 Twitter 克隆版

大家好!我决定参加devchallenges.io网站的 Twitter 挑战。之前我只做过 REST API,现在想试试 GraphQL,有个好的项目和设计总是更有趣 ;)。

推特挑战

在开始这个可能相当复杂且特别漫长的挑战之前,我仍然记录了自己并创建了一个项目来尝试回答我对 GraphQL 的问题(数据加载器、错误管理......)。

免责声明:这不是指南/教程。这只是记录我的学习成果的一种方式,欢迎大家留言反馈 ;)

技术栈

我决定使用Node.js,包括ApolloServer + TypeGraphQL + Typescript + Knex ,后端使用 Postgresql ,前端则使用React + ApolloClient进行查询。在我之前的Trello cloneShoppingify项目上,我曾经使用过 TDD,但这次我会做一些测试,但内容可能会轻量很多。

对于那些想要关注我进度的人来说,这是项目的 Github 仓库 ;)。
Github 仓库

说得够多了,让我们开始编码吧:D。

yarn add apollo-server graphql type-graphql class-validator knex dotenv pg reflect-metadata
Enter fullscreen mode Exit fullscreen mode
yarn add -D typescript ts-node @types/node nodemon jest
Enter fullscreen mode Exit fullscreen mode

tsconfig.json

{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",                    
    "lib": ["es2018", "esnext.asynciterable"],
    "outDir": "dist",                       
    "rootDir": "src",                       
    "strict": true,                           
    "esModuleInterop": true,                  
    "experimentalDecorators": true,        
    "emitDecoratorMetadata": true,     
    "skipLibCheck": true,                    
    "forceConsistentCasingInFileNames": true
  }
}

Enter fullscreen mode Exit fullscreen mode

包.json

"scripts": {
    "dev": "nodemon src/index.ts --exec ts-node",
    "build": "shx rm -rf dist/ && tsc -p .",
    "start": "node dist/src/index.js"
  },
Enter fullscreen mode Exit fullscreen mode

创建 GraphQL 服务器

一旦所有这些都设置好了,我就可以启动我的服务器了。

src/index.ts

import 'reflect-metadata'
import { ApolloServer } from 'apollo-server'
import { buildSchema } from 'type-graphql'
import AuthResolver from './resolvers/AuthResolver'

export const createServer = async () => {
  const server = new ApolloServer({
    schema: await buildSchema({
      resolvers: [AuthResolver],
    }),
    context: ({ req, res }) => {
      return {
        req,
        res,
      }
    },
  })

  server.listen().then(({ port }) => {
    console.log(`Listening on port ${port}`)
  })

  return server
}

createServer()

Enter fullscreen mode Exit fullscreen mode

由于我要从创建用户开始,因此我首先创建用户实体以及AuthResolver

src/实体/用户.ts

import { Field, ID, ObjectType } from 'type-graphql'

@ObjectType()
class User {
  @Field((type) => ID)
  id: number

  @Field()
  username: string

  @Field()
  email: string

  password: string

  @Field()
  created_at: Date

  @Field()
  updated_at: Date
}

export default User

Enter fullscreen mode Exit fullscreen mode

如您所见,我的用户类没有公开密码字段。

src/resolvers/AuthResolver.ts

import { Ctx, Query, Resolver } from 'type-graphql'
import { MyContext } from '../types/types'

@Resolver()
class AuthResolver {
  @Query(() => String)
  async me(@Ctx() ctx: MyContext) {
    return 'Hello'
  }
}

export default AuthResolver
Enter fullscreen mode Exit fullscreen mode

如果我测试请求,我会得到“Hello”。到目前为止一切顺利 ;)。

GraphQL 查询

使用 Knex 设置数据库

为了完成第一部分,我将使用 Postgresql 数据库设置 Knex。

knex init -x ts
Enter fullscreen mode Exit fullscreen mode

knexfile.ts


module.exports = {
  development: {
    client: 'pg',
    connection: {
      database: 'challenge_twitter', 
      user: 'postgres',
      password: 'root',
    },
    pool: {
      min: 2,
      max: 10,
    },
    migrations: {
      directory: './src/db/migrations',
    },
    seeds: {
      directory: './src/db/seeds',
    },
  },
  test: {
    client: 'pg',
    connection: {
      database: 'challenge_twitter_test',
      user: 'postgres',
      password: 'root',
    },
    pool: {
      min: 2,
      max: 10,
    },
    migrations: {
      directory: './src/db/migrations',
    },
    seeds: {
      directory: './src/db/seeds',
    },
  },
}

Enter fullscreen mode Exit fullscreen mode

我目前创建了两个数据库,一个用于开发,另一个用于测试。

剩下要做的就是创建我们的用户表;)。

knex migrate:make create_users_table -x ts
Enter fullscreen mode Exit fullscreen mode

src/db/migrations/create_users_table.ts

import * as Knex from 'knex'

export async function up(knex: Knex): Promise<void> {
  return knex.schema.createTable('users', (t) => {
    t.increments('id')
    t.string('username').notNullable().unique()
    t.string('email').notNullable().unique()
    t.string('password').notNullable()
    t.timestamps(false, true)
  })
}

export async function down(knex: Knex): Promise<void> {
  return knex.raw('DROP TABLE users CASCADE')
}

Enter fullscreen mode Exit fullscreen mode

我开始迁移

knex migrate:latest
Enter fullscreen mode Exit fullscreen mode

我所要做的就是创建与该数据库的连接。顺便说一下,我还将安装knex-tiny-logger库,以便简化 SQL 查询的可视化 ;)。

yarn add -D knex-tiny-logger
Enter fullscreen mode Exit fullscreen mode

src/db/connection.ts

import Knex from 'knex'
import KnexTinyLogger from 'knex-tiny-logger'

const config = require('../../knexfile')[process.env.NODE_ENV || 'development']

export default KnexTinyLogger(Knex(config))

Enter fullscreen mode Exit fullscreen mode

剩下要做的就是将其导入到 index.ts 文件中……

import db from './db/connection'
Enter fullscreen mode Exit fullscreen mode

...并将其添加到我的上下文中,以便能够从解析器访问它。

const server = new ApolloServer({
    schema: await buildSchema({
      resolvers: [AuthResolver],
    }),
    context: ({ req, res }) => {
      return {
        req,
        res,
        db, //Here it is
      }
    },
  })
Enter fullscreen mode Exit fullscreen mode

这是第一部分 ;)。如果您有兴趣我继续写下去,或者在我犯错时指正我等等,请随时告诉我…… :D

在第二部分中,我将设置测试环境。

祝你今天过得愉快 ;)

你学到了 2-3 件事,想请我喝杯咖啡 ;)?
https://www.buymeacoffee.com/ipscoding

文章来源:https://dev.to/ipscodingchallenge/part-1-creating-a-twitter-clone-with-graphql-knex-typescript-and-react-32i1
PREV
DOM 操作初学者指南元素选择器查询选择器创建元素更改属性删除元素
NEXT
告别PhoneGap