LogoLogo
ChangelogGitHubTwitterGitter
v3.x
v3.x
  • Marble.js
  • Getting started
    • Installation
    • Quick setup
  • HTTP
    • Effects
    • Middlewares
    • Routing
    • Errors
    • Output
    • Context
    • Advanced
      • Logging
      • Validation
      • Server Events
      • Streaming
      • Continuous mode
  • Messaging
    • Core concepts
      • Events
      • Effects
    • Microservices
      • AMQP (RabbitMQ)
      • Redis Pub/Sub
    • CQRS
    • WebSockets
  • Testing
    • HTTP routes testing
  • Other
    • How does it glue together?
    • Migration guides
      • Migration from version 2.x
      • Migration from version 1.x
    • API reference
      • core
        • bindTo
        • bindEagerlyTo
        • createEvent
        • createServer
        • combineRoutes
        • createContextToken
        • EffectFactory
        • r.pipe
        • httpListener
        • operator: matchEvent
        • operator: use
        • operator: act
      • messaging
        • eventBus
        • messagingClient
        • createMicroservice
        • reply
      • websockets
        • webSocketListener
        • operator: broadcast
        • operator: mapToServer
      • middleware-multipart
      • middleware-cors
      • middleware-joi
      • middleware-jwt
        • Token signing
      • middleware-io
      • middleware-logger
      • middleware-body
    • Style Guide
    • FAQ
Powered by GitBook
On this page
  • Route composition
  • Body parameters
  • URL parameters
  • Query parameters
  1. HTTP

Routing

Routing determines how an application responds to a client request to a particular endpoint, which is a path and a specific HTTP method (eg. GET, POST).

PreviousMiddlewaresNextErrors

Last updated 4 years ago

Route composition

As we know - every API requires composable routing. Lets assume that we have a separate User feature where its API endpoints respond to GET and POST methods on /user path.

Deprecation warning

With an introduction of Marble.js 3.0, old HTTP route builder is deprecated. Please use builder instead.

r.pipe is an indexed monad builder used for collecting information about Marble REST route details, like: path, request method type, middlewares and connected Effect.

import { combineRoutes, r } from '@marblejs/core';

const getUsers$ = r.pipe(
  r.matchPath('/'),
  r.matchType('GET'),
  r.useEffect(req$ => req$.pipe(
    // ...
  )));

const postUser$ = r.pipe(
  r.matchPath('/'),
  r.matchType('POST'),
  r.useEffect(req$ => req$.pipe(
    // ...
  )));

export const user$ = combineRoutes('/user', [
  getUsers$,
  postUser$,
]);

Route effects can be grouped together using combineRoutes function, which combines routing for a prefixed path passed as a first argument. Exported group of Effects can be combined with other Effects like in the example below.

import { combineRoutes, r } from '@marblejs/core';
import { user$ } from './user.effects';

const root$ = r.pipe(
  r.matchPath('/'),
  r.matchType('GET'),
  r.useEffect(req$ => req$.pipe(
    // ...
  )));

const foo$ = r.pipe(
  r.matchPath('/foo'),
  r.matchType('GET'),
  r.useEffect(req$ => req$.pipe(
    // ...
  )));

export const api$ = combineRoutes('/api/v1', [
  root$,
  foo$,
  user$, // 👈
]);

As you can see, the previously defined routes can be combined together, so as a result the routing is built in a much more structured way. If we analyze the above example, the routing will be mapped to the following routing table.

GET    /api/v1
GET    /api/v1/foo
GET    /api/v1/user
POST   /api/v1/user
import { combineRoutes } from '@marblejs/core';

const user$ = combineRoutes('/user', {
  middlewares: [authorize$],
  effects: [getUsers$, postUser$],
});

Body parameters

All properties and values in req.bodyobject are untrusted and should be validated before usage.

URL parameters

The combineRoutes function and the matchPath allows you to define parameters in the path argument. All parameters are defined by the syntax with a colon prefix.

import { r } from '@marblejs/core';

const foo$ = r.pipe(
  r.matchPath('/:foo/:bar'),
  // ...
);

Decoded path parameters are placed in the req.params property. If there are no decoded URL parameters then the property contains an empty object. For the above example and route /bob/12 the req.params object will contain the following properties:

{
  foo: 'bob',
  bar: '12',
}

All properties and values in req.params object are untrusted and should be validated before usage.

Path parameters can be suffixed with an asterisk (*) to denote a zero or more parameter matches. The code snippet below shows an example use case of a "zero-or-more" parameter. For example, it can be useful for defining routing for static assets.

import { r } from '@marblejs/core';
import { map, mergeMap } from 'rxjs/operators';

const getFile$ = r.pipe(
  r.matchPath('/:dir*'),
  r.matchType('GET'),
  r.useEffect(req$ => req$.pipe(
    // ...
    map(req => req.params.dir),
    mergeMap(readFile(STATIC_PATH)),
    map(body => ({ body }))
  )));

Query parameters

Example 1:

GET /user?name=Patrick
req.query = {
  name: 'Patrick',
};

Example 2:

GET /user?name=Patrick&location[country]=Poland&location[city]=Katowice
req.query = {
  name: 'Patrick',
  location: {
    country: 'Poland',
    city: 'Katowice',
  },
};

All properties and values in req.query object are untrusted and should be validated before usage.

There are some cases where there is a need to compose a bunch of middlewares before grouped routes, e.g. to authenticate requests only for a selected group of endpoints. Instead of composing middlewares using for each route separately, you can compose them via the extended second parameter incombineRoutes() function.

Marble.js doesn't come with a built-in mechanism for parsing POST, PUT and PATCH request bodies. In order to get the parsed request body you can use dedicated @marblejs/middleware-body package. A new req.body object containing the parsed data will be populated on the request object after the middleware, or undefined if there was no body to parse, the Content-Type was not matched, or an error occurred. To learn more about body parsing middleware visit the API specification.

By design, the req.body, req.params, req.queryare of type unknown. In order to work with decoded values you should validate them before (e.g. using dedicated validator middleware) or explicitly assert attributes to the given type. We highly recommend to use the package which allows you to properly infer the type of validated properties.

For parsing and decoding URL parameters, Marble.js makes use of library.

You should validate incoming URL params using dedicated middleware.

Except intercepting URL params, the routing is able to parse query parameters provided in path string. All decoded query parameters are located inside req.query property. If there are no decoded query parameters then the property contains an empty object. For parsing and decoding query parameters, Marble.js makes use of library.

You should validate incoming req.query parameters using dedicated middleware.

EffectFactory
r.pipe
use operator
@marblejs/middleware-body
@marblejs/middlware-io
path-to-regexp
requestValidator$
qs
requestValidator$