目前个人认为的最佳选择是使用Express、React和GraphQL的TypeScript
他妈的烂诗
厌倦JavaScript而投向TypeScript是非常自然的事情。
厌倦REST API并对GraphQL产生兴趣也是非常自然的事情。
不想编写GraphQL查询也是非常自然的事情。
因此,我踏上了在TypeScript中使用GraphQL的最佳解决方案的探索之旅。
这些工具真是太棒了。
经过旅行后,我遇到了这些美妙的工具。
Apollo Server
GraphQL server。
使おう。
Apollo Client
一番人気であろうgraphqlクライアント。
これにはローカルのステート管理機能もついてるので、Reduxを使う必要がなくなる。
使おう。
TypeGraphQL
TypeScriptコードからTypeやResolverを生成するツール。
使わない手はない。
GraphQL Code Generator
GraphQL queryからTypeScriptコードを生成するツール。
これのおかげでGraphQL query書いて、それに合う型ファイルなどを自分で書くという苦行から開放される。
只要拥有这三种神器,就能够充分享受美妙的TypeScript GraphQL生活。
示例存储库
我们向样本库中添加了神器。
Typescript-Express-React-GraphQL -> 类型脚本-快递-反应-图灵身份验证
服务器
搭建GraphQL服务器。
在服务器端使用TypeGraphQL来创建Type和Resolver。
import {Field, ObjectType} from 'type-graphql';
@ObjectType()
export class Hello {
@Field()
str!: string;
@Field()
date!: Date;
}
import {Query, Resolver} from 'type-graphql';
import {Hello} from '../types/Hello';
@Resolver()
export class HelloResolver {
@Query(() => Hello)
hello(): Hello {
return {
str: 'hello world',
date: new Date(),
};
}
}
如果将这两个参数传递给buildSchema函数,就会生成这样的Schema。
scalar DateTime
type Hello {
date: DateTime!
str: String!
}
type Query {
hello: Hello!
}
只要将它与resolvers一起传递给ApolloServer,GraphQL服务器将启动。
import 'reflect-metadata';
import express from 'express';
import {ApolloServer} from 'apollo-server-express';
import {ResolversMap} from 'type-graphql';
import {buildSchema} from './graphql/buildSchema';
import {resolvers} from './graphql/resolvers';
const app = express();
const server = new ApolloServer({
schema: buildSchema(),
resolvers: resolvers as ResolversMap,
});
server.applyMiddleware({app});
app.listen(8080);
是的,令人惊讶的是,我们不需要自己编写架构。
将Schema导出为文件。
为了使用GraphQL Code Generator在客户端端进行代码生成,需要有服务器端的Schema文件。
如果将emitSchemaFile选项传递给buildSchema,它将在指定位置生成该文件,因此请事先准备好该命令。
import 'reflect-metadata';
import path from 'path';
import {buildSchema} from '../src/graphql/buildSchema';
buildSchema({
emitSchemaFile: `${path.dirname(__dirname)}/generated/schema.gql`,
});
import {resolvers} from './resolvers';
import {buildSchemaSync, NonEmptyArray, BuildSchemaOptions} from 'type-graphql';
export const buildSchema = (options: Partial<BuildSchemaOptions> = {}) => {
return buildSchemaSync({
resolvers: (Object.values(resolvers) as unknown) as NonEmptyArray<Function>,
...options,
});
};
{
...
"scripts": {
...
"generate-graphql-schema": "ts-node bin/generate-graphql-schema.ts",
"codegen": "npm run generate-graphql-schema"
},
...
}
当你运行 `npm run codegen`,会在 `server/generated/` 目录下生成类似这样的文件。
# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------
"""
The javascript `Date` as string. Type represents date and time as the ISO Date string.
"""
scalar DateTime
type Hello {
date: DateTime!
str: String!
}
type Query {
hello: Hello!
}
请确保将此文件添加到gitignore和eslintignore中,因为它是一个生成的文件。
这样一来,服务器端的工作就完成了。
顾客
只需按照官方指引初始化Apollo Client即可。
写查询
...
import {gql} from '@apollo/client';
...
gql`
query Hello {
hello {
str
date
}
}
`;
...
GraphQL Code Generator可以识别gql关键字,并将后续的字符串识别为查询语句,这样很方便。
如果想要重复使用的查询语句,可以在client/src/graphql/queries/目录下创建一个.gql文件,并将查询语句写在里面即可。
代码生成
GraphQL Code Generator的设置如下所示。
overwrite: true
schema: "../server/generated/schema.gql"
documents: [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.gql"
]
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
schema使用服务器生成的内容是重点。
希望也能识别在.ts和.tsx文件中编写的查询,所以也要进行相应的配置。
在这个状态下运行npm run codegen,文件将在client/src/generated/中生成。
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
export type Maybe<T> = T | null;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
/** The javascript `Date` as string. Type represents date and time as the ISO Date string. */
DateTime: any;
};
export type Hello = {
__typename?: 'Hello';
date: Scalars['DateTime'];
str: Scalars['String'];
};
export type Query = {
__typename?: 'Query';
hello: Hello;
};
export type HelloQueryVariables = Exact<{ [key: string]: never; }>;
export type HelloQuery = (
{ __typename?: 'Query' }
& { hello: (
{ __typename?: 'Hello' }
& Pick<Hello, 'str' | 'date'>
) }
);
export const HelloDocument = gql`
query Hello {
hello {
str
date
}
}
`;
/**
* __useHelloQuery__
*
* To run a query within a React component, call `useHelloQuery` and pass it any options that fit your needs.
* When your component renders, `useHelloQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useHelloQuery({
* variables: {
* },
* });
*/
export function useHelloQuery(baseOptions?: Apollo.QueryHookOptions<HelloQuery, HelloQueryVariables>) {
return Apollo.useQuery<HelloQuery, HelloQueryVariables>(HelloDocument, baseOptions);
}
export function useHelloLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<HelloQuery, HelloQueryVariables>) {
return Apollo.useLazyQuery<HelloQuery, HelloQueryVariables>(HelloDocument, baseOptions);
}
export type HelloQueryHookResult = ReturnType<typeof useHelloQuery>;
export type HelloLazyQueryHookResult = ReturnType<typeof useHelloLazyQuery>;
export type HelloQueryResult = Apollo.QueryResult<HelloQuery, HelloQueryVariables>;
请不要忘记将此生成文件添加到gitignore和eslintignore中。
发送请求
由于在 client/src/generated/graphql.tsx 中生成了 hook,所以可以直接使用它。
它具有很好的类型安全性。
import React from 'react';
import {gql} from '@apollo/client';
import {useHelloQuery} from './generated/graphql';
gql`
query Hello {
hello {
str
date
}
}
`;
export const Hello = () => {
const {loading, error, data} = useHelloQuery();
if (loading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
return (
<div className={Hello.name}>
<div>
<div>str={data!.hello.str}</div>
<div>date={data!.hello.date}</div>
</div>
</div>
);
};
很遗憾
如果使用这种方法,服务器端将无需编写任何GraphQL查询,但客户端却不得不编写查询。
我觉得这样的话就能解决所有问题,但我还没有找到方法。
我真诚希望某个帅哥能够告诉我,并且颤抖着等待着。