Astro JS non-HTML Routes: File & Resource API
Tags: astro, api, javascript
Published: 2022-10-05 09:54:44

---
title: "Astro JS non-HTML Routes: File & Resource API"
published: "true"
description: "Astro JS non-HTML routes: how you can serve resources and files like PDFs and JSON data alongside your Astro site’s static HTML pages."
tags: "astro, api, javascript"
canonical_url: "https://rodneylab.com/astro-js-non-html-routes/"
cover_image: "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tl5ao6qfen3mu8jhatjr.png"
---

## 🐝 Astro doesn't Just do HTML!

In this post on Astro JS non-HTML routes, we see you can create **file routes** or **resource routes** in Astro.  Astro specialises in delivering **content sites** and doing so quickly.  However that content does not have to be just HTML pages.  In fact, we see a couple of examples of Astro non-HTML routes. In the first, we **generate and serve a PDF** file from an **API route**. Meanwhile in the second example, we create some **JSON data** (again from a resource route).  You could extend this to create a JSON API. Although our examples focus on static Astro sites, we see the techniques can be adapted to **work on Astro SSR sites too**.  On SSR you can push the boat out, responding to Webhooks as well as generating next-gen images or even creating social sharing images for your sites HTML pages.

## 🧱 Astro JS non-HTML Routes

If truth be told, there is not much to adding a non-HTML route to your Astro site. So instead of building a project from scratch we will take a look at some example code. Hope that works for you!  There is a repo with the complete code if you do want to see the entire picture.  You can find a link further down the page for this.

![Astro JS non-HTML Routes: screenshot of finished project shows a list of file routes linking to a P D F file and a J S O N file.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l1dpdtdtuovvdrlasv28.png)

The idea, then, is to be able to link the resources from any HTML code on the site.  Here we put the links on the home page.  Note there are other solutions which might work better depending on your starting point or use case.  Let&rsquo;s say, for example, you have created a factsheet.  You will serve the same factsheet to anyone who visits the site and clicks the link (no customisation).  Here you could just place the PDF file for the factsheet in the `public` folder of your project.  In fact, that folder is for anything which does not need processing by Vite.  You could link `public/factsheet.pdf` with an href `/factsheet.pdf`.

### Static Astro JS non-HTML routes

Let&rsquo;s take it up a notch.  You still have your factsheet, however, you now want to pull in the latest available data each time you build your app as a **static Astro site** (the default).  The factsheet might pull in data from external APIs, which change from time-to-time.  Here you can go for our approach using a static site.  We can generate the PDF at build time in our file route.  Once again every user will get the same newsletter, just you save yourself having to update it manually each time the input data changes.

The `href`s look similar to the `public` case, though we will source the data differently, generating it in the non-HTML resource route:

```html
<!-- TRUNCATED -->
  <body>
    <main class="wrapper">
      <h1>Astro JS non-HTML Routes</h1>
      <ul>
        <li><a aria-label="Open P D F file" href="/pdf-resource.pdf">PDF file Route</a></li>
        <li><a aria-label="Open J S O N file" href="/json-resource.json">JSON file Route</a></li>
      </ul>
    </main>
	</body>
	</html>
```

### Server Side Rendered (SSR) API Route

If you want to go to town and have an SSR Astro site, you can opt for a similar approach, not too different from what we do here. In this case, you could even personalise the factsheet, adding the visitor&rsquo;s name at the top of the it.  We will see only a small couple of changes are needed to generate the PDF on an SSR site (compared to a static one).  In reality, the biggest difference is that you need a form or some other mechanism for collection user personalised data. 

## 📝 Astro PDF File Route on Static Site

To create a non-HTML route in Astro for a static site, you add a new file in `src/pages`.  Similar to file-based routing for HTML pages, the path will mirror the `href` you access the file with.  The only difference is that you will add `.js` or `.ts` on the end. So in our snippet above, where the `href` is `/pdf-resource.pdf` our full path for the resource route will be `src/pages/pdf-resource.ts` (we will use TypeScript).

Here&rsquo;s the file:

```typescript
import type { APIRoute } from 'astro';
import pdf from 'pdfjs';
import helvetica from 'pdfjs/font/Helvetica';

export const get: APIRoute = async function get() {
  try {
    const doc = new pdf.Document({ font: helvetica, padding: 10 });
    const text = doc.text({ fontSize: 12 });
    text.add('Example PDF');
    const buffer = await doc.asBuffer();

    return { body: buffer.toString('binary'), encoding: 'binary' };
  } catch (error: unknown) {
    throw new Error(`Something went wrong in pdf-resource.pdf route!: ${error as string}`);
  }
};
```

