Let's say you have a client/foo.html
file. You can visit /foo
in your browser and everything's great. Until one day, when you need to start displaying data from an API (or perhaps even a hosted database).
How will you do this? You're already fine with a plain old HTML file; you prefer Lancer over coverting everything to next.js/nuxt/etc. And doing it in a client-side JavaScript file means you lose SSR – and that may not even be an option if you can't expose API keys!
As it turns out, this is super easy to do in Lancer. Just add a foo.server.js
file to your project:
client/
├─ foo.html
├─ foo.server.js
...with this content:
export default async ({ locals }) => {
locals.myData = await fetchFromSomeApi()
}
and that's it! This file (specifically, the default export) will now run before every request made to /foo
, allowing you to use it in foo.html
:
<h1>Foo</h1>
<p>The weather today is {{myData.weather}}</p>
But wait – there's more. Anything you export from foo.server.js
is automagically available when you import it from a client-side js file:
/* client/bar.server.js */
export async function getSomeData() {
return await fetchFromSomeApi()
}
<!-- client/bar.html -->
<h1>Bar Example</h1>
<script src="/example.js"></script>
/* client/example.js */
import * as Rpc from './bar.server'
Rpc.getSomeData().then(x => console.log("Got data:", x))
Incredible, it works! But what's going on here?
Lancer is not importing server-side code into your client. Instead, it intercepts the import from './bar.server'
and generates a small RPC file it its place. As a result, when you call Rpc.getSomeData()
, you're not running server code; you're actually running a function that calls fetch
!
Not only does this reduce boilerplate to a bare minimum, it also allows you to re-use data fetching functions across page load (the default export function) and client JS.
Next: Universal JavaScript