Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/api/docs/docs.go

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions core/api/docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,28 @@
},
"type": "object"
},
"BatchGetObjectsRequest": {
"properties": {
"ids": {
"description": "The IDs of the objects to fetch; must be retrieved from ListObjects, SearchSpace or GlobalSearch endpoints or obtained from response context",
"example": [
"[\"bafyrei...\"",
"\"bafyrej...\"]"
],
"items": {
"type": "string"
},
"maxItems": 100,
"minItems": 1,
"type": "array",
"uniqueItems": false
}
},
"required": [
"ids"
],
"type": "object"
},
"ChatAttachment": {
"properties": {
"target": {
Expand Down Expand Up @@ -6478,6 +6500,108 @@
]
}
},
"/v1/spaces/{space_id}/objects/batch": {
"post": {
"description": "Fetches multiple objects by their IDs within the specified space in a single request. Objects that cannot be found are represented by a \"null\" entry in the response, preserving the order of the requested IDs. This avoids the need to issue one request per object when a client operates on a known set of objects (e.g. a list of tasks or commitments).",
"operationId": "get_objects_batch",
"parameters": [
{
"description": "The version of the API to use",
"in": "header",
"name": "Anytype-Version",
"required": true,
"schema": {
"default": "2025-11-08",
"type": "string"
}
},
{
"description": "The ID of the space in which the objects exist; must be retrieved from ListSpaces endpoint",
"in": "path",
"name": "space_id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BatchGetObjectsRequest"
}
}
},
"description": "The IDs of the objects to fetch",
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/ObjectResponse"
},
"type": "array"
}
}
},
"description": "The requested objects in the same order as the requested IDs, with a null placeholder for any ID that could not be found"
},
"400": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidationError"
}
}
},
"description": "Bad request"
},
"401": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedError"
}
}
},
"description": "Unauthorized"
},
"429": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RateLimitError"
}
}
},
"description": "Rate limit exceeded"
},
"500": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServerError"
}
}
},
"description": "Internal server error"
}
},
"security": [
{
"bearerauth": []
}
],
"summary": "Get objects by ID",
"tags": [
"Objects"
]
}
},
"/v1/spaces/{space_id}/objects/{object_id}": {
"delete": {
"description": "This endpoint “deletes” an object by marking it as archived. The deletion process is performed safely and is subject to rate limiting. It returns the object’s details after it has been archived. Proper error handling is in place for situations such as when the object isn’t found or the deletion cannot be performed because of permission issues.",
Expand Down
86 changes: 86 additions & 0 deletions core/api/docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@ components:
type: array
uniqueItems: false
type: object
BatchGetObjectsRequest:
properties:
ids:
description: The IDs of the objects to fetch; must be retrieved from ListObjects,
SearchSpace or GlobalSearch endpoints or obtained from response context
example:
- '["bafyrei..."'
- '"bafyrej..."]'
items:
type: string
maxItems: 100
minItems: 1
type: array
uniqueItems: false
required:
- ids
type: object
ChatAttachment:
properties:
target:
Expand Down Expand Up @@ -4992,6 +5009,75 @@ paths:
summary: Update object
tags:
- Objects
/v1/spaces/{space_id}/objects/batch:
post:
description: Fetches multiple objects by their IDs within the specified space
in a single request. Objects that cannot be found are represented by a "null"
entry in the response, preserving the order of the requested IDs. This avoids
the need to issue one request per object when a client operates on a known
set of objects (e.g. a list of tasks or commitments).
operationId: get_objects_batch
parameters:
- description: The version of the API to use
in: header
name: Anytype-Version
required: true
schema:
default: "2025-11-08"
type: string
- description: The ID of the space in which the objects exist; must be retrieved
from ListSpaces endpoint
in: path
name: space_id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/BatchGetObjectsRequest'
description: The IDs of the objects to fetch
required: true
responses:
"200":
content:
application/json:
schema:
items:
$ref: '#/components/schemas/ObjectResponse'
type: array
description: The requested objects in the same order as the requested IDs,
with a null placeholder for any ID that could not be found
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: Bad request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/UnauthorizedError'
description: Unauthorized
"429":
content:
application/json:
schema:
$ref: '#/components/schemas/RateLimitError'
description: Rate limit exceeded
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ServerError'
description: Internal server error
security:
- bearerauth: []
summary: Get objects by ID
tags:
- Objects
/v1/spaces/{space_id}/properties:
get:
description: |-
Expand Down
54 changes: 52 additions & 2 deletions core/api/handler/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func ListObjectsHandler(s *service.Service) gin.HandlerFunc {
// @Param Anytype-Version header string true "The version of the API to use" default(2025-11-08)
// @Param space_id path string true "The ID of the space in which the object exists; must be retrieved from ListSpaces endpoint"
// @Param object_id path string true "The ID of the object to retrieve; must be retrieved from ListObjects, SearchSpace or GlobalSearch endpoints or obtained from response context"
// @Param format query apimodel.BodyFormat false "The format to return the object body in" default(md)
// @Param format query apimodel.BodyFormat false "The format to return the object body in" default(md)
// @Success 200 {object} apimodel.ObjectResponse "The retrieved object"
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
// @Failure 404 {object} util.NotFoundError "Resource not found"
Expand Down Expand Up @@ -201,7 +201,57 @@ func UpdateObjectHandler(s *service.Service) gin.HandlerFunc {
}
}

// DeleteObjectHandler deletes an object in a space
// BatchGetObjectsHandler retrieves multiple objects by ID in a single request
//
// @Summary Get objects by ID
// @Description Fetches multiple objects by their IDs within the specified space in a single request. Objects that cannot be found are represented by a "null" entry in the response, preserving the order of the requested IDs. This avoids the need to issue one request per object when a client operates on a known set of objects (e.g. a list of tasks or commitments).
// @Id get_objects_batch
// @Tags Objects
// @Accept json
// @Produce json
// @Param Anytype-Version header string true "The version of the API to use" default(2025-11-08)
// @Param space_id path string true "The ID of the space in which the objects exist; must be retrieved from ListSpaces endpoint"
// @Param ids body apimodel.BatchGetObjectsRequest true "The IDs of the objects to fetch"
// @Success 200 {array} apimodel.ObjectResponse "The requested objects in the same order as the requested IDs, with a null placeholder for any ID that could not be found"
// @Failure 400 {object} util.ValidationError "Bad request"
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
// @Failure 429 {object} util.RateLimitError "Rate limit exceeded"
// @Failure 500 {object} util.ServerError "Internal server error"
// @Security bearerauth
// @Router /v1/spaces/{space_id}/objects/batch [post]
func BatchGetObjectsHandler(s *service.Service) gin.HandlerFunc {
return func(c *gin.Context) {
spaceId := c.Param("space_id")

request := apimodel.BatchGetObjectsRequest{}
if err := c.BindJSON(&request); err != nil {
apiErr := util.CodeToApiError(http.StatusBadRequest, err.Error())
c.JSON(http.StatusBadRequest, apiErr)
return
}

objects, err := s.BatchGetObjects(c.Request.Context(), spaceId, request.Ids)
code := util.MapErrorCode(err,
util.ErrToCode(service.ErrBatchTooManyIds, http.StatusBadRequest),
util.ErrToCode(service.ErrFailedRetrieveBatchObject, http.StatusInternalServerError),
)

if code != http.StatusOK {
apiErr := util.CodeToApiError(code, err.Error())
c.JSON(code, apiErr)
return
}

// Wrap each object in an ObjectResponse; nil entries become JSON null
responses := make([]*apimodel.ObjectResponse, len(objects))
for i, obj := range objects {
if obj != nil {
responses[i] = &apimodel.ObjectResponse{Object: *obj}
}
}
c.JSON(http.StatusOK, responses)
}
}
//
// @Summary Delete object
// @Description This endpoint “deletes” an object by marking it as archived. The deletion process is performed safely and is subject to rate limiting. It returns the object’s details after it has been archived. Proper error handling is in place for situations such as when the object isn’t found or the deletion cannot be performed because of permission issues.
Expand Down
8 changes: 6 additions & 2 deletions core/api/model/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,14 @@ type CreateObjectRequest struct {

type UpdateObjectRequest struct {
Name *string `json:"name" example:"My object"` // The name of the object
Icon *Icon `json:"icon" oneOf:"EmojiIcon,FileIcon,NamedIcon"` // The icon to set for the object
Icon *Icon `json:"icon" oneOf:"EmojiIcon,FileIcon,NamedIcon"` // The icon to set for the object
TypeKey *string `json:"type_key" example:"page"` // The key of the type of object to set
Properties *[]PropertyLinkWithValue `json:"properties" oneOf:"TextPropertyLinkValue,NumberPropertyLinkValue,SelectPropertyLinkValue,MultiSelectPropertyLinkValue,DatePropertyLinkValue,FilesPropertyLinkValue,CheckboxPropertyLinkValue,UrlPropertyLinkValue,EmailPropertyLinkValue,PhonePropertyLinkValue,ObjectsPropertyLinkValue"` // The properties to set for the object; see ListTypes or GetType endpoints for linked properties
Markdown *string `json:"markdown" example:"This is the updated body of the object. Markdown syntax is supported here."` // The updated body of the object
Markdown *string `json:"markdown" example:"This is the updated body of the object. Markdown syntax is supported here."` // The updated body of the object
}

type BatchGetObjectsRequest struct {
Ids []string `json:"ids" binding:"required,min=1,max=100" example:"[\"bafyrei...\",\"bafyrej...\"]"` // The IDs of the objects to fetch; must be retrieved from ListObjects, SearchSpace or GlobalSearch endpoints or obtained from response context
}

type ObjectResponse struct {
Expand Down
5 changes: 5 additions & 0 deletions core/api/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ func (srv *Server) registerObjectRoutes(v1 *gin.RouterGroup, eventService apicor
ensureAnalyticsEvent("OpenObject", eventService),
handler.GetObjectHandler(srv.service),
)
v1.POST("/spaces/:space_id/objects/batch",
writeRateLimitMW,
ensureAnalyticsEvent("BatchGetObjects", eventService),
handler.BatchGetObjectsHandler(srv.service),
)
v1.POST("/spaces/:space_id/objects",
writeRateLimitMW,
ensureAnalyticsEvent("CreateObject", eventService),
Expand Down
Loading
Loading