Skip to content

Latest commit

 

History

History
104 lines (85 loc) · 4.72 KB

how.md

File metadata and controls

104 lines (85 loc) · 4.72 KB

How pocket works

From a high-level view pocket looks like a classic server-side web framework. I handles requests and renders HTML-templates. The main difference compared to other frameworks is that pocket is able to use the same code to handle requests directly in a service worker within the user's browser.

HTML

Pocket (for now) has its own HTML templating engine. Pocket exports a tagged template function called html. The name enables native html code highlighting in IDEs such as vscode (e.G. with the lit-html extension).

html returns instances of Html.

html is invoked like

html`
 <main>
   <h1>${title}</h1>
   <p>${content}</p>
 </main>
`

Title and content will be html-escaped. To pass raw values one would have to use html.raw:

html`
  <div>${html.raw(myRenderedHTML)}</div>
`

Interpolated values can be string, number, null (which will render nothing), false (which will also render nothing), more Html, an array of all those values or, notably, a Promise<string>, Promise<Html> etc. Html renders not to a string but to a bytestream and resolves any passed in promises so streaming rendering is native to pocket using the browser's native streaming rendering capabilities.

Learn more

Streaming

Streaming rendering is the default with pocket. All pages are rendered as a stream so the browser can begin rendering while content is still streaming. Whenever pocket encounters a promise as an interpolated value, it will await that promise before it continues rendering. This means that

function body() {
  return html`
    <div>
      ${(async () => {
        const result = await longRunningOperation()
        return result
      })()}
    </div>
  `
}

will return the response headers and <div> to the client, then wait for longRunningOperation and then return the rest of the response.

Routing

Pocket uses folder-based routing similar to next.js (because I really like that about next.js). The router starts at the root of the routes/ folder. Put a file called route.ts in there. That's the one that will handle requests to /. Subfolders are used for different paths, e.G. /contact/route.ts will handle requests to /contact etc.

Within your route.ts you'll typically export a couple of functions with special names:

export function body(context: RouteBodyContext): Html

returns the page body. The RouteBodyContext provides access to the Request-object as wenn as some props that we'll come back to later on.

If no other export is provided, this is sufficient to render an HTML-page.

export function head(context: RouteHeadContext): Html

returns HTML that will be appended to the <head> of the resulting page which is useful to add meta tags like titles, descriptions etc.

export function get(context: RouteHandlerContext): Response

can be used to manually handle the request. The RouteHandlerContext includes a render-function to render the actual template (as defined by the head and body functions)

get allows modifying things like cookies, headers etc. and can work standalone (without a body) if you need to have an API-style route that returns something that's not HTML.

post, delete, put etc. work the same way as get.

Learn more about the filesystem API Learn more about the route APIs

Layouts

Pocket has native support for layouts. Any folder within routes/ can, in addition to the route.ts, also contain a layout.ts with body and head exports very similar to the ones in route.ts. Routes are wrapped in every layout from its own folder up the tree to the routes/ folder.

This means that in

routes/
 | - layout.ts
 | - app/
 |    | - layout.ts
 | .  | - contact/
 | .  |    | - layout.ts
 | .  | .  | - route.ts
 | - misc/
 | .  | - layout.ts

routes/app/contact/route.ts would match routes/layout.ts, routes/app/layout.ts and routes/app/contact/layout.ts but not routes/misc/layout.ts.

Learn more

Server-Side

When run as a standalone server or in dev mode, user code is executed in Vercel's edge-runtime. This means that roughly the same APIs as in a service worker are available to it (see APIs).

Vercel

One huge advantage of limiting the server side to APIs available in a browser context is that node is not required for a pocket implementation to run. Pocket does actually leverage this by providing a build option (pocket build --output vercel) to output a vercel-compatible file-system format. On vercel, pocket runs completely in vercel's edge-lambdas.