В этом посте я проведу вас по пути создания приложения Next.js, обслуживаемого клиентским сервером Express c применением фреймворка Typescript. Полную версию проекта можно увидеть на GitHub.
Сначала создадим папку проекта:
mkdir -p next-express-typescript
cd ./next-express-typescript
Инициализируем файл package.json
и установим зависимости:
npm init -y
npm install react react-dom next
Добавим скрипты (описания команд) в соответствующий раздел файла package.json
:
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
Создадим первую страницу проекта:
mkdir -p src/pages
touch src/pages/index.js
Наполним ее некоторым содержимым:
function IndexPage() {
return <div>Hello World Next - Typescript - Express</div>;
}
export default IndexPage;
Запустим команду npm run dev
и перейдем на http://localhost:3000
, чтобы посмотреть созданную страницу:
В основном, Next.js
предоставляет быстрый способ настройки TypeScript. Создадим пустой файл tsconfig.json
:
Снова запустим команду npm run dev
. После чего увидим предложение от Next.js
установить необходимые пакеты:
Установим их. На этом установка TypeScript завершится.
npm install --save-dev typescript @types/react @types/node
mv src/pages/index.js src/pages/index.tsx
Снова запустим сервер npm run dev
. Теперь все должно работать правильно.
При выполнении команды npm run dev
Next.js запускает свой сервер, но возможна программная установка собственного сервера Express. Необходимо установить Express
, а также зависимости Typescript для него.
npm install express
npm install --save-dev @types/express ts-node
Создадим папку server
и точку входа для сервера:
mkdir -p server
touch server/index.ts
В файле index.ts
напишем следующий код:
import express, { Request, Response } from "express";
import next from "next";
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const port = process.env.PORT || 3000;
(async () => {
try {
await app.prepare();
const server = express();
server.all("*", (req: Request, res: Response) => {
return handle(req, res);
});
server.listen(port, (err?: any) => {
if (err) throw err;
console.log(`> Ready on localhost:${port} - env ${process.env.NODE_ENV}`);
});
} catch (e) {
console.error(e);
process.exit(1);
}
})();
Теперь отредактируем скрипт dev
в package.json
"scripts": {
"dev": "ts-node server/index.ts",
...
}
ts-node — указывает на исполняемый файл TypeScript и REPL для Node.js с поддержкой source map.
В очередной раз запускаем npm run dev
. И… получаем ошибку.
import express from "express";
^^^^^^^
Проблема заключается в tsconfig.json
, в строке “module”: “esnext”
.
Как правило, Next.js заставляет TS компилироваться с шаблоном ESNext, но express
был собран по шаблону commonjs
. Если изменить значение esnext
на commonjs
, Next.js автоматически вернет его обратно.
Чтобы решить эту проблему, давайте создадим еще один конфигурационный файл:
touch tsconfig.server.jsonИ добавим в него следующие строки кода из tsconfig.json
:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist",
"noEmit": false
},
"include": ["server"]
}
Рассмотрим каждую строчку в отдельности:
· "extends": "./tsconfig.json"
: получаем все опции из tsconfig.json
;
· "compilerOptions.module": "commonjs"
: используем commonjs
как шаблон, чтобы была возможна дальнейшая совместная работа express
и next
;
· "compilerOptions.outDir": "dist"
: эта опция используется для продакшна. Файлы с расширением .ts
в директории server
будут компилироваться в файлы с расширением .js
и отправляться в директорию dist
.
· "compilerOptions.noEmit": true
: еще одна настройка для продакшна. Next.js использует в основном babel
для компиляции TypeScript, поэтому TS-компилятор не будет понимать файлы с расширением .js
. Переопределение значения сообщит TS-компилятору о том, что он может транслировать и выводить файлы с server
.
· "include": ["server"]
: эта опция также используется для продакшна. Она позволяет компилятору транслировать только файлы с расширением .ts
в директорию server/
.
Будем использовать эту конфигурацию для локальной разработки. Снова отредактируем скрипт dev
:
Выполняем npm run dev
, переходим на http//localhost:3000
… Все работает!
Некоторые могут столкнуться с ошибкой RangeError: Invalid array length
, которая появляется в консоли при запуске пользовательского сервера.
Кроме того, при посещении незнакомой страницы, скажем http://localhost:3000/no-exist-page
, получим Internal Server Error
вместо сообщения 404 not found
. Это потому, что Next.js больше не обрабатывает ошибки при интеграции с пользовательским сервером. Решением этой проблемы будет создание кастомной страницы ошибки.
Откроем созданный файл и напишем в нем такой код:
import { NextPageContext } from "next";
const Error = ({ statusCode }) => {
return (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: "An error occurred on client"}
</p>
);
};
Error.getInitialProps = ({ res, err }: NextPageContext) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
return { statusCode };
};
export default Error;
Снова посещаем страницу http://localhost:3000/no-exist-page
, где видим наше сообщение об ошибке.
Перед запуском сервера в продакшне необходимо скомпилировать файлы сервера и Next.js в файлы с расширением .js
. Изменим scripts
в package.json
.
"scripts": {
"build:server": "tsc --project tsconfig.server.json",
"build:next": "next build",
"build": "npm run build:next && npm run build:server",
"start": "NODE_ENV=production node dist/index.js"
}
Рассмотрим каждую строку в отдельности:
· build: server
: сборка кода сервера в каталог dist
;
· build: next
: сборка приложения Next.js;
· build
: совместный запуск build: server
и build: next
;
· start
: запуск сервера. Строчка запускает node dist/index.js
вместо next start
, потому что теперь мы позволяем express
обрабатывать сервер.
Запустим сборку npm run build
и увидим такой результат:
Теперь запустим npm start
и посмотрим, как наше приложение работает в продакшне.
> NODE_ENV=production node dist/index.js
> Ready on localhost:3000 – env production
Теперь у вас есть шаблон, чтобы начать Next.js-проект с применением сервера Express и TypeScript! Есть еще некоторые продвинутые технологии, осуществляющие тестирование кода Next и Express… Но о них как-нибудь в другой раз.
Надеюсь, было полезно! Спасибо за чтение и удачи в программировании!
Перевод статьи Tien: Set Up Next.js with a Custom Express Server + Typescript.
Комментарии