Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add serverFetch hook #1465

Merged
merged 10 commits into from
Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions documentation/docs/04-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,26 @@ export function getSession(request) {
```

> `session` must be serializable, which means it must not contain things like functions or custom classes, just built-in JavaScript data types

### serverFetch

This function provides possibilities to replace `fetch` on the server-side for the external API requests. It's beneficial if you want to retrieve resources differently from what it is doing on the browser. For example, if you want to change URL or headers for server-side only.

```ts
type ServerFetch = (req: Request) => Promise<Response>;
```

```js
/** @type {import('@sveltejs/kit').ServerFetch} */
export async function serverFetch(request) {
let newRequest = request;
if (request.url.startsWith('https://my.site/api')) {
newRequest = new Request(
request.url.replace('https://my.site/api', 'http://localhost:9999/'),
request,
);
}

return fetch(newRequest);
}
```
3 changes: 2 additions & 1 deletion packages/kit/src/core/build/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,8 @@ async function build_server(
// named imports without triggering Rollup's missing import detection
const get_hooks = hooks => ({
getSession: hooks.getSession || (() => ({})),
handle: hooks.handle || (({ request, resolve }) => resolve(request))
handle: hooks.handle || (({ request, resolve }) => resolve(request)),
serverFetch: hooks.serverFetch || fetch
});

const module_lookup = {
Expand Down
3 changes: 2 additions & 1 deletion packages/kit/src/core/dev/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ class Watcher extends EventEmitter {
},
hooks: {
getSession: hooks.getSession || (() => ({})),
handle: hooks.handle || (({ request, resolve }) => resolve(request))
handle: hooks.handle || (({ request, resolve }) => resolve(request)),
serverFetch: hooks.serverFetch || fetch
},
hydrate: this.config.kit.hydrate,
paths: this.config.kit.paths,
Expand Down
3 changes: 2 additions & 1 deletion packages/kit/src/runtime/server/page/load_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ export async function load_node({

if (/^[a-zA-Z]+:/.test(url)) {
// external fetch
stalkerg marked this conversation as resolved.
Show resolved Hide resolved
response = await fetch(url, /** @type {RequestInit} */ (opts));
const request = new Request(url, /** @type {RequestInit} */ (opts));
dummdidumm marked this conversation as resolved.
Show resolved Hide resolved
response = await options.hooks.serverFetch.call(null, request);
} else {
const [path, search] = url.split('?');

Expand Down
13 changes: 13 additions & 0 deletions packages/kit/test/apps/basics/src/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,16 @@ export async function handle({ request, resolve }) {
};
}
}

/** @type {import('@sveltejs/kit').ServerFetch} */
export async function serverFetch(request) {
let newRequest = request;
if (request.url.endsWith('/server-fetch-request.json')) {
newRequest = new Request(
request.url.replace('/server-fetch-request.json', '/server-fetch-request2.json'),
request
);
}

return fetch(newRequest);
}
38 changes: 38 additions & 0 deletions packages/kit/test/apps/basics/src/routes/load/_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,44 @@ export default function (test, is_dev) {
server.close();
});

test('handles external api', '/load', async ({ base, page }) => {
const port = await ports.find(4000);

const files_names = ['/server-fetch-request.json', '/server-fetch-request2.json'];

let times_responded = 0;
let expect_answer = 42;

const server = http.createServer(async (req, res) => {
if (files_names.includes(req.url)) {
times_responded += 1;

res.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'content-type': 'application/json'
});

if (req.url === files_names[0]) {
res.end(JSON.stringify({ answer: expect_answer }));
} else {
expect_answer = 43;
res.end(JSON.stringify({ answer: expect_answer }));
}
}
});

await new Promise((fulfil) => {
server.listen(port, () => fulfil());
});

await page.goto(`${base}/load/server-fetch-request?port=${port}`);

assert.equal(times_responded, 1);
assert.equal(await page.textContent('h1'), `the answer is ${expect_answer}`);

server.close();
});

test(
'makes credentialed fetches to endpoints by default',
'/load',
Expand Down
1 change: 1 addition & 0 deletions packages/kit/test/apps/basics/src/routes/load/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
<a href="/load/fetch-credentialed">fetch credentialed</a>
<a href="/load/large-response">large response</a>
<a href="/load/raw-body">raw body</a>
<a href="/load/server-fetch-request">server fetch request</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script context="module">
/** @type {import('@sveltejs/kit').Load} */
export async function load({ page, fetch }) {
const url = `http://localhost:${page.query.get('port')}/server-fetch-request.json`;

// @ts-ignore
const res = await fetch(url);
const { answer } = await res.json();
return {
props: { answer }
};
}
</script>

<script>
/** @type {number} */
export let answer;
</script>

<h1>the answer is {answer}</h1>
2 changes: 2 additions & 0 deletions packages/kit/types/hooks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ export type Handle<Locals = Record<string, any>> = (input: {
request: ServerRequest<Locals>;
resolve: (request: ServerRequest<Locals>) => MaybePromise<ServerResponse>;
}) => MaybePromise<ServerResponse>;

export type ServerFetch = (req: Request) => Promise<Response>;
5 changes: 3 additions & 2 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export {
GetSession,
Handle,
ServerRequest as Request,
ServerResponse as Response
} from './hooks';
ServerResponse as Response,
ServerFetch
} from './hooks';
3 changes: 2 additions & 1 deletion packages/kit/types/internal.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Load } from './page';
import { Incoming, GetSession, Handle, ServerResponse } from './hooks';
import { Incoming, GetSession, Handle, ServerResponse, ServerFetch } from './hooks';
import { RequestHandler } from './endpoint';

type PageId = string;
Expand Down Expand Up @@ -111,6 +111,7 @@ export type SSRManifest = {
export type Hooks = {
getSession?: GetSession;
handle?: Handle;
serverFetch?: ServerFetch;
};

export type SSRNode = {
Expand Down