We use the `pdfjs` package here to generate the PDF for us.  It just has dummy content in our case.  You might generate it from scratch or compose it from other PDFs in your real-world app.  See <a aria-label="Open P D F J S docs" href="https://github.com/rkusa/pdfjs/tree/main/docs">more on `pdfjs` usage in the docs</a>. The most important part is the output we return in line `12`.  For a static site, aim to return an object with a `body` field which is a string.  For SSR you can return a `Response` object instead and even stream HTML.  We use `binary` encoding here which makes sure Astro encodes PDFs including images (for example) correctly &mdash; thanks to <a aria-label="OPen pull request udating Astro endpoint encoding" href="https://github.com/withastro/astro/commit/255636cc7b4ed5f72045f75a2411ebd84a2bdb0d">@altano for tip here</a>.

Remember, building a static site, this file will only change when you rebuild the site.  This is fine if you do not need to customise it for each visitor.

![Astro JS non-HTML Routes: screenshot of P D F route. Shows a P D F file open in a browser at route /pdf-resources.pdf. The file contains the text Example P D F](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/as10bvux2fmi8yj0jd8k.png)

### SSR Extension

For the SSR equivalent, we do not need the `.pdf` extension in the filename.  We return a `Response` object and can also include the `application/pdf` header:

```typescript
import type { APIRoute } from 'astro';
import pdf from 'pdfjs';
import helvetica from 'pdfjs/font/Helvetica';

export const get: APIRoute = async function get() {
  try {
    const doc = new pdf.Document({ font: helvetica, padding: 10 });
    const text = doc.text({ fontSize: 12 });
    text.add('Example PDF');
    const buffer = await doc.asBuffer();

    return new Response(buffer.toString(), {
      status: 200,
      headers: { 'content-type': `application/pdf` },
    });
  } catch (error: unknown) {
    return new Response(`Something went wrong in pdf-resource.pdf route!: ${error as string}`, {
      status: 501,
      statusText: 'Server error',
    });
  }
};
```

We also are free to customise the output here for every request.

## 🤖 Astro JSON Resource Route on Static Site

```typescript
import type { APIRoute } from 'astro';

export const get: APIRoute = async function get() {
  try {
    const astroResources = [
      { name: 'docs', url: 'https://docs.astro.build/en/getting-started/' },
      { name: 'discord', url: 'https://docs.astro.build/chat' },
    ];

    return { body: JSON.stringify(astroResources) };
  } catch (error) {
    throw new Error('Something went wrong in json-resource.json route!');
  }
};
```

Here we build the JSON within the file.  Again for a static site, we can customise per-build, but have to go full SSR to customise per-request.  Like for the PDF route if we do go SSR we can drop the `.json` extension (going for `json-resource.ts` instead).  Naturally, we can also update the `content-type` header too, going for `application/json`.

![Astro JS non-HTML Routes: screenshot of J S O N route. The screenshot show JSON browser in Firefox. There is an array of two objects.  The url is localhost: 3001/json-resource.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p1qbwd8nbkv2ob8n294o.png)

## 🙌🏽 Astro JS non-HTML Routes: Wrapping Up

In this Astro JS non-HTML routes post, we have taken a look at serving files or resources from API routes.  In particular, we saw:

- how to serve  **PDFs and JSON data from static file routes**,
- an example generating a **PDF resource in an SSR API route**,
- when you can avoid these techniques and **go for something simpler**!.

Hope you have found this post useful! As always there are a lot of ways to you can level up.  You can adapt the code to generate a CSV file on your static or SSR Astro site. You could explore pdfjs and generate a full-featured PDF for your app.  If you want to go next-level, consider adding a Webhook listener, for example which listens for a Webhook from one service and triggers a Webhook on another (like rebuilding your static Astro site)!  You could also generate Open Graph social sharing images. Whatever you opt for, take a look at the full project code on the <a aria-label="Open the Astro J S Location Git Hub repo" href="https://github.com/rodneylab/astro/tree/main/demos/astro-js-non-html-route">Rodney Lab GitHub page</a>.  I am keen to hear how you will the starter on your own projects as well as possible improvements.

## 🙏🏽 Astro JS non-HTML Routes: Feedback

Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, please <a aria-label="Support Rodney Lab via Buy me a Coffee" href="https://rodneylab.com/giving/">consider supporting me through Buy me a Coffee</a>.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via <a aria-label="Reach out on Twitter" href="https://twitter.com/messages/compose?recipient_id=1323579817258831875">@askRodney</a> on Twitter and also <a aria-label="Contact Rodney Lab via Telegram" href="https://t.me/askRodney">askRodney on Telegram</a>. Also, see <a aria-label="Get in touch with Rodney Lab" href="https://rodneylab.com/contact/">further ways to get in touch with Rodney Lab</a>. I post regularly on <a aria-label="See posts on Astro" href="https://rodneylab.com/tags/astro/">Astro</a> as well as <a aria-label="See posts on svelte kit" href="https://rodneylab.com/tags/sveltekit/">SvelteKit</a>. Also <a aria-label="Subscribe to the Rodney Lab newsletter" href="https://rodneylab.com/about/#newsletter">subscribe to the newsletter to keep up-to-date</a> with our latest projects.