Grouping Resources
Sharing path prefixes and middleware across resources
Overview
Resources can be grouped so they share functionality. A group is the only way to attach middleware or path prefixes to a resource — there is no per-resource setting for either. This is true whether the group holds one resource or many.
A resource group is not a runtime object. It is built at build time and returns an array of resource classes, which is exactly what the HTTP module’s Application.builder().resources() method already accepts.
Recommended Reading
- Read Creating a Resource
- Read the Middleware tutorial before attaching any
Objectives
To gain familiarity with:
- grouping resources using
ResourceGroup.builder(); - prefixing every path in the group; and
- attaching middleware to every resource in the group.
Syntax
A group is built with the ResourceGroup class’ static builder() method:
Deno
import {
Application,
ResourceGroup,
} from "npm:@drashland/drash/modules/http.native.js";
const group = ResourceGroup
.builder() // Get the resource group builder
.resources( // Add these resources to the group
ResourceA,
ResourceB,
)
.prefix( // Prefix every path in every resource above
"/api/v1",
)
.middleware( // Wrap every resource above with this middleware
SomeMiddlewareA, // This middleware runs first
SomeMiddlewareB, // This middleware runs second
)
.build(); // Returns an array of resource classes
const app = Application
.builder()
.resources(group) // Add the group to the chain the same way you add a single resource
.build();For the full builder API, see Resource groups.
The builder methods can be called in any order. Only build() has to come last.
Path Prefixes
.prefix() sets path prefixes — it rewrites the paths property of every resource in the group:
class Coffees extends Resource {
public paths = ["/coffees"];
}
const group = ResourceGroup
.builder()
.resources(Coffees)
.prefix("/api/v1")
.build();
// Coffees.paths is now ["/api/v1/coffees"]Every prefix is applied, not just the first. Passing more than one prefix produces one path per prefix per path — a cross product, not a fallback list.
class Coffees extends Resource {
public paths = ["/coffees", "/teas"];
}
const group = ResourceGroup
.builder()
.resources(Coffees)
.prefix("/api/v1", "/api/latest")
.build();
// Coffees.paths is now:
// ["/api/v1/coffees", "/api/v1/teas",
// "/api/latest/coffees", "/api/latest/teas"]That is how you serve the same resource under a versioned path and an alias without writing it twice. It is also how you accidentally quadruple your routes, so pass the prefixes you mean.
Prefixes are concatenated, not joined. "/api/v1" plus "/coffees" gives "/api/v1/coffees", and "/api/v1/" plus "/coffees" gives "/api/v1//coffees" — leave the trailing slash off.
Middleware
.middleware() wraps every resource in the group, in the order you pass them:
const group = ResourceGroup
.builder()
.resources(Users, Orders)
.middleware(
Auth, // Runs first
Logger, // Runs second, then the resource is called
)
.build();Each resource gets its own middleware instances. Users and Orders do not share an Auth object, so state stored on a middleware instance never leaks between resources.
See the Middleware tutorial for how to write one, and Pre-Built Middleware for the ones Drash ships.
How It Works
TLDR
build() generates anonymous proxy classes around each resource — one layer for middleware, one for prefixes — and returns those proxies instead of your original classes.
Detailed Explanation
build() applies the two features in a fixed order:
- Middleware first. Each resource class is wrapped in a
MiddlewareEntryPointclass whose HTTP methods delegate to the first middleware in the chain. The middleware are nested, so.middleware(A, B)producesA{original: B{original: resource}}. - Prefixes second. The result is wrapped again in a
PrefixedResourceProxy, which extends the class from step 1 and rewritesthis.pathsin its constructor.
Your original resource class is never mutated. The group hands the chain a subclass of it, which is why the same resource class can appear in two groups with different prefixes.