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

🛣️ Profile routes

Profile routes are SDK composers, not Express middleware. All routes authenticate first; routes below state their exact second access rule and map source errors through orThrow or legacy terminators.

Inventory

RouteMethod / protocolPathSchemaValidatorsSuccess status
createProfileRoutePOST / HTTP/profilescreateProfileSchemaisAuthenticated(), some( checkIdentityType(['admin']), isSelf(['params', 'requestBody', 'identityId']) )200
getProfileRouteGET / HTTP/profiles/:profileIdgetProfileSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )200
findProfilesRouteGET / HTTP/profilesfindProfilesSchemaisAuthenticated(), checkIdentityType(['admin'])200
updateProfileRoutePATCH / HTTP/profiles/:profileIdupdateProfileSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )200
deleteProfileRouteDELETE / HTTP/profiles/:profileIddeleteProfileSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
getAvatarUploadUrlRouteGET / HTTP/profiles/:profileId/avatar-upload-urlgetSignedImageUploadUrlSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )200
findProfilesByIdentityIdRouteGET / HTTP/profiles/identities/:identityIdfindByIdentityIdSchemaisAuthenticated(), isSelf(['params', 'requestParams', 'identityId'])200
createProfileFollowRoutePUT / HTTP/profiles/:profileId/profile-follows/:followProfileIdcreateProfileFollowSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
deleteProfileFollowRouteDELETE / HTTP/profiles/:profileId/profile-follows/:followProfileIddeleteProfileFollowSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
getProfileFollowersRouteGET / HTTP/profiles/:profileId/followersgetProfileFollowersSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )200
createOrganizationFollowRoutePUT / HTTP/profiles/:profileId/organization-follows/:followOrganizationIdcreateOrganizationFollowSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
deleteOrganizationFollowRouteDELETE / HTTP/profiles/:profileId/organization-follows/:followOrganizationIddeleteOrganizationFollowSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
createProductLikeRoutePUT / HTTP/profiles/:profileId/product-likes/:likeProductIdcreateProductLikeSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
deleteProductLikeRouteDELETE / HTTP/profiles/:profileId/product-likes/:likeProductIddeleteProductLikeSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204

Details

createProfileRoute

Implementation

Endpoint: POST /profiles

Access: Authenticated administrator or the identity in the JSON body.

Request: createProfileSchema, application/json body.

Pipeline: createProfile, getProfileById, avatar normalization, raw-document normalization, orThrow.

Success: Normalized profile data is returned without a statusCode descriptor.

Failure: The legacy create/load handlers can return 400, 404, or 500; orThrow has no local error map, so those errors propagate.

View complete source
export const createProfileRoute = withRoute({
handler: compose(
withLogging(createProfile),
flatMapAsync(withLogging(getProfileByIdHandler)),

flatMapAsync(
applyPayloadArgs(
normalizeAvatarOfOwner,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profile'],
],
'profileWithAvatarUrl'
)
),

flatMapAsync(
applyPayloadArgs(
normalizeRawDocument,
[['context', 'data', 'profileWithAvatarUrl']],
'normalizedProfile'
)
),

lift(withLogging(orThrow([], [['context', 'data', 'normalizedProfile']])))
),
method: 'POST',
path: '/profiles',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
isSelf(['params', 'requestBody', 'identityId'])
),
],
});

getProfileRoute

Implementation

Endpoint: GET /profiles/:profileId

Access: authenticated administrator or owner.

Request: path profileId; see getProfileSchema.

Pipeline: getProfileById, avatar and raw-document normalization, orThrow.

Success: normalized profile JSON.

Failure: source profile lookup failure.

