Loading...
Loading...
Apply when working with MasterData v2 entities, schemas, or MasterDataClient in VTEX IO apps. Covers data entities, JSON Schema definitions, CRUD operations, the masterdata builder, triggers, search and scroll operations, and schema lifecycle management. Use for storing, querying, and managing custom data in VTEX IO apps while avoiding the 60-schema limit through proper schema versioning.
npx skill4agent add vtexdocs/ai-skills vtex-io-masterdatamasterdatactx.clients.masterdatavtex-io-service-appsvtex-io-graphql-apivtex-io-app-structuremasterdatamasterdata/{entityName}/schema.json{vendor}_{appName}_{entityName}ctx.clients.masterdatamasterDataFor@vtex/clientswherev-indexedsearchDocumentsscrollDocumentsmasterdata| Method | Description |
|---|---|
| Retrieve a single document by ID |
| Create a new document, returns generated ID |
| Upsert a complete document |
| Upsert partial fields (patch) |
| Replace all fields of an existing document |
| Update specific fields only |
| Delete a document by ID |
| Search with filters, pagination, and field selection |
| Search with total count metadata |
| Iterate over large result sets |
wherewhere: "productId=12345 AND approved=true"
where: "rating>3"
where: "createdAt between 2025-01-01 AND 2025-12-31"VTEX IO App (node builder)
│
├── ctx.clients.masterdata.createDocument()
│ │
│ ▼
│ Master Data v2 API
│ │
│ ├── Validates against JSON Schema
│ ├── Indexes declared fields
│ └── Fires triggers (if conditions match)
│ │
│ ▼
│ HTTP webhook / Email / Action
│
└── ctx.clients.masterdata.searchDocuments()
│
▼
Master Data v2 (reads indexed fields for efficient queries)ctx.clients.masterdatamasterDataFor@vtex/clients/api/dataentities//api/dataentities/api.vtex.com/api/dataentitiesctx.clients.masterdata// Using MasterDataClient through ctx.clients
export async function getReview(ctx: Context, next: () => Promise<void>) {
const { id } = ctx.query
const review = await ctx.clients.masterdata.getDocument<Review>({
dataEntity: 'reviews',
id: id as string,
fields: ['id', 'productId', 'author', 'rating', 'title', 'text', 'approved'],
})
ctx.status = 200
ctx.body = review
await next()
}// Direct REST call to Master Data — bypasses client infrastructure
import axios from 'axios'
export async function getReview(ctx: Context, next: () => Promise<void>) {
const { id } = ctx.query
// No caching, no retry, no proper auth, no metrics
const response = await axios.get(
`https://api.vtex.com/api/dataentities/reviews/documents/${id}`,
{
headers: {
'X-VTEX-API-AppKey': process.env.VTEX_APP_KEY,
'X-VTEX-API-AppToken': process.env.VTEX_APP_TOKEN,
},
}
)
ctx.status = 200
ctx.body = response.data
await next()
}masterdatamasterdata/{
"$schema": "http://json-schema.org/schema#",
"title": "review-schema-v1",
"type": "object",
"properties": {
"productId": {
"type": "string"
},
"author": {
"type": "string"
},
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5
},
"title": {
"type": "string",
"maxLength": 200
},
"text": {
"type": "string",
"maxLength": 5000
},
"approved": {
"type": "boolean"
},
"createdAt": {
"type": "string",
"format": "date-time"
}
},
"required": ["productId", "rating", "title", "text"],
"v-default-fields": ["productId", "author", "rating", "title", "approved", "createdAt"],
"v-indexed": ["productId", "author", "approved", "rating", "createdAt"]
}// Saving documents without any schema — no validation, no indexing
await ctx.clients.masterdata.createDocument({
dataEntity: 'reviews',
fields: {
productId: '12345',
rating: 'five', // String instead of number — no validation!
title: 123, // Number instead of string — no validation!
},
})
// Searching on unindexed fields — full table scan, will time out on large datasets
await ctx.clients.masterdata.searchDocuments({
dataEntity: 'reviews',
where: 'productId=12345', // productId is not indexed — very slow
fields: ['id', 'rating'],
pagination: { page: 1, pageSize: 10 },
})masterdatamasterdata# Periodically clean up unused schemas
# List schemas for the entity
curl -X GET "https://{account}.vtexcommercestable.com.br/api/dataentities/reviews/schemas" \
-H "X-VTEX-API-AppKey: {appKey}" \
-H "X-VTEX-API-AppToken: {appToken}"
# Delete old schemas that are no longer in use
curl -X DELETE "https://{account}.vtexcommercestable.com.br/api/dataentities/reviews/schemas/old-schema-name" \
-H "X-VTEX-API-AppKey: {appKey}" \
-H "X-VTEX-API-AppToken: {appToken}"Never cleaning up schemas during development.
After 60 link cycles, the builder fails:
"Error: Maximum number of schemas reached for entity 'reviews'"
The app cannot be linked or installed until old schemas are deleted.{
"builders": {
"node": "7.x",
"graphql": "1.x",
"masterdata": "1.x"
},
"policies": [
{
"name": "outbound-access",
"attrs": {
"host": "api.vtex.com",
"path": "/api/*"
}
},
{
"name": "ADMIN_DS"
}
]
}{
"$schema": "http://json-schema.org/schema#",
"title": "review-schema-v1",
"type": "object",
"properties": {
"productId": {
"type": "string"
},
"author": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5
},
"title": {
"type": "string",
"maxLength": 200
},
"text": {
"type": "string",
"maxLength": 5000
},
"approved": {
"type": "boolean"
},
"createdAt": {
"type": "string",
"format": "date-time"
}
},
"required": ["productId", "rating", "title", "text"],
"v-default-fields": ["productId", "author", "rating", "title", "approved", "createdAt"],
"v-indexed": ["productId", "author", "approved", "rating", "createdAt"],
"v-cache": false
}masterDataFor// node/clients/index.ts
import { IOClients } from '@vtex/api'
import { masterDataFor } from '@vtex/clients'
interface Review {
id: string
productId: string
author: string
email: string
rating: number
title: string
text: string
approved: boolean
createdAt: string
}
export class Clients extends IOClients {
public get reviews() {
return this.getOrSet('reviews', masterDataFor<Review>('reviews'))
}
}// node/resolvers/reviews.ts
import type { ServiceContext } from '@vtex/api'
import type { Clients } from '../clients'
type Context = ServiceContext<Clients>
export const queries = {
reviews: async (
_root: unknown,
args: { productId: string; page?: number; pageSize?: number },
ctx: Context
) => {
const { productId, page = 1, pageSize = 10 } = args
const results = await ctx.clients.reviews.search(
{ page, pageSize },
['id', 'productId', 'author', 'rating', 'title', 'text', 'createdAt', 'approved'],
'', // sort
`productId=${productId} AND approved=true`
)
return results
},
}
export const mutations = {
createReview: async (
_root: unknown,
args: { input: { productId: string; rating: number; title: string; text: string } },
ctx: Context
) => {
const { input } = args
const email = ctx.vtex.storeUserEmail ?? 'anonymous@store.com'
const response = await ctx.clients.reviews.save({
...input,
author: email.split('@')[0],
email,
approved: false,
createdAt: new Date().toISOString(),
})
return ctx.clients.reviews.get(response.DocumentId, [
'id', 'productId', 'author', 'rating', 'title', 'text', 'createdAt', 'approved',
])
},
deleteReview: async (
_root: unknown,
args: { id: string },
ctx: Context
) => {
await ctx.clients.reviews.delete(args.id)
return true
},
}{
"name": "notify-moderator-on-new-review",
"active": true,
"condition": "approved=false",
"action": {
"type": "email",
"provider": "default",
"subject": "New review pending moderation",
"to": ["moderator@mystore.com"],
"body": "A new review has been submitted for product {{productId}} by {{author}}."
},
"retry": {
"times": 3,
"delay": { "addMinutes": 5 }
}
}// node/index.ts
import type { ParamsContext, RecorderState } from '@vtex/api'
import { Service } from '@vtex/api'
import { Clients } from './clients'
import { queries, mutations } from './resolvers/reviews'
export default new Service<Clients, RecorderState, ParamsContext>({
clients: {
implementation: Clients,
options: {
default: {
retries: 2,
timeout: 5000,
},
},
},
graphql: {
resolvers: {
Query: queries,
Mutation: mutations,
},
},
})axiosfetchctx.clients.masterdatamasterDataForwherev-indexedscrollDocumentsmasterdatamanifest.jsonwherev-indexedctx.clients.masterdatamasterDataForoutbound-accessADMIN_DS