Guides

Concepts

Server Side API Calls

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>

Usage in Client-Side JavaScript

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