Skip to content

Commit

Permalink
[feat] expose handler to allow use in custom server
Browse files Browse the repository at this point in the history
  • Loading branch information
matths authored and benmccann committed Aug 20, 2021
1 parent 23ff8b5 commit 1b10a92
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 21 deletions.
28 changes: 28 additions & 0 deletions documentation/faq/80-integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ onMount(() => {

Put the code to query your database in [endpoints](/docs#routing-endpoints) - don't query the database in .svelte files. You can create a `db.js` or similar that sets up a connection immediately and makes the client accessible throughout the app as a singleton. You can execute any one-time setup code in `hooks.js` and import your database helpers into any endpoint that needs them.

### How use middleware?

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:

```js
const myPlugin = {
name: 'log-request-middleware',
configureServer(server) {
server.middlewares.use((req, res, next) => {
console.log(`Got request ${req.url}`);
next();
})
}
}

/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
target: '#svelte',
vite: {
plugins: [ myPlugin ]
}
}
};

export default config;
```

### Does it work with Yarn 2?

Sort of. The Plug'n'Play feature, aka 'pnp', is broken (it deviates from the Node module resolution algorithm, and [doesn't yet work with native JavaScript modules](https://github.com/yarnpkg/berry/issues/638) which SvelteKit — along with an [increasing number of packages](https://blog.sindresorhus.com/get-ready-for-esm-aa53530b3f77) — uses). You can use `nodeLinker: 'node-modules'` in your [`.yarnrc.yml`](https://yarnpkg.com/configuration/yarnrc#nodeLinker) file to disable pnp, but it's probably easier to just use npm or [pnpm](https://pnpm.io/), which is similarly fast and efficient but without the compatibility headaches.
32 changes: 32 additions & 0 deletions packages/adapter-node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,38 @@ HOST=127.0.0.1 PORT=4000 node build

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

## Middleware

The adapter exports a [Express](https://github.com/expressjs/expressjs.com) /
[Polka](https://github.com/lukeed/polka) compatible middleware function `(req, res, next) => {}`
as well as a reference server implementation using this with a plain node http server.

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`.

```
import { createHandler } from './build/handler.js';
import express from 'express';
const app = express();
var myMiddleware = function (req, res, next) {
console.log('Hello world!');
next();
};
app.use(myMiddleware);
app.get('/no-svelte', (req, res) => {
res.send('This is not Svelte!')
});
// SvelteKit
app.get('*', createHandler());
app.listen(3000)
```

## Advanced Configuration

### esbuild
Expand Down
33 changes: 30 additions & 3 deletions packages/adapter-node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function ({
await compress(static_directory);
}

utils.log.minor('Building server');
utils.log.minor('Building SvelteKit request handler');
const files = fileURLToPath(new URL('./files', import.meta.url));
utils.copy(files, '.svelte-kit/node');
writeFileSync(
Expand All @@ -66,10 +66,11 @@ export default function ({
port_env
)}] || (!path && 3000);`
);

/** @type {BuildOptions} */
const defaultOptions = {
entryPoints: ['.svelte-kit/node/index.js'],
outfile: join(out, 'index.js'),
entryPoints: ['.svelte-kit/node/handler.js'],
outfile: join(out, 'handler.js'),
bundle: true,
external: Object.keys(JSON.parse(readFileSync('package.json', 'utf8')).dependencies || {}),
format: 'esm',
Expand All @@ -83,6 +84,32 @@ export default function ({
const buildOptions = esbuildConfig ? await esbuildConfig(defaultOptions) : defaultOptions;
await esbuild.build(buildOptions);

utils.log.minor('Building SvelteKit reference server');
/** @type {BuildOptions} */
const defaultOptionsRefServer = {
entryPoints: ['.svelte-kit/node/index.js'],
outfile: join(out, 'index.js'),
bundle: true,
external: ['./handler.js'], // does not work, eslint does not exclude handler from target
format: 'esm',
platform: 'node',
target: 'node12',
// external exclude workaround, see https://github.com/evanw/esbuild/issues/514
plugins: [
{
name: 'fix-handler-exclude',
setup(build) {
// Match an import called "./handler.js" and mark it as external
build.onResolve({ filter: /^\.\/handler\.js$/ }, () => ({ external: true }));
}
}
]
};
const buildOptionsRefServer = esbuildConfig
? await esbuildConfig(defaultOptionsRefServer)
: defaultOptionsRefServer;
await esbuild.build(buildOptionsRefServer);

utils.log.minor('Prerendering static pages');
await utils.prerender({
dest: `${out}/prerendered`
Expand Down
12 changes: 11 additions & 1 deletion packages/adapter-node/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';

export default [
{
input: 'src/handler.js',
output: {
file: 'files/handler.js',
format: 'esm',
sourcemap: true
},
plugins: [nodeResolve(), commonjs(), json()],
external: ['../output/server/app.js', ...require('module').builtinModules]
},
{
input: 'src/index.js',
output: {
Expand All @@ -11,7 +21,7 @@ export default [
sourcemap: true
},
plugins: [nodeResolve(), commonjs(), json()],
external: ['../output/server/app.js', './env.js', ...require('module').builtinModules]
external: ['./handler.js', './env.js', ...require('module').builtinModules]
},
{
input: 'src/shims.js',
Expand Down
8 changes: 8 additions & 0 deletions packages/adapter-node/src/handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// TODO hardcoding the relative location makes this brittle
import { init, render } from '../output/server/app.js'; // eslint-disable-line import/no-unresolved
import { createPolkaHandler } from './polka-handler.js';

export function createHandler() {
init();
return createPolkaHandler({ render });
}
14 changes: 5 additions & 9 deletions packages/adapter-node/src/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
// TODO hardcoding the relative location makes this brittle
import { init, render } from '../output/server/app.js'; // eslint-disable-line import/no-unresolved
import { path, host, port } from './env.js'; // eslint-disable-line import/no-unresolved
import { createServer } from './server';

init();

const instance = createServer({ render });
import { createHandler } from './handler.js';
import { createServer } from 'http';

const server = createServer(createHandler());
const listenOpts = { path, host, port };
instance.listen(listenOpts, () => {
server.listen(listenOpts, () => {
console.log(`Listening on ${path ? path : host + ':' + port}`);
});

export { instance };
export { server };
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const paths = {
prerendered: join(__dirname, '/prerendered')
};

export function createServer({ render }) {
export function createPolkaHandler({ render }) {
const prerendered_handler = fs.existsSync(paths.prerendered)
? sirv(paths.prerendered, {
etag: true,
Expand All @@ -38,7 +38,7 @@ export function createServer({ render }) {
})
: noop_handler;

const server = polka().use(
const polka_instance = polka().use(
compression({ threshold: 0 }),
assets_handler,
prerendered_handler,
Expand Down Expand Up @@ -73,5 +73,5 @@ export function createServer({ render }) {
}
);

return server;
return polka_instance.handler;
}
13 changes: 8 additions & 5 deletions packages/adapter-node/tests/smoke.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { test } from 'uvu';
import { createServer } from '../src/server.js';
import { createPolkaHandler } from '../src/polka-handler.js';
import * as assert from 'uvu/assert';
import fetch from 'node-fetch';
import { createServer } from 'http';

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

function startServer(opts = DEFAULT_SERVER_OPTS) {
const server = createServer(opts);
const handler = createPolkaHandler(opts);

return new Promise((fulfil, reject) => {
const server = createServer(handler);
server.listen(PORT, (err) => {
if (err) {
reject(err);
Expand All @@ -21,14 +24,14 @@ function startServer(opts = DEFAULT_SERVER_OPTS) {
test('starts a server', async () => {
const server = await startServer();
assert.ok('server started');
server.server.close();
server.close();
});

test('serves a 404', async () => {
const server = await startServer();
const res = await fetch(`http://localhost:${PORT}/nothing`);
assert.equal(res.status, 404);
server.server.close();
server.close();
});

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

test('passes through umlaut as encoded path', async () => {
Expand Down

0 comments on commit 1b10a92

Please sign in to comment.