DEV Community

Cover image for Calling an imported API at runtime in Astro
Cassidy Williams
Cassidy Williams

Posted on

Calling an imported API at runtime in Astro

Astro is a web framework that I like for a lot of reasons, but in particular I like that it runs as little JavaScript as possible (at runtime) by default. When I say "at runtime" I mean as the page is run in the browser, not when your site is built (I'm not going to get into the difference in this post as much, but you'll see why it matters in a sec).

Anyway, the lack of JavaScript at runtime makes for some really quick, lightweight sites that have a lot of power behind them, but sometimes you want to use some good ol' scripts.

An Astro component has some frontmatter at the top, kind of like Markdown, where you can import different components and scripts, and then a JSX-like syntax below:

// index.astro

---
import BaseLayout from "./BaseLayout.astro";

let userName = "pretend I'm calling a function or something here";
---

<BaseLayout>
    <h1>Hello, {userName ? userName : "world"}!</h1>
</BaseLayout>

Enter fullscreen mode Exit fullscreen mode

When you want to run a script on this page, you could import a component, or just add some <script> tags to handle it.

But, what if you want to import an API?

// index.astro

---
import BaseLayout from "./BaseLayout.astro";

let userName = "pretend I'm calling a function or something here";

import api from "whatever";
---

<script>
const res = await api(); // api is not defined
</script>

<BaseLayout>
    <h1>Hello, {userName ? userName : "world"}!</h1>
</BaseLayout>

Enter fullscreen mode Exit fullscreen mode

Unfortunately, this won't work! Because the frontmatter imports and the script is called at different points, the api function there is considered undefined when you call it.

But, have no fear, this is actually a really quick fix:

// index.astro

---
import BaseLayout from "./BaseLayout.astro";

let userName = "pretend I'm calling a function or something here";
---

<script>
import api from "whatever";
const res = await api(); // api IS defined!
</script>

<BaseLayout>
    <h1>Hello, {userName ? userName : "world"}!</h1>
</BaseLayout>

Enter fullscreen mode Exit fullscreen mode

You just have to import your API at the <script> level, and you're golden!

Did I write an entire blog post for a one-line fix? Yes.

I keep forgetting this is a thing and I figure a blog will help me actually remember this when I inevitably run into it again. Classic.

Happy trails!

Top comments (7)

Collapse
 
efpage profile image
Eckehard

But what happens, if you want your script to run across multiple components? What about persistence of state, if you have multiple scripts running that way?

Collapse
 
cassidoo profile image
Cassidy Williams

It depends on how you want to organize your components! The Astro team recommends using Nano Stores for that.

Collapse
 
kamal250 profile image
kamal250

Here is an example of Nano Stores - github.com/kamal250/hcg-levels/blo... while I was playing with Astro.

Collapse
 
johnwritescode_ profile image
ツ John

Nice tip!

I built findcool.tools with Astro.

Pretty nice experience

Collapse
 
efpage profile image
Eckehard

Just for better understanding. Assume, you run a script on the initial page that defines a variable, let say:

<script>
   let myVar = 10
<\script>
Enter fullscreen mode Exit fullscreen mode

would this value be available in other modules?

Collapse
 
dansasser profile image
dansasser

So I have an API that takes care of all my database logic. I have been using PHP to communicate with that API and then server side render my pages. I just started using Astro and was about to start working on this part. Are you able to respond as to why the JavaScript does not go in the frontmatter but after it?

Collapse
 
cassidoo profile image
Cassidy Williams

JavaScript can go in the frontmatter, but that JS is run at build time/server side. Any JS that is in a script outside of the frontmatter is run client side!