API routes

You can build your backend API with Rakkas using endpoints and middlewares that are analogous to pages and layouts respectively.

In most Node.js frameworks (like Express) you handle HTTP requests by manipulating request and response objects. These are not available in some serverless environment Rakkas aims to support. So we use a simpler mental model: An endpoint is a function that takes a request and returns a response. You get a plain object that represents the request, and you return a plain object that represents the response.

RakkasRequest

Rakkas's request object RakkasRequest has the following props:

NameTypeDescription
ipstringIP address
urlURLRequest URL
methodstringHTTP method
headersHeadersHTTP headers
paramsRecord<string, string>Path parameters
contextRecord<string, any>See below
typeSee belowContent-type of the request body
bodySeel belowRequest body

The type prop can be either "empty", "binary", "text", "form-data", or "json" depending on the request's body and Content-Type header:

TypeType of bodyDescription
"empty"undefinedThe request body is empty
"text"stringContent-Type starts with text/, body has been parsed as a string
"form-data"¹URLSearchParamsContent-Type is application/x-www-form-urlencoded, body has been parsed as URLSearchParams
"json"unknownContent-Type is application/json or ends with +json, body has been parsed as JSON
"binary"Uint8ArrayNone of the above, body has been parsed as binary

¹ Rakkas will support parsing multipart/form-data before version 1.0.

RakkasResponse

Rakkas's request object RakkasResponse has the following props, all optional:

NameTypeDescription
statusnumberHTTP status, defaults 200
headersSee belowResponse headers
bodySee belowResponse body

The headers property can either be a name value dictionary or an array of name value pairs. The body will be empty if it's null or undefined, sent as binary if it is Uint8Array, as text if it's a string. It will be JSON stringified if it is something else.

Endpoints

An endpoint is a module in the src/api directory, named endpoint.js or anything that matches *.endpoint.js (or ts, or any other extension included in the apiExtensions configuration option). It exports method handlers, functions that correspond to HTTP methods like get, post, put etc. DELETE method is handled by the del function because delete is a reserved word in JavaScript.

A method handler is just a function that takes a RakkasRequest and returns a RakkasResponse, possibly asynchronously.

Routing works similarly to pages:

Module nameURL path
src/api/endpoint.jsx/api/
src/api/user.endpoint.jsx/api/user
src/api/user/endpoint.jsxalso /api/user
src/api/user/[userId].endpoint.jsx/api/user/[userId] where [userId] is a dynamic route parameter

As an alternative, or in addition to exporting individual method handlers you can default export a function to handle all methods. Specific method handlers have priority.

Middleware functions

If you default export a function from a file named middleware.js (or ts, or any other extension included in the apiExtensions configuration option) in the src/api directory or in one of its subdirectories, you can intercept requests that would be otherwise handled by endpoint handlers in that directory or in one of its subdirectories. Like layouts, middleware functions can be nested and thematically grouped.

A middleware function receives a RakkasRequest just like an endpoint handler and is expected to return a RakkasRespone (or a promise of one) just like an endpoint. But it receives a second parameter, a next() function which calls the next middleware function or endpoint handler in the hierarchy. You can pass next() a modified request and return a modified response or you can return a response without even calling next.

The context property is a way to pass data from a middleware function to nested middleware functions or endpoint handlers. For example, you can create a cookie parsing middleware like this:

import { RakkasMiddleware, RakkasRequest } from "rakkasjs";
// This is a cookie parsing library available on npm, also install @types/cookie
import cookie from "cookie";

// Define a custom request type
export type RequestWithCookies = RakkasRequest<{
	cookies: Record<string, string>;
}>;

const cookieParserMiddleware: RakkasMiddleware = (request, next) => {
	const cookies = cookie.parse(request.headers.get("cookie") || "");

	return next({
		...request,
		context: { cookies },
	});
};

export default cookieParserMiddleware;

And you can acces the cookies like this:

import { RakkasResponse } from "rakkasjs";
import { RequestWithCookies } from "./middleware";

export function get(request: RequestWithCookies): RakkasResponse {
	return {
		// Return the parsed cookies as JSON
		body: { cookies: request.context.cookies },
	};
}

Fetching data from downstream servers

A server-side fetch implementation is available to middleware and endpoint modules for fetching data from downstream servers.