メインコンテンツまでスキップ
バージョン: 🚧 Canary

🛣️ Location routes

Location routes are SDK composers, not Express middleware. Reads are public; mutations require isAuthenticated() and checkIdentityType(['admin']).

Inventory

RouteMethod / protocolPathSchemaValidatorsSuccess status
createLocationRoutePOST / HTTP/locationscreateLocationSchemaisAuthenticated(), checkIdentityType(['admin'])201
getLocationRouteGET / HTTP/locations/:locationIdgetLocationSchemaNone200
updateLocationRoutePATCH / HTTP/locations/:locationIdupdateLocationSchemaisAuthenticated(), checkIdentityType(['admin'])200
deleteLocationRouteDELETE / HTTP/locations/:locationIddeleteLocationSchemaisAuthenticated(), checkIdentityType(['admin'])204
findLocationsRouteGET / HTTP/locationsfindLocationsSchemaNone200

Details

createLocationRoute

Implementation

Endpoint: POST /locations

POST /locations creates a root or child location for an authenticated administrator and returns the created location with 201; a missing parent is 404.

Access: Administrator. Factories run isAuthenticated() then checkIdentityType(['admin']).

Request: Required, strict application/json body from createLocationSchema: string name, code, and type, plus optional string parentId; it has no path or query parameters. In the default/unset Bearer mode, send Authorization: Bearer <access-token>; with authMode: 'cookie', send the accessToken cookie instead. A truthy parentId is loaded before hierarchy creation.

Pipeline: getLocationById and buildAncestorsFromParent only with a parent → buildLocationToCreatecreateLocationgetLocationByIdorThrow.

Success: Explicit 201 with the created location as returned by getLocationById: Mongo _id is projected out, while parentId and ancestors remain because normalizeLocation is not composed.

Failure: LocationNotFoundBlockError from the parent lookup or post-create lookup maps to 404; creation/read database errors map to 500. The linked validators run first and can fail with their documented authentication, setup, or authorization errors.

View complete source
export const createLocationRoute = withRoute({
handler: compose(
ifElse(
match(Boolean, ['params', 'requestBody', 'parentId']),
compose(
withLogging(
applyPayloadArgs(
getLocationById,
[
['context', 'db', 'locations'],
['params', 'requestBody', 'parentId'],
],
'parentLocation',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
buildAncestorsFromParent,
[
['context', 'data', 'parentLocation', 'ancestors'],
['params', 'requestBody', 'parentId'],
],
'ancestors',
),
),
),
),
applyPayloadArgs(noop, [[]]),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
buildLocationToCreate,
[
['params', 'requestBody'],
['context', 'data', 'ancestors'],
],
'locationToCreate',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
createLocation,
[
['context', 'db', 'locations'],
['context', 'data', 'locationToCreate'],
],
'locationId',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
getLocationById,
[
['context', 'db', 'locations'],
['context', 'data', 'locationId'],
],
'location',
),
),
),
lift(
withLogging(
orThrow(
[
[LocationNotFoundBlockError, 404],
[LocationUnexpectedDBError, 500],
],
[['context', 'data', 'location'], 201],
),
),
),
),
method: 'POST',
path: '/locations',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});

getLocationRoute

Implementation

Endpoint: GET /locations/:locationId

GET /locations/:locationId reads one public location and returns it with 200; an unknown ID is 404.

Access: Public.

Request: getLocationSchema requires path locationId; no body or query.

Pipeline: getLocationByIdorThrow.

Success: Explicit 200 with Mongo _id projected out; parentId and ancestors remain because normalizeLocation is not composed.

Failure: 404 missing location or 500 database error.

View complete source
export const getLocationRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getLocationById,
[
['context', 'db', 'locations'],
['params', 'requestParams', 'locationId'],
],
'location',
),
),
lift(
withLogging(
orThrow(
[
[LocationNotFoundBlockError, 404],
[LocationUnexpectedDBError, 500],
],
[['context', 'data', 'location'], 200],
),
),
),
),
method: 'GET',
path: '/locations/:locationId',
validators: [],
});

updateLocationRoute

Implementation

Endpoint: PATCH /locations/:locationId

PATCH /locations/:locationId updates an administrator-selected location and returns its reloaded public projection with 200; an unknown ID is 404.

Access: Administrator; isAuthenticated() then checkIdentityType(['admin']).

Request: updateLocationSchema requires string path locationId and a required, strict application/json body. The only optional body fields are string code, name, and type (an empty object is schema-valid); parentId is not accepted. It has no query parameters. Use Authorization: Bearer <access-token> by default, or the accessToken cookie in cookie mode.

