[第 1 部分] 使用 GraphQL、Knex、Typescript 和 React 创建 Twitter 克隆版
大家好!我决定参加devchallenges.io网站的 Twitter 挑战。之前我只做过 REST API,现在想试试 GraphQL,有个好的项目和设计总是更有趣 ;)。
在开始这个可能相当复杂且特别漫长的挑战之前,我仍然记录了自己并创建了一个项目来尝试回答我对 GraphQL 的问题(数据加载器、错误管理......)。
免责声明:这不是指南/教程。这只是记录我的学习成果的一种方式,欢迎大家留言反馈 ;)
技术栈
我决定使用Node.js,包括ApolloServer + TypeGraphQL + Typescript + Knex ,后端使用 Postgresql ,前端则使用React + ApolloClient进行查询。在我之前的Trello clone和Shoppingify项目上,我曾经使用过 TDD,但这次我会做一些测试,但内容可能会轻量很多。
对于那些想要关注我进度的人来说,这是项目的 Github 仓库 ;)。
Github 仓库
说得够多了,让我们开始编码吧:D。
yarn add apollo-server graphql type-graphql class-validator knex dotenv pg reflect-metadata
yarn add -D typescript ts-node @types/node nodemon jest
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
}
}
包.json
"scripts": {
"dev": "nodemon src/index.ts --exec ts-node",
"build": "shx rm -rf dist/ && tsc -p .",
"start": "node dist/src/index.js"
},
创建 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()
由于我要从创建用户开始,因此我首先创建用户实体以及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
如您所见,我的用户类没有公开密码字段。
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
如果我测试请求,我会得到“Hello”。到目前为止一切顺利 ;)。
使用 Knex 设置数据库
为了完成第一部分,我将使用 Postgresql 数据库设置 Knex。
knex init -x ts
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',
},
},
}
我目前创建了两个数据库,一个用于开发,另一个用于测试。
剩下要做的就是创建我们的用户表;)。
knex migrate:make create_users_table -x ts
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')
}
我开始迁移
knex migrate:latest
我所要做的就是创建与该数据库的连接。顺便说一下,我还将安装knex-tiny-logger库,以便简化 SQL 查询的可视化 ;)。
yarn add -D knex-tiny-logger
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))
剩下要做的就是将其导入到 index.ts 文件中……
import db from './db/connection'
...并将其添加到我的上下文中,以便能够从解析器访问它。
const server = new ApolloServer({
schema: await buildSchema({
resolvers: [AuthResolver],
}),
context: ({ req, res }) => {
return {
req,
res,
db, //Here it is
}
},
})
这是第一部分 ;)。如果您有兴趣我继续写下去,或者在我犯错时指正我等等,请随时告诉我…… :D
在第二部分中,我将设置测试环境。
祝你今天过得愉快 ;)
你学到了 2-3 件事,想请我喝杯咖啡 ;)?
https://www.buymeacoffee.com/ipscoding