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

Allow passing platform context from adapters #3429

Merged
merged 45 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
d574cb6
Can pass locals at app.render
fezproof Jan 20, 2022
33d4737
Updated cloudflare workers to pass in env to locals
fezproof Jan 20, 2022
56bfe37
Fixed other adapters for new API
fezproof Jan 20, 2022
8309cd7
Fixed naming in app types to be more consistent
fezproof Jan 20, 2022
bb67019
Merge branch 'master' into locals-from-adapter
Rich-Harris Jan 20, 2022
9f95150
Updated to use meta in stead of polluting locals
fezproof Jan 21, 2022
4f4247c
updated adapter cloudflare to use meta
fezproof Jan 21, 2022
fd9a6f5
Added changesets
fezproof Jan 21, 2022
df36652
Formated and linted
fezproof Jan 21, 2022
6d34a4c
Updated types to include Meta consistently
fezproof Jan 21, 2022
2d887a3
Updated documentation to include information about the meta object
fezproof Jan 21, 2022
bcd3c8d
Fixed type issue in hooks
fezproof Jan 21, 2022
36b139a
Merge branch 'master' of https://github.com/sveltejs/kit into locals-…
fezproof Jan 21, 2022
eb977ea
Updated naming to platform
fezproof Jan 21, 2022
a9d3fa9
Added a way to get the original request from the node adapter
fezproof Jan 21, 2022
8745a71
Made platform Readonly in RequestEvent
fezproof Jan 21, 2022
40f8cd7
Fixed typo in docs
fezproof Jan 21, 2022
201e595
Update documentation/docs/01-routing.md
fezproof Jan 22, 2022
0bce46d
Fixed bad spaces
fezproof Jan 22, 2022
e95e1b3
Merge branch 'master' into locals-from-adapter
fezproof Jan 22, 2022
741eda4
Fixed merge issues
fezproof Jan 22, 2022
19d5039
Merge branch 'master' into locals-from-adapter
ignatiusmb Jan 25, 2022
2f6cb31
rerun flakey tests
fezproof Jan 25, 2022
c7b10a1
rerun flakey tests 2
fezproof Jan 25, 2022
e4a1047
Update documentation/docs/10-adapters.md grammer
fezproof Jan 25, 2022
d91beaf
rerun flakey tests 3
fezproof Jan 25, 2022
22c2e6f
Merge branch 'locals-from-adapter' of https://github.com/fezproof/kit…
fezproof Jan 25, 2022
492809d
rerun flakey tests 4
fezproof Jan 25, 2022
e4c7be7
Changed node handler to pass in `req`
fezproof Jan 26, 2022
bb2ddaa
Updated cloudflare readme
fezproof Jan 26, 2022
e5fd336
Renamed to request
fezproof Jan 26, 2022
4e96cf1
Use Record<string, any>
fezproof Jan 26, 2022
9fc934b
rerun flakey tests 5
fezproof Jan 26, 2022
72459fe
change API to app.render(request, opts)
Rich-Harris Jan 27, 2022
54de5bf
update adapters
Rich-Harris Jan 27, 2022
d417d00
use platform.req instead of platform.request — more idiomatic
Rich-Harris Jan 27, 2022
e68d469
remove changesets for unaffected adapters
Rich-Harris Jan 27, 2022
358c747
tweak changesets
Rich-Harris Jan 27, 2022
2a4b54b
remove platform stuff from routing section
Rich-Harris Jan 27, 2022
9d57b6c
tweak adapter docs
Rich-Harris Jan 27, 2022
a3121b8
Merge branch 'master' into locals-from-adapter
Rich-Harris Jan 27, 2022
6e90095
Fix documentation/docs/04-hooks.md spaces
fezproof Jan 27, 2022
9ca9144
remove changes to adapter-node
Rich-Harris Jan 27, 2022
5d3c3ae
create-svelte is unchanged
Rich-Harris Jan 27, 2022
983ec4f
simplify docs
Rich-Harris Jan 27, 2022
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
6 changes: 6 additions & 0 deletions .changeset/cyan-numbers-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sveltejs/adapter-cloudflare': patch
'@sveltejs/adapter-node': patch
---

Pass `platform` object to SvelteKit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to say in the changelog what it contains? If so, we're presumably want separate changesets for the Cloudflare and Node adapters.