Pipeline: updateLocationgetLocationByIdorThrow.

Success: Explicit 200 with the reloaded getLocationById projection: Mongo _id is omitted, but parentId and ancestors remain.

Failure: LocationNotFoundBlockError from the update or reload maps to 404; update/read database errors map to 500. The linked validators run first and can fail with their documented authentication, setup, or authorization errors.

View complete source
export const updateLocationRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
updateLocation,
[
['context', 'db', 'locations'],
['params', 'requestBody'],
['params', 'requestParams', 'locationId'],
],
'hasUpdatedLocation',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
getLocationById,
[
['context', 'db', 'locations'],
['params', 'requestParams', 'locationId'],
],
'location',
),
),
),
lift(
withLogging(
orThrow(
[
[LocationNotFoundBlockError, 404],
[LocationUnexpectedDBError, 500],
],
[['context', 'data', 'location'], 200],
),
),
),
),
method: 'PATCH',
path: '/locations/:locationId',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});

deleteLocationRoute

Implementation

Endpoint: DELETE /locations/:locationId

DELETE /locations/:locationId deletes an administrator-selected location only when it has no descendants and returns 204; a descendant produces 409.

Access: Administrator; isAuthenticated() then checkIdentityType(['admin']).

Request: deleteLocationSchema requires string path locationId; it has no body or query. Use Authorization: Bearer <access-token> by default, or the accessToken cookie in cookie mode.

Pipeline: buildDescendantsFilterfindLocationsassertNoDescendantLocationsdeleteLocation → empty body → orThrow.

Success: The terminator explicitly returns the { data: {}, statusCode: 204 } descriptor; the Express service calls res.status(204).json({}), whose HTTP 204 response has no response body.

Failure: LocationConflictError from assertNoDescendantLocations maps to 409; query/deletion database errors map to 500. Although the terminator also maps LocationNotFoundBlockError to 404, this pipeline does not call a block that produces that error: a missing target makes deleteLocation return LocationUnexpectedDBError and therefore 500. The linked validators run first and can fail with their documented authentication, setup, or authorization errors.

View complete source
export const deleteLocationRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(buildDescendantsFilter, [['params', 'requestParams', 'locationId']], 'ancestorsFilter'),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
findLocations,
[
['context', 'db', 'locations'],
['context', 'data', 'ancestorsFilter'],
],
'childLocations',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(assertNoDescendantLocations, [['context', 'data', 'childLocations']], 'hasNoChildLocations'),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
deleteLocation,
[
['context', 'db', 'locations'],
['params', 'requestParams', 'locationId'],
],
'hasDeletedLocation',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(partial(noop, [{}]), [[]], 'normalizedBody'))),
lift(
withLogging(
orThrow(
[
[LocationNotFoundBlockError, 404],
[LocationConflictError, 409],
[LocationUnexpectedDBError, 500],
],
[['context', 'data', 'normalizedBody'], 204],
),
),
),
),
method: 'DELETE',
path: '/locations/:locationId',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});

findLocationsRoute

Implementation

Endpoint: GET /locations

GET /locations lists public locations with pagination and returns 200; a database query failure is 500.

Access: Public.

Request: findLocationsSchema accepts optional integer query page (11000) and limit (150); it has no body or path parameters. withPagination defaults absent values to page 1 and limit 10, removes those two keys before passing the remaining query filter to findLocations.

Pipeline: withPagination around findLocationsapplySpec response builder → orThrow.

Success: Explicit 200 with { data, metadata: { pagination } }. Each location has Mongo _id projected out; pagination is { hasNext, hasPrev, limit, page, total, totalPages }.

Failure: A LocationUnexpectedDBError from the location query maps to 500. The source also lists a LocationNotFoundBlockError404 mapping, but findLocations returns an empty array rather than that error, so this route does not produce 404 for an empty result.

View complete source
export const findLocationsRoute = withRoute({
handler: compose(
withPagination(
withLogging(
applyPayloadArgs(
findLocations,
[
['context', 'db', 'locations'],
['params', 'requestQuery'],
],
'locations',
),
),
),
flatMapAsync(
applyPayloadArgs(
applySpec({data: nthArg(0), metadata: {pagination: nthArg(1)}}),
[
['context', 'data', 'locations', 'data'],
['context', 'data', 'locations', 'metadata'],
],
'normalizedBody',
),
),
lift(
withLogging(
orThrow(
[
[LocationNotFoundBlockError, 404],
[LocationUnexpectedDBError, 500],
],
[['context', 'data', 'normalizedBody'], 200],
),
),
),
),
method: 'GET',
path: '/locations',
validators: [],
});