NestJS 和 NEXT.js 集成库简介
TLDR
在你的控制器中,例如server/app.controller.ts:
import {
Controller,
Get,
Req,
Res,
} from '@nestjs/common';
import { NextService } from '@nestpress/next';
@Controller()
export class AppController {
constructor(
private readonly next: NextService,
) {}
@Get()
public async showIndexPage(@Req() req, @Res() res) {
///////////////////////////////////////////
// //
// this will render `pages/index.tsx`! //
// //
///////////////////////////////////////////
await this.next.render('/index', req, res);
}
}
为什么?
NestJS是一个渐进式 Node.js 框架,因此我们需要决定使用Handlebars之类的视图引擎。
我喜欢使用 React,有时在创建博客系统时,我希望获得更好的 SEO 效果。
在这种情况下,NEXT.js是一个不错的选择。
但是我们如何在 NestJS 之上使用 NEXT.js 呢?
NEXT.js 是一个 SSR 库,因此它已经包含了生成静态 html 文件的服务器。
那么,我们如何动态地将服务器数据传递给 NEXT.js 客户端呢?
我心中充满了疑问,不知道为什么以及如何实现,所以我决定编写集成库。
#showdev
(注意,下面的博客系统正在建设中!哈哈)
一个基于 NestJS 和 NEXT.js 的、可用于生产环境的个人博客系统

