Skip to content

Commit aeb0a82

Browse files
matthsbenmccann
authored andcommitted
[feat] expose handler to allow use in custom server
1 parent ade5616 commit aeb0a82

File tree

8 files changed

+96
-23
lines changed

8 files changed

+96
-23
lines changed

documentation/faq/80-integrations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Put the code to query your database in [endpoints](/docs#routing-endpoints) - do
6767

6868
### How do I use middleware?
6969

70-
In dev, you can add middleware to Vite by using a Vite plugin. For example:
70+
You can add middleware to `adapter-node` for production mode. In dev, you can add middleware to Vite by using a Vite plugin. For example:
7171

7272
```js
7373
const myPlugin = {

packages/adapter-node/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,35 @@ HOST=127.0.0.1 PORT=4000 node build
4545

4646
You can specify different environment variables if necessary using the `env` option.
4747

48+
## Middleware
49+
50+
The adapter exports a middleware `(req, res, next) => {}` that's compatible with [Express](https://github.com/expressjs/expressjs.com) / [Polka](https://github.com/lukeed/polka). Additionally, it also exports a reference server implementation using this middleware with a plain Node HTTP server.
51+
52+
But you can use your favorite server framework to combine it with other middleware and server logic. You can import `createHandler()`, your ready-to-use SvelteKit bundle as middleware, from `./build/handler.js`.
53+
54+
```
55+
import { createHandler } from './build/handler.js';
56+
import express from 'express';
57+
58+
const app = express();
59+
60+
var myMiddleware = function (req, res, next) {
61+
console.log('Hello world!');
62+
next();
63+
};
64+
65+
app.use(myMiddleware);
66+
67+
app.get('/no-svelte', (req, res) => {
68+
res.send('This is not Svelte!')
69+
});
70+
71+
// SvelteKit
72+
app.get('*', createHandler());
73+
74+
app.listen(3000)
75+
```
76+
4877
## Advanced Configuration
4978

5079
### esbuild

packages/adapter-node/index.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default function ({
5353
await compress(static_directory);
5454
}
5555

56-
utils.log.minor('Building server');
56+
utils.log.minor('Building SvelteKit request handler');
5757
const files = fileURLToPath(new URL('./files', import.meta.url));
5858
utils.copy(files, '.svelte-kit/node');
5959
writeFileSync(
@@ -66,10 +66,11 @@ export default function ({
6666
port_env
6767
)}] || (!path && 3000);`
6868
);
69+
6970
/** @type {BuildOptions} */
7071
const defaultOptions = {
71-
entryPoints: ['.svelte-kit/node/index.js'],
72-
outfile: join(out, 'index.js'),
72+
entryPoints: ['.svelte-kit/node/handler.js'],
73+
outfile: join(out, 'handler.js'),
7374
bundle: true,
7475
external: Object.keys(JSON.parse(readFileSync('package.json', 'utf8')).dependencies || {}),
7576
format: 'esm',
@@ -83,6 +84,32 @@ export default function ({
8384
const buildOptions = esbuildConfig ? await esbuildConfig(defaultOptions) : defaultOptions;
8485
await esbuild.build(buildOptions);
8586

87+
utils.log.minor('Building SvelteKit reference server');
88+
/** @type {BuildOptions} */
89+
const defaultOptionsRefServer = {
90+
entryPoints: ['.svelte-kit/node/index.js'],
91+
outfile: join(out, 'index.js'),
92+
bundle: true,
93+
external: ['./handler.js'], // does not work, eslint does not exclude handler from target
94+
format: 'esm',
95+
platform: 'node',
96+
target: 'node12',
97+
// external exclude workaround, see https://github.com/evanw/esbuild/issues/514
98+
plugins: [
99+
{
100+
name: 'fix-handler-exclude',
101+
setup(build) {
102+
// Match an import called "./handler.js" and mark it as external
103+
build.onResolve({ filter: /^\.\/handler\.js$/ }, () => ({ external: true }));
104+
}
105+
}
106+
]
107+
};
108+
const buildOptionsRefServer = esbuildConfig
109+
? await esbuildConfig(defaultOptionsRefServer)
110+
: defaultOptionsRefServer;
111+
await esbuild.build(buildOptionsRefServer);
112+
86113
utils.log.minor('Prerendering static pages');
87114
await utils.prerender({
88115
dest: `${out}/prerendered`

packages/adapter-node/rollup.config.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import commonjs from '@rollup/plugin-commonjs';
33
import json from '@rollup/plugin-json';
44

55
export default [
6+
{
7+
input: 'src/handler.js',
8+
output: {
9+
file: 'files/handler.js',
10+
format: 'esm',
11+
sourcemap: true
12+
},
13+
plugins: [nodeResolve(), commonjs(), json()],
14+
external: ['../output/server/app.js', ...require('module').builtinModules]
15+
},
616
{
717
input: 'src/index.js',
818
output: {
@@ -11,7 +21,7 @@ export default [
1121
sourcemap: true
1222
},
1323
plugins: [nodeResolve(), commonjs(), json()],
14-
external: ['../output/server/app.js', './env.js', ...require('module').builtinModules]
24+
external: ['./handler.js', './env.js', ...require('module').builtinModules]
1525
},
1626
{
1727
input: 'src/shims.js',

packages/adapter-node/src/handler.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// TODO hardcoding the relative location makes this brittle
2+
import { init, render } from '../output/server/app.js'; // eslint-disable-line import/no-unresolved
3+
import { createPolkaHandler } from './polka-handler.js';
4+
5+
export function createHandler() {
6+
init();
7+
return createPolkaHandler({ render });
8+
}

packages/adapter-node/src/index.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
// TODO hardcoding the relative location makes this brittle
2-
import { init, render } from '../output/server/app.js'; // eslint-disable-line import/no-unresolved
31
import { path, host, port } from './env.js'; // eslint-disable-line import/no-unresolved
4-
import { createServer } from './server';
5-
6-
init();
7-
8-
const instance = createServer({ render });
2+
import { createHandler } from './handler.js';
3+
import { createServer } from 'http';
94

5+
const server = createServer(createHandler());
106
const listenOpts = { path, host, port };
11-
instance.listen(listenOpts, () => {
7+
server.listen(listenOpts, () => {
128
console.log(`Listening on ${path ? path : host + ':' + port}`);
139
});
1410

15-
export { instance };
11+
export { server };

packages/adapter-node/src/server.js renamed to packages/adapter-node/src/polka-handler.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const paths = {
1616
prerendered: join(__dirname, '/prerendered')
1717
};
1818

19-
export function createServer({ render }) {
19+
export function createPolkaHandler({ render }) {
2020
const prerendered_handler = fs.existsSync(paths.prerendered)
2121
? sirv(paths.prerendered, {
2222
etag: true,
@@ -39,7 +39,7 @@ export function createServer({ render }) {
3939
})
4040
: noop_handler;
4141

42-
const server = polka().use(
42+
const polka_instance = polka().use(
4343
compression({ threshold: 0 }),
4444
assets_handler,
4545
prerendered_handler,
@@ -74,5 +74,5 @@ export function createServer({ render }) {
7474
}
7575
);
7676

77-
return server;
77+
return polka_instance.handler;
7878
}

packages/adapter-node/tests/smoke.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import { test } from 'uvu';
2-
import { createServer } from '../src/server.js';
2+
import { createPolkaHandler } from '../src/polka-handler.js';
33
import * as assert from 'uvu/assert';
44
import fetch from 'node-fetch';
5+
import { createServer } from 'http';
56

67
const { PORT = 3000 } = process.env;
78
const DEFAULT_SERVER_OPTS = { render: () => {} };
89

910
function startServer(opts = DEFAULT_SERVER_OPTS) {
10-
const server = createServer(opts);
11+
const handler = createPolkaHandler(opts);
12+
1113
return new Promise((fulfil, reject) => {
14+
const server = createServer(handler);
1215
server.listen(PORT, (err) => {
1316
if (err) {
1417
reject(err);
@@ -21,14 +24,14 @@ function startServer(opts = DEFAULT_SERVER_OPTS) {
2124
test('starts a server', async () => {
2225
const server = await startServer();
2326
assert.ok('server started');
24-
server.server.close();
27+
server.close();
2528
});
2629

2730
test('serves a 404', async () => {
2831
const server = await startServer();
2932
const res = await fetch(`http://localhost:${PORT}/nothing`);
3033
assert.equal(res.status, 404);
31-
server.server.close();
34+
server.close();
3235
});
3336

3437
test('responses with the rendered status code', async () => {
@@ -43,7 +46,7 @@ test('responses with the rendered status code', async () => {
4346
});
4447
const res = await fetch(`http://localhost:${PORT}/wow`);
4548
assert.equal(res.status, 203);
46-
server.server.close();
49+
server.close();
4750
});
4851

4952
test('passes through umlaut as encoded path', async () => {
@@ -57,7 +60,7 @@ test('passes through umlaut as encoded path', async () => {
5760
});
5861
const res = await fetch(`http://localhost:${PORT}/%C3%BCber-uns`);
5962
assert.equal(await res.text(), '/%C3%BCber-uns');
60-
server.server.close();
63+
server.close();
6164
});
6265

6366
test.run();

0 commit comments

Comments
 (0)