View complete source
export const getProfileRoute = withRoute({
handler: compose(
withLogging(getProfileByIdHandler),

flatMapAsync(
applyPayloadArgs(
normalizeAvatarOfOwner,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profile'],
],
'profileWithAvatarUrl'
)
),

flatMapAsync(
applyPayloadArgs(
normalizeRawDocument,
[['context', 'data', 'profileWithAvatarUrl']],
'normalizedProfile'
)
),

lift(withLogging(orThrow([], [['context', 'data', 'normalizedProfile']])))
),
method: 'GET',
path: '/profiles/:profileId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

findProfilesRoute

Implementation

Endpoint: GET /profiles

Access: authenticated administrator.

Request: identity/name filters and shared pagination query parameters; see findProfilesSchema.

Pipeline: findProfiles, pagination, avatar/document normalization.

Success: { data, metadata: { pagination } } without a statusCode descriptor.

Failure: the legacy list handler can return 500; the route has no local error map.

View complete source
export const findProfilesRoute = withRoute({
handler: compose(
withPagination(withLogging(findProfilesHandler)),
flatMapAsync(
applyPayloadArgs(
normalizeAvatarsOfOwners,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profiles', 'data'],
],
'profilesWithAvatarUrl'
)
),
flatMapAsync(
applyPayloadArgs(
normalizeDocuments,
[['context', 'data', 'profilesWithAvatarUrl']],
'normalizedProfiles'
)
),
flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'normalizedProfiles'],
['context', 'data', 'profiles', 'metadata'],
],
'normalizedProfilesWithPagination'
)
),
lift(
withLogging(
orThrow([], [['context', 'data', 'normalizedProfilesWithPagination']])
)
)
),
method: 'GET',
path: '/profiles',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});

updateProfileRoute

Implementation

Endpoint: PATCH /profiles/:profileId

Access: authenticated administrator or owner.

Request: path ID and JSON body; see updateProfileSchema.

Pipeline: loads prior profile, updateProfile, reloads it, conditionally deletes replaced avatar, normalizes it.

Success: normalized profile JSON.

Failure: 404 profile; 500 file storage.

View complete source
export const updateProfileRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profileBefore'
)
),

flatMapAsync(withLogging(updateProfile)),
flatMapAsync(withLogging(getProfileByIdHandler)),

flatMapAsync(
withLogging(
applyPayloadArgs(
deleteAvatarIfReplaced,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profileBefore', 'avatar'],
['context', 'data', 'profile', 'avatar'],
],
// TODO: Remove this pointless key
'x'
)
)
),

flatMapAsync(
applyPayloadArgs(
normalizeAvatarOfOwner,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profile'],
],
'profileWithAvatarUrl'
)
),

flatMapAsync(
applyPayloadArgs(
normalizeRawDocument,
[['context', 'data', 'profileWithAvatarUrl']],
'normalizedProfile'
)
),

lift(
withLogging(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[FileStorageServiceError, 500],
],
[['context', 'data', 'normalizedProfile']]
)
)
)
),
method: 'PATCH',
path: '/profiles/:profileId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

deleteProfileRoute

Implementation

Endpoint: DELETE /profiles/:profileId

Access: authenticated administrator or owner.

Request: path ID.

Pipeline: getProfileById, deleteProfile, deleteAvatar, deleteProfileTerminator.

Success: 204.

Failure: legacy loader/deleter errors and avatar storage errors propagate; the terminator also throws 500 if data.deleteProfile is missing.

View complete source
export const deleteProfileRoute = withRoute({
handler: compose(
withLogging(getProfileByIdHandler),
flatMapAsync(withLogging(deleteProfile)),
flatMapAsync(
withLogging(
applyPayloadArgs(
deleteAvatar,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profile', 'avatar'],
],
'wasAvatarDeleted'
)
)
),
lift(withLogging(deleteProfileTerminator))
),
method: 'DELETE',
path: '/profiles/:profileId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

getAvatarUploadUrlRoute

Implementation

Endpoint: GET /profiles/:profileId/avatar-upload-url

Access: authenticated administrator or owner.

Request: shared signed-image query contract (contentType, contentLength).

Pipeline: generateSignedAvatarUploadUrl, orThrow.

Success: 200

Failure: file-storage error maps to 500.

View complete source
export const getAvatarUploadUrlRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(generateSignedAvatarUploadUrl, [
['context', 'fileStorageDriver'],
['params', 'requestQuery', 'contentType'],
['params', 'requestQuery', 'contentLength'],
])
),
lift(orThrow([[FileStorageServiceError, 500]], [['context', 'data'], 200]))
),
method: 'GET',
path: '/profiles/:profileId/avatar-upload-url',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

