Node サーバー
スタンドアロンな Node サーバーを作る場合は、adapter-node
を使います。
使い方
npm i -D @sveltejs/adapter-node
を実行してインストールし、svelte.config.js
にこの adapter を追加します:
import import adapter
adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: any;
}
kit: {
adapter: any
adapter: import adapter
adapter()
}
};
デプロイ(Deploying)
まず、npm run build
でアプリをビルドします。これによって adapter のオプションで指定した出力ディレクトリ (デフォルトは build
) に本番環境用のサーバーが作成されます。
アプリケーションを実行するには、出力ディレクトリ、プロジェクトの package.json
、node_modules
の本番向けの依存関係(production dependencies)が必要です。本番向けの依存関係は、package.json
と package-lock.json
をコピーしてから npm ci --omit dev
を実行すると生成することができます (あなたのアプリが何の依存関係も持たない場合はこのステップをスキップできます)。そして、このコマンドでアプリを起動することができます:
node build
Rollup を使うと開発用の依存関係(Development dependencies)もアプリにバンドルされます。パッケージをバンドルするか外部化するかコントロールするには、そのパッケージを package.json
の devDependencies
か dependencies
にそれぞれ配置します。
レスポンスの圧縮
通常、サーバーからのレスポンスを圧縮したいでしょう。SSL やロードバランシングのためにリバースプロキシの後ろにサーバーを配置している場合は、通常はそちらのレイヤーで圧縮を処理したほうがパフォーマンスの向上につながります。Node.js はシングルスレッドだからです。
しかし、あなたが custom server を構築していて、そこに圧縮用のミドルウェアを追加したい場合は、@polka/compression
を使用することをおすすめします。SvelteKit はレスポンスをストリーミングしますが、一般的な compression
パッケージはストリーミングをサポートしていないため、使用するとエラーとなる可能性があります。
環境変数
dev
と preview
のときは、SvelteKit は .env
ファイル (または .env.local
や .env.[mode]
、Vite によって決定されているもの) から環境変数を読み取ります。
プロダクションでは、.env
ファイルは自動的に読み取れらません。そうするには、プロジェクトに dotenv
をインストールします…
npm install dotenv
…そしてビルドされたアプリを実行する前にそれを呼び出します:
node -r dotenv/config build
If you use Node.js v20.6+, you can use the --env-file
flag instead:
node --env-file=.env build
PORT、HOST、SOCKET_PATH
デフォルトでは、サーバーは 0.0.0.0
、port 3000 でコネクションを受け付けます。これは環境変数の PORT
と HOST
を使ってカスタマイズすることができます。
HOST=127.0.0.1 PORT=4000 node build
その他の方法としては、指定したソケットパスでコネクションを受け付けるようサーバーを設定することができます。環境変数の SOCKET_PATH
を使用して設定する場合、環境変数の HOST
と PORT
は無視されます。
SOCKET_PATH=/tmp/socket node build
ORIGIN、PROTOCOL_HEADER、HOST_HEADER、PORT_HEADER
HTTP は SvelteKit に現在リクエストされている URL を知るための信頼できる方法を提供しません。アプリがホストされている場所を Sveltekit に伝える最も簡単な方法は、環境変数 ORIGIN
を設定することです:
ORIGIN=https://my.site node build
# or e.g. for local previewing and testing
ORIGIN=http://localhost:3000 node build
これにより、パス名 /stuff
に対するリクエストは正しく https://my.site/stuff
に解決されます。別の方法として、リクエストプロトコルとホストを SvelteKit に伝えるヘッダーを指定し、そこから origin URL を組み立てることもできます:
PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build
x-forwarded-proto
とx-forwarded-host
は事実上の標準となっているヘッダーで、リバースプロキシー (ロードバランサーや CDN などを考えてみてください) を使用している場合に、オリジナルのプロトコルとホストを転送します。これらの変数は、あなたのサーバーが信頼できるリバースプロキシーの後ろにある場合にのみ設定すべきです。そうしないと、クライアントがこれらのヘッダーを偽装することが可能になってしまいます。プロキシーを非標準の port でホストしていて、リバースプロキシ−が
x-forwarded-port
をサポートしている場合は、PORT_HEADER=x-forwarded-port
を設定することもできます。
adapter-node
があなたのデプロイの URL を正しく判断することができない場合、form actions を使用するとこのエラーが発生することがあります:
クロスサイトの POST フォーム送信は禁止されています
ADDRESS_HEADER と XFF_DEPTH
hooks とエンドポイントに渡される RequestEvent オブジェクトにはクライアントの IP アドレスを返す event.getClientAddress()
関数が含まれています。デフォルトでは、これは接続中の remoteAddress
です。もしサーバーが1つ以上のプロキシー (例えばロードバランサー) の後ろにある場合、この値はクライアントの IP アドレスではなく、最も内側にあるプロキシーの IP アドレスを含むことになるため、アドレスを読み取るために ADDRESS_HEADER
を指定する必要があります:
ADDRESS_HEADER=True-Client-IP node build
ヘッダーは簡単に偽装されます。
PROTOCOL_HEADER
やHOST_HEADER
と同様、これらを設定する前に自分が何をしているのか知るべきです。
ADDRESS_HEADER
が X-Forwarded-For
の場合、ヘッダーの値にはカンマで区切られた IP アドレスのリストが含まれます。環境変数 XFF_DEPTH
には、あなたのサーバーの前に信頼できるプロキシーがいくつあるか指定する必要があります。例えば、3つの信頼できるプロキシーがある場合、プロキシー3はオリジナルのコネクションと最初の2つのプロキシーのアドレスを転送します:
<client address>, <proxy 1 address>, <proxy 2 address>
一番左のアドレスを読め、というガイドもありますが、これだとスプーフィング(なりすまし)に対し脆弱なままです:
<spoofed address>, <client address>, <proxy 1 address>, <proxy 2 address>
代わりに、信頼できるプロキシーの数を考慮して右から読み込みます。この場合、XFF_DEPTH=3
を使用します。
もし、一番左のアドレスを読む必要がある場合 (そしてスプーフィングを気にしない場合) — 例えば、位置情報サービスを提供する場合、つまり IP アドレスが信頼できることよりもリアルであることが重要な場合、アプリの中で
x-forwarded-for
ヘッダーを検査することでそれが可能です。
BODY_SIZE_LIMIT
ストリーミング中も含め、受け付けるリクエストボディの最大サイズを byte で指定します。ボディサイズの指定には単位を使用することができます。キロバイトには (K
)、メガバイトには (M
)、ギガバイトには (G
) です。例えば、512K
や 1M
のようにします。デフォルトは 512キロバイト です。もっと高度な設定が必要な場合は、このオプションの値を Infinity
(adapter が古いバージョンの場合は 0) にして無効化し、handle
にカスタムのチェックを実装することができます。
SHUTDOWN_TIMEOUT
The number of seconds to wait before forcefully closing any remaining connections after receiving a SIGTERM
or SIGINT
signal. Defaults to 30
. Internally the adapter calls closeAllConnections
. See Graceful shutdown for more details.
IDLE_TIMEOUT
When using systemd socket activation, IDLE_TIMEOUT
specifies the number of seconds after which the app is automatically put to sleep when receiving no requests. If not set, the app runs continuously. See Socket activation for more details.
Options
この adapter は様々なオプションで設定を行うことができます:
import import adapter
adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: any;
}
kit: {
adapter: any
adapter: import adapter
adapter({
// default options are shown
out: string
out: 'build',
precompress: boolean
precompress: true,
envPrefix: string
envPrefix: ''
})
}
};
out
サーバーをビルドするディレクトリです。デフォルトは build
です。つまり、node build
を指定すると、サーバが作成されローカルで起動します。
precompress
アセットやプリレンダリングされたページを gzip や brotli を使って事前圧縮(precompress)するのを有効にします。デフォルトは true
です。
envPrefix
デプロイの設定に使用される環境変数の名前を変更する必要がある場合 (例えば、あなたのコントロール下にない環境変数との競合を解消するため)、接頭辞(prefix)を指定することができます:
envPrefix: 'MY_CUSTOM_';
MY_CUSTOM_HOST=127.0.0.1 \
MY_CUSTOM_PORT=4000 \
MY_CUSTOM_ORIGIN=https://my.site \
node build
Graceful shutdown
By default adapter-node
gracefully shuts down the HTTP server when a SIGTERM
or SIGINT
signal is received. It will:
- reject new requests (
server.close
) - wait for requests that have already been made but not received a response yet to finish and close connections once they become idle (
server.closeIdleConnections
) - and finally, close any remaining connections that are still active after
SHUTDOWN_TIMEOUT
seconds. (server.closeAllConnections
)
If you want to customize this behaviour you can use a custom server.
You can listen to the sveltekit:shutdown
event which is emitted after the HTTP server has closed all connections. Unlike Node’s exit
event, the sveltekit:shutdown
event supports asynchronous operations and is always emitted when all connections are closed even if the server has dangling work such as open database connections.
var process: NodeJS.Process
process.NodeJS.Process.on(event: string | symbol, listener: (...args: any[]) => void): NodeJS.Process (+12 overloads)
Adds the listener
function to the end of the listeners array for the event
named eventName
. No checks are made to see if the listener
has already
been added. Multiple calls passing the same combination of eventName
and
listener
will result in the listener
being added, and called, multiple times.
server.on('connection', (stream) => {
console.log('someone connected!');
});
Returns a reference to the EventEmitter
, so that calls can be chained.
By default, event listeners are invoked in the order they are added. The emitter.prependListener()
method can be used as an alternative to add the
event listener to the beginning of the listeners array.
import { EventEmitter } from 'node:events';
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
// b
// a
on('sveltekit:shutdown', async (reason: any
reason) => {
await jobs.stop();
await db.close();
});
The parameter reason
has one of the following values:
SIGINT
- shutdown was triggered by aSIGINT
signalSIGTERM
- shutdown was triggered by aSIGTERM
signalIDLE
- shutdown was triggered byIDLE_TIMEOUT
Socket activation
Most Linux operating systems today use a modern process manager called systemd to start the server and run and manage services. You can configure your server to allocate a socket and start and scale your app on demand. This is called socket activation. In this case, the OS will pass two environment variables to your app — LISTEN_PID
and LISTEN_FDS
. The adapter will then listen on file descriptor 3 which refers to a systemd socket unit that you will have to create.
You can still use
envPrefix
with systemd socket activation.LISTEN_PID
andLISTEN_FDS
are always read without a prefix.
To take advantage of socket activation follow these steps.
- Run your app as a systemd service. It can either run directly on the host system or inside a container (using Docker or a systemd portable service for example). If you additionally pass an
IDLE_TIMEOUT
environment variable to your app it will gracefully shutdown if there are no requests forIDLE_TIMEOUT
seconds. systemd will automatically start your app again when new requests are coming in.
[Service]
Environment=NODE_ENV=production IDLE_TIMEOUT=60
ExecStart=/usr/bin/node /usr/bin/myapp/build
- Create an accompanying socket unit. The adapter only accepts a single socket.
[Socket]
ListenStream=3000
[Install]
WantedBy=sockets.target
- Make sure systemd has recognised both units by running
sudo systemctl daemon-reload
. Then enable the socket on boot and start it immediately usingsudo systemctl enable --now myapp.socket
. The app will then automatically start once the first request is made tolocalhost:3000
.
カスタムサーバー
この adapter は、ビルドのディレクトリに2つのファイルを作成します — index.js
と handler.js
です。デフォルトのビルドのディレクトリを使用している場合、node build
などで index.js
を実行すると、設定された port でサーバーが起動されます。
別の方法として、Express、Connect、Polka (またはビルトインの http.createServer
) を使用するためのハンドラーをエクスポートする handler.js
ファイルをインポートし、独自のサーバーをセットアップすることもできます。
import { import handler
handler } from './build/handler.js';
import import express
express from 'express';
const const app: any
app = import express
express();
// add a route that lives separately from the SvelteKit app
const app: any
app.get('/healthcheck', (req, res) => {
res: any
res.end('ok');
});
// let SvelteKit handle everything else, including serving prerendered pages and static assets
const app: any
app.use(import handler
handler);
const app: any
app.listen(3000, () => {
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object’s methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log('listening on port 3000');
});