Create an API

Learn how to build RESTful APIs by handling GET, POST, PUT, and DELETE requests.

Overview

In JopiJS, creating an API is as simple as creating a page. Instead of returning React components, API routes handle HTTP methods and return data (usually JSON).

In this guide, you will learn how to:

  • Define API routes using file-based naming.
  • Handle different HTTP methods (POST, GET, etc.).
  • Retrieve request data and uploaded files.
  • Return JSON or HTML responses.

1. Routing & File Naming

API routes follow the same folder structure as pages inside the @routes directory. The HTTP method you want to handle is determined by the filename:

File NameHTTP MethodUse Case
onGET.tsGETFetching data (conflicts with page.tsx).
onPOST.tsPOSTCreating data or handling forms.
onPUT.tsPUTUpdating existing data.
onDELETE.tsDELETERemoving data.

Project Structure example:

src/
└── mod_myModuleName/
    └── @routes/
        └── users/
            ├── onGET.ts     # -> GET  /users
            └── onPOST.ts    # -> POST /users

2. Handling a POST Request

Let's create an endpoint to receive JSON data. JopiJS automatically parses the request body for you.

src/mod_api/@routes/contact/onPOST.ts
import { JopiRequest } from "jopijs";

export default async function(req: JopiRequest) {
    // 1. Get the parsed body data (JSON, Form-data, etc.)
    const data = await req.req_getBodyData();

    console.log("Received data:", data);

    // 2. Return a JSON response
    return req.res_jsonResponse({
        status: "success",
        message: "Message received!",
        echo: data
    });
}

Testing with curl

curl -X POST http://localhost:3000/contact \
  -H "Content-Type: application/json" \
  -d '{"name": "John Doe", "email": "john@example.com"}'

3. Receiving Files

JopiJS makes handling file uploads seamless. req_getBodyData() automatically detects multipart/form-data.

export default async function(req: JopiRequest) {
    const data = await req.req_getBodyData();

    // If "avatar" was a file field in the form
    const file = data.avatar;

    if (file instanceof File) {
        const bytes = await file.arrayBuffer();
        console.log(`Received file: ${file.name}, size: ${bytes.byteLength} bytes`);
    }

    return req.res_jsonResponse({ uploaded: true });
}

4. Path Parameters & URL Info

Just like pages, API routes can use dynamic segments (e.g., [userId]).

src/mod_api/@routes/users/[userId]/onGET.ts
import { JopiRequest } from "jopijs";

export default function(req: JopiRequest) {
    // Access dynamic segments
    const userId = req.req_urlParts.userId;

    // Access query parameters (/users/1?active=true)
    const isActive = req.req_urlInfos.searchParams.get("active");

    return req.res_jsonResponse({
        userId,
        path: req.req_urlInfos.pathname,
        isActive
    });
}

5. Important: onGET.ts vs page.tsx

Since both files listen for GET requests at the same URL, they cannot coexist peacefully in the same folder.

  • page.tsx: Intended for browser navigation (returns HTML).
  • onGET.ts: Intended for API calls (returns Data).

Conflict resolution: If both files exist in the same folder, onGET.ts will take priority, and the page will not be displayed in the browser.


Summary of Response Methods

MethodDescription
req.res_jsonResponse(data)Returns a JSON response with the correct headers.
req.res_htmlResponse(html)Returns a raw HTML string.
req.res_textResponse(text)Returns a plain text response.
req.res_redirect(url)Sends a temporary redirect.