Make files public

Learn how to serve static assets, shared folders, and manual file responses.

Overview

Most of the time, JopiJS handles file exposure automatically. However, there are cases where you need more control, such as serving global assets or creating a custom file server.

In this guide, you will learn:

  • How JopiJS automatically handles CSS and images.
  • How to use the global public/ folder.
  • How to manually return a file as a response.
  • How to serve entire directories using catch-all routes.

Before manually exposing files, remember that JopiJS automatically exposes any asset imported via code.

When you do this:

import "./styles.css";
import logo from "./logo.png";

JopiJS detects the imports, optimizes the files, and serves them via an internal URL. You don't need to do anything else.

Use imports whenever possible. It ensures your assets are bundled, hashed (for better caching), and optimized (e.g., small images inlined as base64).


2. The Global public/ Folder

If you have assets that aren't imported in React but need to be accessible via a direct URL (like robots.txt or a legacy image folder), use the public/ folder at your project root.

Structure:

my-jopi-app/
├── public/
│   ├── favicon.ico
│   └── docs/
│       └── manual.pdf
└── src/ ...

Access:

  • http://localhost:3000/favicon.ico
  • http://localhost:3000/docs/manual.pdf

3. Manually Returning a File

Sometimes you need an API route (e.g., onGET.ts) to return a specific file based on some logic (like checking if a user is authenticated).

Using file_returnFile

Returns a file based on an absolute path or a path relative to the project root.

src/mod_api/@routes/download/onGET.ts
import { JopiRequest } from "jopijs";

export default function(req: JopiRequest) {
    // Check some permission...
    if (!isAuthorized(req)) return req.res_redirect("/login");

    // Relative to the project root
    return req.file_returnFile("./storage/reports/annual-report.pdf");
}

Using file_returnRelFile

Returns a file relative to the current source file.

export default function(req: JopiRequest) {
    // Relative to this .ts file
    return req.file_returnRelFile("./invoice.pdf", import.meta);
}

4. Serving Entire Directories

If you want to serve a whole local folder under a specific URL prefix, you can use a Catch-all route ([...]) combined with file_serveFromDir.

Example: Serving a "Reports" folder

We want to serve ./my-internal-files/ via the URL /view-reports/*.

Structure:

src/
└── mod_admin/
    └── @routes/
        └── view-reports/
            └── [...]/           # <--- Catch-all URLs from /view-reports/
                └── onGET.ts

Implementation:

src/mod_admin/@routes/view-reports/[...]/onGET.ts
import { JopiRequest } from "jopijs";

export default function(req: JopiRequest) {
    // 1. Prepare the path (remove the route prefix)
    // For URL /view-reports/2024/jan.pdf, we want to look for 2024/jan.pdf
    const relativePath = req.req_urlInfos.pathname.substring(req.routeInfos.route.length - 2);
    req.req_urlInfos.pathname = relativePath;

    // 2. Serve from the directory my-internal-files
    // This directory is at the root of the project.
    //
    return req.file_serveFromDir("./my-internal-files");
}

Security Note: Be extremely careful when serving directories. Ensure that users cannot use ".." in the URL to navigate outside the intended folder. JopiJS provides built-in protections, but always validate your inputs.