findProfilesByIdentityIdRoute

Implementation

Endpoint: GET /profiles/identities/:identityId

Access: authenticated self.

Request: shared identity path parameter plus shared pagination query.

Pipeline: identity filter, no-_id find options, paginated findProfiles, avatar normalization, response assembly.

Success: { data, metadata: { pagination } } without a statusCode descriptor.

Failure: ProfileDbBlockError and AvatarBlockError map to 500.

View complete source
export const findProfilesByIdentityIdRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
buildIdentityIdFilter,
[['params', 'requestParams', 'identityId']],
'filter'
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(buildWithoutMongoIdFindOptions, [], 'options')
)
),
flatMapAsync(
withPagination(
withLogging(
applyPayloadArgs(
findProfiles,
[
['context', 'db', 'profiles'],
['context', 'data', 'filter'],
['context', 'data', 'options'],
],
'paginatedProfiles'
)
)
)
),
flatMapAsync(
applyPayloadArgs(
normalizeAvatarsOfOwners,
[
['context', 'fileStorageDriver'],
['context', 'data', 'paginatedProfiles', 'data'],
],
'profilesWithAvatarUrl'
)
),
flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'profilesWithAvatarUrl'],
['context', 'data', 'paginatedProfiles', 'metadata'],
],
'normalizedBody'
)
),
lift(
withLogging(
orThrow(
[
[ProfileDbBlockError, 500],
[AvatarBlockError, 500],
],
[['context', 'data', 'normalizedBody']]
)
)
)
),
method: 'GET',
path: '/profiles/identities/:identityId',
validators: [
isAuthenticated(),
isSelf(['params', 'requestParams', 'identityId']),
],
});

createProfileFollowRoute

Implementation

Endpoint: PUT /profiles/:profileId/profile-follows/:followProfileId

Access: authenticated administrator or owner.

Request: both path IDs; see createProfileFollowSchema.

Pipeline: loads both profiles, then createProfileFollow.

Success: 204

Failure: 404 profile, 409 duplicate, 500 database failure.

View complete source
export const createProfileFollowRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'followProfileId'],
],
'followProfile'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
createProfileFollow,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'followProfileId'],
],
'createProfileFollowResult'
)
)
),
lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
[ProfileAlreadyFollowedBlockError, 409],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'PUT',
path: '/profiles/:profileId/profile-follows/:followProfileId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

deleteProfileFollowRoute

Implementation

Endpoint: DELETE /profiles/:profileId/profile-follows/:followProfileId

Access: authenticated administrator or owner.

Request: both path IDs.

Pipeline: loads both profiles, then deleteProfileFollow.

Success: 204

Failure: 404 profile/follow, 500 database failure.

View complete source
export const deleteProfileFollowRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'followProfileId'],
],
'followProfile'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
deleteProfileFollow,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'followProfileId'],
],
'deleteProfileFollowResult'
)
)
),
lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[ProfileFollowNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'DELETE',
path: '/profiles/:profileId/profile-follows/:followProfileId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

getProfileFollowersRoute

Implementation

Endpoint: GET /profiles/:profileId/followers

Access: authenticated administrator or owner.

Request: path ID and pagination.

Pipeline: verifies profile, builds follower filter, paginates findProfiles, then normalizeFollowers.

Success: { data, metadata: { pagination } } with 200.

Failure: ProfileNotFoundBlockError maps to 404 and ProfileUnexpectedDBError to 500; unmapped block errors propagate.

View complete source
export const getProfileFollowersRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
buildProfileFollowersByFollowProfileIdQuery,
[['params', 'requestParams', 'profileId']],
'filters'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(buildWithoutMongoIdFindOptions, [], 'options')
)
),

