Concepts

Routes

Edit on GitHub

Primate uses filesystem-based routes. Route files are JavaScript files in the routes directory that correspond to their routes. For example, the file at routes/user/profile.js is used to handle a client accessing /user/profile. The path may include parameters in brackets, which are mapped to request.path, and which may have types.

To illustrate this, consider that inside routes

HTTP verbs

Every route file exports an object containing one or many HTTP verb functions.

routes/user/profile.js
export default {
  get() {
    return "this is a GET request";
  },
  put() {
    return "this is a PUT request";
  },
};

In this example, accessing the path /user/profile using any of the specified verbs will return a plain-text response with the given string.

The request object

Route verb functions accept a single parameter representing request data. This aggregate object allows easy access to the request body, any path parameters defined with brackets, the query string split into parts, cookies as well as other headers and a reference to the original WHATWG Request object. The aggregate nature of this object allows you to pull in what you need using object destructuring.

body

The request body.

routes/your-name.js
export default {
  post(request) {
    return `Hello, ${request.body.name}`;
  },
}

If a client sends a POST request to /your-name using the content type application/json and {"name": "Donald"} as body, this route will respond with 200 saying Hello, Donald.

Primate will attempt to decode the body according to the Content-Type header used in the request.

routes/your-full-name.js
export default {
  post(request) {
    const { name } = request.body;

    if (name === undefined) {
      return "You haven't specified your name";
    }

    return `Hello, ${name}`;
  }
}

In this example, if a client sends a request to /your-full-name with a URL-encoded form (application/x-www-form-urlencoded) or JSON data (application/json) with a field name in its body, Primate will respond by saying Hello and the provided name.

path

The request's path, an object containing named parameters.

routes/users/[user].js
import { error } from "primate";

const users = ["Donald", "Ryan"];

export default {
  post(request) {
    const user = request.path.get("user");

    if (users.includes(user)) {
      return `Hello, ${user}`;
    }

    return error("Unknown user");
  },
};

If a user requests POST /users/Donald or /users/Ryan, Primate will respond with 200, saying Hello. It will otherwise reply with 404 saying Unknown user.

We will later handle routes with parameters in depth.

query

The request's query string, broken down into its constituent parts.

routes/users.js
import { error } from "primate";

const users = ["Donald", "Ryan"];

export default {
  post(request) {
    const user = request.query.get("user");

    if (users.includes(user)) {
      return `Hello, ${user}`;
    }

    return error("Unknown user");
  },
};

If a user requests POST /users?user=Donald or /users?user=Ryan, Primate will respond with 200, otherwise with 404.

cookies

The request's Cookie header, broken down into individual cookies.

routes/current-user.js
import { error } from "primate";

const users = ["Donald", "Ryan"];

export default {
  post(request) {
    const user = request.cookies.get("user");

    if (users.includes(user)) {
      return `Hello, ${user}`;
    }

    return error("Unknown user");
  },
};

If a user requests POST /current-user with the Cookie header set to user=Donald or user=Ryan, Primate will respond with 200, otherwise with 404.

headers

The request's individual headers.

routes/current-x-user.js
import { error } from "primate";

const users = ["Donald", "Ryan"];

export default {
  post(request) {
    const user = request.headers.get("X-User");

    if (users.includes(user)) {
      return `Hello, ${user}`;
    }

    return error("Unknown user");
  },
};

If a user requests POST /current-x-user with a X-User header set to Donald or Ryan, Primate will respond with 200, otherwise with 404.

original

The original property of the request object provides access to the original WHATWG Request object.

Parameters

Route paths may contain parameters in brackets, which indicate they will be mapped to request.path. Path parameter names are case sensitive, and a request may contain any number of them, though it must not contain the same parameter in the same case twice. They must be non-empty, that is matched by at least one character.

By default, parameters will match anything in the path except /, though they are not greedy. A path like /users/[user_id]a.js is unambiguous: it will match any path that starts with /users/ followed by anything that is not /, provided that it ends with a. The last a can therefore not be part of the match.

Such a path will thus be matched by all the following requests.

The same path won't be matched by any of the following requests.

Parameters can be also typed, in which case their value can be restricted. The types section elaborates on the use of types in path parameters.

Previous
Use cases
Next
Responses