6 changes: 6 additions & 0 deletions .changeset/many-mayflies-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'create-svelte': patch
'@sveltejs/kit': patch
---

Allow adapters to pass in `platform` object
16 changes: 13 additions & 3 deletions documentation/docs/01-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ Endpoints are modules written in `.js` (or `.ts`) files that export functions co
// Declaration types for Endpoints
// * declarations that are not exported are for internal use

export interface RequestEvent<Locals = Record<string, any>> {
export interface RequestEvent<Locals = Record<string, any>, Platform = Record<string, any>> {
request: Request;
url: URL;
params: Record<string, string>;
locals: Locals;
platform: Platform;
}

type Body = JSONString | Uint8Array | ReadableStream | stream.Readable;
Expand All @@ -69,8 +70,13 @@ type MaybePromise<T> = T | Promise<T>;
interface Fallthrough {
fallthrough: true;
}
export interface RequestHandler<Locals = Record<string, any>, Output extends Body = Body> {
(event: RequestEvent<Locals>): MaybePromise<

export interface RequestHandler<
Locals = Record<string, any>,
Platform = Record<string, any>,
Output extends Body = Body
> {
(event: RequestEvent<Locals, Platform>): MaybePromise<
Either<Response | EndpointOutput<Output>, Fallthrough>
>;
}
Expand Down Expand Up @@ -194,6 +200,10 @@ A route can have multiple dynamic parameters, for example `src/routes/[category]

> `src/routes/a/[...rest]/z.svelte` will match `/a/z` as well as `/a/b/z` and `/a/b/c/z` and so on. Make sure you check that the value of the rest parameter is valid.

#### Platform object

Some adapters may pass in some extra context when calling your application. They do this by setting the `platform` object, that can be accessed in both route handlers and hooks ([detailed later](#hooks)). It is up to the adapter to decide what is passed in.

#### Fallthrough routes

Finally, if you have multiple routes that match a given path, SvelteKit will try each of them until one responds. For example if you have these routes...
Expand Down
23 changes: 14 additions & 9 deletions documentation/docs/04-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,22 @@ If unimplemented, defaults to `({ event, resolve }) => resolve(event)`.
// everything else must be a type of string
type ResponseHeaders = Record<string, string | string[]>;

export interface RequestEvent<Locals = Record<string, any>> {
export interface RequestEvent<Locals = Record<string, any>, Platform = Record<string, any>> {
request: Request;
url: URL;
params: Record<string, string>;
locals: Locals;
platform: Platform;
}

export interface ResolveOpts {
ssr?: boolean;
}

export interface Handle<Locals = Record<string, any>> {
export interface Handle<Locals = Record<string, any>, Platform = Record<string, any>> {
(input: {
event: RequestEvent<Locals>;
resolve(event: RequestEvent<Locals>, opts?: ResolveOpts): MaybePromise<Response>;
event: RequestEvent<Locals, Platform>;
resolve(event: RequestEvent<Locals, Platform>, opts?: ResolveOpts): MaybePromise<Response>;
}): MaybePromise<Response>;
}
```
Expand Down Expand Up @@ -84,8 +85,8 @@ If unimplemented, SvelteKit will log the error with default formatting.

```ts
// Declaration types for handleError hook
export interface HandleError<Locals = Record<string, any>> {
(input: { error: Error & { frame?: string }; event: RequestEvent<Locals> }): void;
export interface HandleError<Locals = Record<string, any>, Platform = Record<string, any>> {
(input: { error: Error & { frame?: string }; event: RequestEvent<Locals, Platform> }): void;
}
```

Expand All @@ -107,8 +108,12 @@ If unimplemented, session is `{}`.

```ts
// Declaration types for getSession hook
export interface GetSession<Locals = Record<string, any>, Session = any> {
(event: RequestEvent<Locals>): Session | Promise<Session>;
export interface GetSession<
Locals = Record<string, any>,
Platform = Record<string, any>,
Session = any
> {
(event: RequestEvent<Locals, Platform>): MaybePromise<Session>;
}
```

Expand All @@ -125,7 +130,7 @@ export function getSession(event) {
email: event.locals.user.email,
avatar: event.locals.user.avatar
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be changed back to a tab

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what is causing this one, can't find a setting that would do it. Fixed now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code blocks in markdown files aren't linted. If it's just a regular .js file it should be handled though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still doesn't look fixed.

fezproof marked this conversation as resolved.
Show resolved Hide resolved
: {};
}
```
Expand Down
1 change: 1 addition & 0 deletions documentation/docs/10-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Within the `adapt` method, there are a number of things that an adapter should d
- Imports `App` from `${builder.getServerDirectory()}/app.js`
- Instantiates the app with a manifest generated with `builder.generateManifest({ relativePath })`
- Listens for requests from the platform, converts them to a standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) if necessary, calls the `render` function to generate a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) and responds with it
- `render` optionally accepts a `platform` object, which allows passing of environment specific values and functions. The `platform` object will be accessible from the handle hook and any API endpoints in the application
- Globally shims `fetch` to work on the target platform, if necessary. SvelteKit provides a `@sveltejs/kit/install-fetch` helper for platforms that can use `node-fetch`
- Bundle the output to avoid needing to install dependencies on the target platform, if necessary
- Put the user's static files and the generated JS/CSS in the correct location for the target platform
Expand Down
22 changes: 22 additions & 0 deletions packages/adapter-cloudflare/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,28 @@ When configuring your project settings, you must use the following settings:

> **Important:** You need to add a `NODE_VERSION` environment variable to both the "production" and "preview" environments. You can add this during project setup or later in the Pages project settings. SvelteKit requires Node `14.13` or later, so you should use `14` or `16` as the `NODE_VERSION` value.

## Platform

The handler that is outputed by the build will pass in the env object from cloudflare pages into the `platform` variable. You can read more about what env is [here](https://developers.cloudflare.com/pages/platform/functions#adding-bindings). This allows access to KV and Durable Objects from any hook or endpoint handler.

```ts
interface Env {
COUNTER: DurableObjectNamespace;
}

interface Locals {
// Locals set in locals hook
}

interface Platform {
env: Env;
}

export async function post<Locals, Platform>({ request, platform }) {
const counter = platform.env.COUNTER.idFromName('A');
}
```

## Changelog

[The Changelog for this package is available on GitHub](https://github.com/sveltejs/kit/blob/master/packages/adapter-cloudflare/CHANGELOG.md).
2 changes: 1 addition & 1 deletion packages/adapter-cloudflare/files/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default {

// dynamically-generated pages
try {
return await app.render(req);
return await app.render(req, { platform: { env } });
} catch (e) {
return new Response('Error rendering route: ' + (e.message || e.toString()), { status: 500 });
}
Expand Down
21 changes: 21 additions & 0 deletions packages/adapter-node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ MY_ORIGINURL=https://my.site \
node build
```

## Platform

The handler that is outputed by the build will pass in the original Node request object into the `platform` variable. This means that it can be accessed from all hooks and endpoint handlers. This is particually useful if previous middleware has added values to the request object.

```ts
import { Request } from 'polka';

interface Locals {
// Locals set in locals hook
}

interface Platform {
req: Request;
}

// If using cookie-parser middleware
export async function post<Locals, Platform>({ request, platform }) {
const cookies = platform.req.cookies;
}
```

## Custom server

The adapter creates two files in your build directory — `index.js` and `handler.js`. Running `index.js` — e.g. `node build`, if you use the default build directory — will start a server on the configured port.
Expand Down
2 changes: 1 addition & 1 deletion packages/adapter-node/src/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const ssr = async (req, res) => {
return res.end(err.reason || 'Invalid request body');
}

setResponse(res, await app.render(request));
setResponse(res, await app.render(request, { platform: { req } }));
};

/** @param {import('polka').Middleware[]} handlers */
Expand Down
7 changes: 2 additions & 5 deletions packages/kit/src/core/build/build_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,12 @@ export class App {
};
}