一个基于NestJS和NEXT.js的、可用于生产环境的个人博客系统
路线图
用法
数据库设置
适用于 Mac 用户
#安装 PostgreSQL
$ brew install postgresql
#如果你想在启动时启动 PostgreSQL,请尝试这样做
$ brew services start postgresql
#创建用户“nestpressuser”,密码为“nestpresspass”
$ createuser -P nestpressuser
#创建数据库“nestpressdb”,所有者为“nestpressuser”
$ createdb nestpressdb -O nestpressuser
适用于 Windows 用户
PostgreSQL
> postgresql-11.2-1-windows-x64.exe --install_runtimes 0
pgAdmin
- 请访问https://www.pgadmin.org/download下载最新安装程序
- 运行 pgAdmin 并以 root 用户身份登录
- 右键单击
Login/Group Roles并Create > Login/Group Role
General控制板
Definition控制板:
Priviledges控制板:
- 右键单击
Databases并Create > Database
General标签页
Database:nestpressdb
Owner…
核心库是@nestpress/next。
本文将介绍@nestpress/next及其用途。
安装
$ npm install --save @nestpress/next
用法
首先,填充以下内容package.json:tsconfig.jsontsconfig.server.json
package.json
{
"name": "sample-app",
"scripts": {
"dev": "ts-node --project tsconfig.server.json server/main.ts",
"build": "next build && tsc --project tsconfig.server.json",
"start": "cross-env NODE_ENV=production node .next/production-server/main.js"
},
"dependencies": {
"@nestjs/common": "latest",
"@nestjs/core": "latest",
"@nestpress/next": "latest",
"next": "latest",
"react": "latest",
"react-dom": "latest",
"reflect-metadata": "latest",
"rxjs": "latest"
},
"devDependencies": {
"@types/node": "latest",
"@types/react": "latest",
"@types/react-dom": "latest",
"cross-env": "latest",
"ts-node": "latest",
"typescript": "latest"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"jsx": "preserve",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"strict": true,
"noEmit": true,
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"isolatedModules": true,
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"incremental": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}
tsconfig.server.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"noEmit": false,
"outDir": ".next/production-server"
},
"include": [
"server"
]
}
其次,实施这些文件
- NestJS 方面
server/app.module.ts
server/app.controller.ts
server/main.ts
- NEXT.js 端
server/app.module.ts
在您的应用程序模块中注册NextModule,以便 Nest 可以处理依赖项:
import {
Module,
NestModule,
MiddlewareConsumer,
RequestMethod,
} from '@nestjs/common';
import {
NextModule,
NextMiddleware,
} from '@nestpress/next';
import { AppController } from './app.controller';
@Module({
imports: [
// register NextModule
NextModule,
],
controllers: [
AppController,
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// handle scripts
consumer
.apply(NextMiddleware)
.forRoutes({
path: '_next*',
method: RequestMethod.GET,
});
// handle other assets
consumer
.apply(NextMiddleware)
.forRoutes({
path: 'images/*',
method: RequestMethod.GET,
});
consumer
.apply(NextMiddleware)
.forRoutes({
path: 'favicon.ico',
method: RequestMethod.GET,
});
}
}
server/app.controller.ts
NextService在控制器中这样使用:
import {
IncomingMessage,
ServerResponse,
} from 'http';
import {
Controller,
Get,
Req,
Res,
} from '@nestjs/common';
import { NextService } from '@nestpress/next';
@Controller()
export class AppController {
constructor(
private readonly next: NextService,
) {}
@Get()
public async showHome(@Req() req: IncomingMessage, @Res() res: ServerResponse) {
// this will render `pages/index.tsx`!
await this.next.render('/index', req, res);
}
}
server/main.ts
在主入口点准备 Next.js 服务:
import { NestFactory } from '@nestjs/core';
import { NextModule } from '@nestpress/next';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.get(NextModule).prepare().then(() => {
app.listen(3000, 'localhost', () => {
console.log('> Ready on http://localhost:3000 with Next.js!');
});
});
}
bootstrap();
pages/index.tsx
在pages目录中,我们可以采用与 Next.js 相同的方式:
export default () => (
<p>Next.js on top of NestJS!</p>
);
开发模式
$ yarn dev (or `npm run dev`)
去http://localhost:3000看看就知道了Next.js on top of NestJS!。
生产模式
$ yarn build (or `npm run build`)
$ yarn start (or `npm start`)
选项
import { NestFactory } from '@nestjs/core';
import { NextModule } from '@nestpress/next';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.get(NextModule).prepare({
/**
* Whether to launch Next.js in dev mode
*/
dev: process.env.NODE_ENV !== 'production',
/**
* Where the Next project is located
*/
dir: process.cwd(),
/**
* Hide error messages containing server information
*/
quiet: false,
/**
* Object what you would use in next.config.js
*/
conf: {},
}).then(() => {
app.listen(3000, 'localhost', () => {
console.log('> Ready on http://localhost:3000 with Next.js!');
});
});
}
bootstrap();
高级用法:将服务器数据传递给 NEXT.js 客户端
server/app.controller.ts
@Controller()
export class AppController {
constructor(
private readonly next: NextService,
private readonly articleService: ArticleService,
) {}
@Get()
public async showHome(@Req() req: IncomingMessage, @Res() res: ServerResponse) {
const articles = await this.articleService.findAll();
const data = { articles };
await this.next.render('/index', data, req, res);
}
}
pages/index.tsx
import fetch from 'isomorphic-unfetch';
const HomePage = (props) => {
const { articles } = props;
return (
<ul>
{articles.map((article, index) => (
<li key={index}>{article.title}</li>
))}
</ul>
);
};
// we must define `getInitialProps` so that the NEXT.js can generate static markups
HomePage.getInitialProps = async ({ req, query }) => {
const isServer: boolean = !!req;
let articles;
if (isServer) {
// in the NEXT.js server side, we can pass the server data
// this `query.articles` is passed from AppController
articles = query.articles;
} else {
// in the NEXT.js client side, we need to fetch the same data above
const response = await fetch('http://localhost:3000/api/articles');
articles = await response.json();
}
return {
articles,
};
};
export default HomePage;
结论
NestJS 和 NEXT.js 的集成有点复杂,但如果我们需要使用 SSR 来托管 React 应用,希望这对您有所帮助 :)
相关存储库
基于 NestJS、TypeORM、NEXT.js(v9.3) 和 Material UI(v4) 的最简单身份验证系统。
文章来源:https://dev.to/saltyshiomix/an-introduction-of-the-integration-library-with-nestjs-and-next-js-29f1