flatMapAsync(
withPagination(
withLogging(
applyPayloadArgs(
findProfiles,
[
['context', 'db', 'profiles'],
['context', 'data', 'filters'],
['context', 'data', 'options'],
],
'paginatedFollowers'
)
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
normalizeFollowers,
[
['context', 'fileStorageDriver'],
['context', 'data', 'paginatedFollowers', 'data'],
],
'normalizedFollowers'
)
)
),

flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'normalizedFollowers'],
['context', 'data', 'paginatedFollowers', 'metadata'],
],
'normalizedBody'
)
),

lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'normalizedBody'], 200]
)
)
),
method: 'GET',
path: '/profiles/:profileId/followers',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

createOrganizationFollowRoute

Implementation

Endpoint: PUT /profiles/:profileId/organization-follows/:followOrganizationId

Access: authenticated administrator or owner.

Request: both path IDs.

Pipeline: verifies profile and organization, then createOrganizationFollow.

Success: 204

Failure: 404, duplicate 409, database 500.

View complete source
export const createOrganizationFollowRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getOrganizationById,
[
['context', 'db', 'organizations'],
['params', 'requestParams', 'followOrganizationId'],
],
'followOrganization'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
createOrganizationFollow,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'followOrganizationId'],
],
'createOrganizationFollowResult'
)
)
),

lift(
orThrow(
[
[OrganizationNotFoundError, 404],
[ProfileNotFoundBlockError, 404],
[OrganizationAlreadyFollowedBlockError, 409],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'PUT',
path: '/profiles/:profileId/organization-follows/:followOrganizationId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

deleteOrganizationFollowRoute

Implementation

Endpoint: DELETE /profiles/:profileId/organization-follows/:followOrganizationId

Access: authenticated administrator or owner.

Request: both path IDs.

Pipeline: verifies profile and organization, then deleteOrganizationFollow.

Success: 204

Failure: 404 or database 500.

View complete source
export const deleteOrganizationFollowRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getOrganizationById,
[
['context', 'db', 'organizations'],
['params', 'requestParams', 'followOrganizationId'],
],
'organization'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
deleteOrganizationFollow,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'followOrganizationId'],
],
'deleteProfileFollowResult'
)
)
),
lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[OrganizationFollowNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'DELETE',
path: '/profiles/:profileId/organization-follows/:followOrganizationId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

createProductLikeRoute

Implementation

Endpoint: PUT /profiles/:profileId/product-likes/:likeProductId

Access: authenticated administrator or owner.

Request: both path IDs.

Pipeline: verifies profile and product, then createProductLike.

Success: 204

Failure: 404, duplicate 409, database 500.

View complete source
export const createProductLikeRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getProductById,
[
['context', 'db', 'products'],
['params', 'requestParams', 'likeProductId'],
],
'likeProduct'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
createProductLike,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'likeProductId'],
],
'createProductLikeResult'
)
)
),
lift(
orThrow(
[
[ProductNotFoundBlockError, 404],
[ProfileNotFoundBlockError, 404],
[ProductAlreadyLikedBlockError, 409],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'PUT',
path: '/profiles/:profileId/product-likes/:likeProductId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

deleteProductLikeRoute

Implementation

Endpoint: DELETE /profiles/:profileId/product-likes/:likeProductId

Access: authenticated administrator or owner.

Request: both path IDs.

Pipeline: verifies profile and product, then deleteProductLike.

Success: 204

Failure: 404 or database 500.

View complete source
export const deleteProductLikeRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getProductById,
[
['context', 'db', 'products'],
['params', 'requestParams', 'likeProductId'],
],
'profile'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
deleteProductLike,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'likeProductId'],
],
'deleteProfileFollowResult'
)
)
),
lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[ProductLikeNotFoundBlockError, 404],
[ProductNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
[ProductNotFoundBlockError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'DELETE',
path: '/profiles/:profileId/product-likes/:likeProductId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});