render(request, {
prerender
} = {}) {
render(request, options = {}) {
if (!(request instanceof Request)) {
throw new Error('The first argument to app.render must be a Request object. See https://github.com/sveltejs/kit/pull/3384 for details');
}

return respond(request, this.options, { prerender });
return respond(request, this.options, options);
}
}
`;
Expand Down Expand Up @@ -169,7 +167,6 @@ export async function build_server(
return relative_file[0] === '.' ? relative_file : `./${relative_file}`;
};

// prettier-ignore
fs.writeFileSync(
input.app,
template({
Expand Down
3 changes: 2 additions & 1 deletion packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export async function respond(request, options, state = {}) {
request,
url,
params: {},
locals: {}
locals: {},
platform: state.platform
};

// TODO remove this for 1.0
Expand Down
10 changes: 7 additions & 3 deletions packages/kit/types/app.d.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { PrerenderOptions, SSRNodeLoader, SSRRoute } from './internal';

export interface RequestOptions<Platform = Record<string, any>> {
platform?: Platform;
}

export class App {
constructor(manifest: SSRManifest);
render(request: Request): Promise<Response>;
render(request: Request, options?: RequestOptions): Promise<Response>;
}

export class InternalApp extends App {
render(
request: Request,
options?: {
prerender: PrerenderOptions;
options?: RequestOptions & {
prerender?: PrerenderOptions;
}
): Promise<Response>;
}
Expand Down
8 changes: 6 additions & 2 deletions packages/kit/types/endpoint.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ export interface Fallthrough {
fallthrough: true;
}

export interface RequestHandler<Locals = Record<string, any>, Output extends Body = Body> {
(event: RequestEvent<Locals>): MaybePromise<
export interface RequestHandler<
Locals = Record<string, any>,
Platform = Record<string, any>,
Output extends Body = Body
> {
(event: RequestEvent<Locals, Platform>): MaybePromise<
Either<Response | EndpointOutput<Output>, Fallthrough>
>;
}
30 changes: 19 additions & 11 deletions packages/kit/types/hooks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,47 @@ import { MaybePromise } from './helper';

export type StrictBody = string | Uint8Array;

export interface RequestEvent<Locals = Record<string, any>> {
export interface RequestEvent<Locals = Record<string, any>, Platform = Record<string, any>> {
request: Request;
url: URL;
params: Record<string, string>;
locals: Locals;
platform: Readonly<Platform>;
}

export interface GetSession<Locals = Record<string, any>, Session = any> {
(event: RequestEvent<Locals>): MaybePromise<Session>;
export interface GetSession<
Locals = Record<string, any>,
Platform = Record<string, any>,
Session = any
> {
(event: RequestEvent<Locals, Platform>): MaybePromise<Session>;
}

export interface ResolveOpts {
ssr?: boolean;
}

export interface Handle<Locals = Record<string, any>> {
export interface Handle<Locals = Record<string, any>, Platform = Record<string, any>> {
(input: {
event: RequestEvent<Locals>;
resolve(event: RequestEvent<Locals>, opts?: ResolveOpts): MaybePromise<Response>;
event: RequestEvent<Locals, Platform>;
resolve(event: RequestEvent<Locals, Platform>, opts?: ResolveOpts): MaybePromise<Response>;
}): MaybePromise<Response>;
}

// internally, `resolve` could return `undefined`, so we differentiate InternalHandle
// from the public Handle type
export interface InternalHandle<Locals = Record<string, any>> {
export interface InternalHandle<Locals = Record<string, any>, Platform = Record<string, any>> {
(input: {
event: RequestEvent<Locals>;
resolve(event: RequestEvent<Locals>, opts?: ResolveOpts): MaybePromise<Response | undefined>;
event: RequestEvent<Locals, Platform>;
resolve(
event: RequestEvent<Locals, Platform>,
opts?: ResolveOpts
): MaybePromise<Response | undefined>;
}): MaybePromise<Response | undefined>;
}

export interface HandleError<Locals = Record<string, any>> {
(input: { error: Error & { frame?: string }; event: RequestEvent<Locals> }): void;
export interface HandleError<Locals = Record<string, any>, Platform = Record<string, any>> {
(input: { error: Error & { frame?: string }; event: RequestEvent<Locals, Platform> }): void;
}

export interface ExternalFetch {
Expand Down
1 change: 1 addition & 0 deletions packages/kit/types/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export interface SSRRenderOptions {
export interface SSRRenderState {
fetched?: string;
initiator?: SSRPage | null;
platform?: any;
prerender?: PrerenderOptions;
fallback?: string;
}
Expand Down