Add & Replace Routes

Learn how to define URLs, handle parameters, and override existing routes using JopiJS's file-based routing system.

Overview

Routing in JopiJS is file-based. Each folder inside a module's @routes/ directory automatically becomes a public URL. Because JopiJS is modular, routes from different modules are merged into a single routing tree at runtime.

In this guide, you will learn how to:

  • Create simple, parameterized, and catch-all routes.
  • Attach UI Pages and API Listeners to the same URL.
  • Merge and replace routes from different modules.
  • Use marker files to manage caching and security metadata.

1. Route Types

JopiJS supports three types of route segments to handle different URL patterns.

A. Simple Routes

The most common type. The folder name matches the URL path exactly.

  • File: src/mod_main/@routes/about/us/page.tsx
  • URL: /about/us

B. Parameterized Routes [name]

Used for dynamic URLs where a segment can be any value (e.g., product IDs, usernames).

  • File: src/mod_shop/@routes/product/[id]/page.tsx
  • URL: /product/123, /product/phone-case
src/mod_shop/@routes/product/[id]/page.tsx
import { JopiPageProps } from "jopijs/ui";

export default function ProductPage({ params }: JopiPageProps) {
    const { id } = params; // Retrieve the [id] from the URL
    return <div>Displaying Product: {id}</div>;
}

C. Catch-all Routes [...]

Captures all remaining segments of a URL. Useful for serving files or building localized sub-sites.

  • File: src/mod_docs/@routes/help/[...]/page.tsx
  • URL: /help/getting-started, /help/api/reference/v1

2. Attaching Listeners (Page vs API)

Each route folder can contain two types of "Listeners":

  1. UI Page (page.tsx): A React component that renders the HTML for GET requests.
  2. API Handlers (onGET.ts, onPOST.ts, etc.): Functions that handle specific HTTP methods.
FileHandlesPriority
page.tsxHTTP GET (renders HTML)Lowest
onGET.tsHTTP GET (returns JSON/Data)Overrides page.tsx
onPOST.tsHTTP POSTN/A
onPUT.tsHTTP PUTN/A
onPOST.ts
import { JopiRequest } from "jopijs";

export default async function(req: JopiRequest) {
    const data = await req.req_getBodyData();
    return req.res_jsonResponse({ success: true, received: data });
}

Important: A single route cannot have both a page.tsx and an onGET.ts.
If both exist, onGET.ts will take priority and the HTML page will never be rendered.


3. Merging & Replacing Routes

Since JopiJS is modular, multiple modules can contribute to the same route.

Scenario: Merging

  • mod_auth defines /profile with a page.tsx.
  • mod_settings defines /profile with an onPOST.ts.
  • Result: /profile now handles both displaying the page (GET) and saving data (POST).

Scenario: Replacing (Priority)

If two modules define the same listener (e.g., both provide a page.tsx for /home), JopiJS uses the Priority System to decide which one to use.

Overriding a Route
mod_base_theme/
  └── @routes/home/page.tsx (default.priority)

mod_custom_theme/
  └── @routes/home/
      ├── page.tsx (high.priority)
      └── high.priority

The version in mod_custom_theme will be served to all users.


4. Route Metadata (Marker Files)

You can control the behavior of a route by adding Marker Files (empty files with specific extensions) inside the route folder.

Caching

  • autoCache.enable: Forces the page to be cached (recommended for speed).
  • autoCache.disable: Disables caching (not recommended).

Security & Roles

JopiJS uses Marker Files to protect your routes. These files act as gates: if the user doesn't have the required role, the request is rejected before reaching your code.

  • needRole_[roleName].cond: The Global marker. It applies to all HTTP methods (GET, POST, etc.) and the HTML page.
  • Method-Specific Markers: You can target specific handlers by adding a prefix. These markers only apply to their respective methods.

Role Mapping Table

Marker PrefixTargetScope
needRole_All handlers (page.tsx, onPOST, etc.)Global
getNeedRole_page.tsx or onGET.tsSpecific to GET
postNeedRole_onPOST.tsSpecific to POST
putNeedRole_onPUT.tsSpecific to PUT
deleteNeedRole_onDELETE.tsSpecific to DELETE

Example Scenario: You have a /blog route where everyone can read (no global role), but only editors can post.

Route Structure
@routes/blog/
├── page.tsx                # Accessible to all (no global role)
├── onPOST.ts               # Handler to create a post
└── postNeedRole_editor.cond # Protects ONLY the POST method

If you wanted to protect the entire blog (no read, no write) for everyone but admins, you would use:

  • needRole_admin.cond (Global protection)

For more information on managing users and complex access rules, see the Limiting Access guide.

Inheritance: When you replace a route using the priority system, JopiJS also uses the metadata files (Cache and Security) from the winning module. This only for the replaced items.