使用 Next.js + Prisma 2 + TypeScript + Now 实现安全且高效的全栈开发的方法
对于喜欢前端的人来说,最困难的莫过于后端实现。至少对于我来说是这样的。
所以今天我們將使用Next.js的API Routes,以一種非常簡單的方式介紹如何建立一個GraphQL的終端點API。
首先
这次使用的库如下
-
- Next.js
-
- Prisma2 w/ Photon & Lift
-
- GraphQL Nexus
-
- Apollo Server
- Apollo Client
Prisma是什么?
有兴趣的人请阅读这篇文章,它比自己解释更清楚地说明了。
简单来说,它是根据在 schema.prisma 中编写的数据库模式,在生成好的 ORM 中提供便利。
就像上面的文章已经提到的那样,由于目前仍然处于预览阶段,所以并不适合用于正式环境。只能用来玩玩而已。
GraphQL Nexus是什么?
GraphQL Nexus是Prisma公司开发的工具,它可以使code-first的GraphQL模式具有类型安全性和可扩展性。
传统的GraphQL服务器构建方法是以SDL(Schema Definition Language)为基础的。这里所说的是以.graphql结尾的文件。首先定义模式,然后在其他地方编写解析器。如果使用TypeScript,还需要编写模式的类型。结果会出现一点变更模式,就需要同时修改依赖于该模式的其他文件,当应用程序变得越来越庞大时,可扩展性就会受到影响。
采用代码优先的方式,意味着模式在代码中被定义,SDL会自动生成,这样一来,在更改模式时就省去了在其他文件中进行编辑的麻烦。稍后将会通过代码详细说明。
发展
使用create-next-app命令来创建Next.js应用程序
npx create-next-app next-prisma
用这个简单的Next.js应用程序已经创建好了。由于这次是使用TypeScript进行开发的,所以请先将pages/index.js更改为index.tsx,然后运行npm run dev试试看。Next.js会检测文件扩展名的变化,并自动创建tsconfig.json(非常方便)。然后会出现需要安装typescript、@types/react和@types/node的错误提示,请按照提示进行安装。
模块的安装
无论使用npm还是yarn都可以。由于create-next-app使用yarn来安装初始模块,因此可以使用yarn执行参考代码。
yarn add @prisma/photon apollo-server-micro graphql nexus nexus-prisma
yarn add --dev prisma2 ts-node
请在package.json文件中添加以下脚本。稍后会进行说明。
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"generate:prisma": "prisma2 generate",
"generate:nexus": "ts-node --project tsconfig-api.json --transpile-only api/_src/schema",
"generate": "yarn generate:prisma && yarn generate:nexus",
"postinstall": "yarn generate"
}
创建prisma文件
在根目錄中創建Prisma文件。
在其中創建schema.prisma並定義以下模型。
generator photon {
provider = "photonjs"
}
datasource db {
provider = "postgresql"
url = env("PG_URL")
}
model User {
id Int @id
email String @unique
name String?
}
数据源的提供者被设定为PostgreSQL,但也可以使用MySQL或MongoDB,选择您喜欢的。
在环境变量中,env会查找在.env文件中定义的环境变量,因此可以在本地和生产环境中分别使用不同的数据库。要向Now添加环境变量,需要在终端中登录Now。
now secrets add pg-url "postgresql://USERNAME:PASSWORD@localhost:PORT/postgres?SCHEMA"
# now secrets add <secret-name> <secret-value>
在Heroku等平台上可以很容易地创建PostgreSQL数据库,可以通过打开设置来进行配置。创建一个next.config.js文件,并按照以下方式定义以引用。由于Prisma2在编译期间会引用环境变量,因此也需要在构建时进行定义。
exports.default = {
env: {
PG_URL: '@pg-url'
},
build: {
env: {
PG_URL: '@pg-url'
}
}
}
在这个阶段,我们执行yarn generate:prisma命令。这将生成基于模式的代码到node_modules/@prisma/photon目录。通过这样做,将生成photon API的基础结构,我们可以使用类似photon.users.findMany({})之类的API。
顺便说一下,还有一个名为prisma2 dev的命令,当执行它时,Prisma会监视模式文件,如果有变动,则会自动重新生成photon,并且更新数据库的模式,还提供了一个名为Prisma Studio的图形界面工具。你可以直接在GUI中编辑记录,非常方便。
建立GraphQL服务器
由于Prisma作为数据层已经准备好了,我们要构建一个用于访问ORM的API层的GraphQL服务器。
想象一下,就好像有两个服务器。
以下是一种中文本地化的解释:Next.js <—-> GraphQL服务器 <—-> Prisma
首先,在根目录下创建一个名为”api”的文件夹。
普通,在Next.js中需要在/pages目录下创建api文件夹,但是在部署到now后,Photon和Next.js之间的一致性无法保持,现在无法建立连接。请在此处了解更多详细信息。然而,这样做会导致next dev无法连接到API服务器,所以请使用now dev在本地模拟now环境进行开发。希望尽快解决这个问题。
在其中创建graphql.ts、_src/schema.ts和_src/context.ts文件。
├── api
│ ├── _src
│ │ ├── context.ts
│ │ └── schema.ts
│ └── graphql.ts
大概是这样的。似乎以”_开头的文件不能作为无服务器端点处理。”
import { Photon } from '@prisma/photon';
const photon = new Photon()
export interface Context {
photon: Photon
}
// apolloServerでphotonをcontextとして渡すと、のちにnexus側で使用可能になる
export const createContext = () => ({ photon })
import { makeSchema, objectType } from 'nexus'
import { nexusPrismaPlugin } from "nexus-prisma";
import { join } from 'path';
// __dirname使えない問題はこれで解決。詳細はこちら
// https://github.com/prisma/prisma2/issues/1021
const getPath = (fileName: string) =>
join(process.cwd(), "generated", fileName);
// objectTypeでタイプを作る。なんでも作れるが、prisma側とあわせて作るとmodel APIを提供してくれて型を補給してくれるので、便利
const User = objectType({
name: "User",
definition(t) {
t.model.email();
t.model.id();
}
})
const Query = objectType({
name: "Query",
definition(t) {
t.crud.user();
t.crud.users();
}
})
const Mutation = objectType({
name: "Mutation",
definition(t) {
t.crud.createOneUser({
alias: 'createUser'
})
}
})
export const schema = makeSchema({
types: [User, Query, Mutation],
plugins: [nexusPrismaPlugin()],
outputs: {
typegen: getPath('nexus.ts'),
schema: getPath('schema.graphql')
},
typegenAutoConfig: {
contextType: "Context.Context",
sources: [
{
source: "@prisma/photon",
alias: "photon"
},
{
source: require.resolve("./context"),
alias: "Context"
}
]
}
})
import { ApolloServer } from 'apollo-server-micro';
import { createContext } from './_src/context';
import { schema } from './_src/schema';
const apolloServer = new ApolloServer({ schema, context: createContext() })
export const config = {
api: {
bodyParser: false
}
};
export default apolloServer.createHandler({ path: '/api/graphql' });
在运行yarn generate:nexus之前,请先创建一个名为tsconfig-api.json的文件,除了基本的tsconfig.json之外。然后,在其中添加以下内容:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
请用中文进行以下内容的改写。只需提供一种选项:
在进行Next.js开发时,如果使用”module”: “esnext”来编译Next.js代码,则无法使用ts-node进行编译。因此,只有在使用ts-node进行编译时,才会使用专门的tsconfig-api.json配置文件。在脚本中使用”–project tsconfig-api.json”进行指定。
请运行命令”yarn generate:nexus”,如果成功,将在根目录下生成一个名为”generated”的文件夹。在该文件夹中包含了nexus内部使用的”nexus.ts”文件和描述SDL的”schema.graphql”文件。这样一来,平时需要手动编写的graphql文件将会被nexus自动生成。目前阶段只有一个简单的”User”类型,但随着模型的增加和复杂度的提高,将能享受到这个自动生成的好处。
现在基本完成了,请尝试运行now dev。当访问/api/graphql的端点时,应该会打开graphql playground。然后,您可以在客户端使用apollo client,随心所欲地传递数据。
最后
prisma2目前仍处于预览阶段,开发进度可在此确认。
我觉得在一个仓库里,不需要使用单体库等,只需一个`now`命令就能部署后端和前端,这真是一个了不起的世界。
当将GitHub与Now进行链接,将能够在每个PR时进行部署,并提供一个验证用的URL,使得我们可以轻松检查差异。感觉今年对于前端工程师来说可能会是最好的一年。
强烈推荐阅读Guillermo Rauch的这篇博客文章,他作为Zeit的创始人,在这篇文章中详细阐述了未来网络开发的展望等内容。
结束了。