Implementation Guide
Installation
npm install @shellapps/data-contractBasic Implementation
import { createDataContract } from '@shellapps/data-contract'
const contract = createDataContract({
secret: process.env.DATA_CONTRACT_SECRET!,
describe: async (userId) => ({
fields: [
{
name: 'profile',
description: 'User profile',
fields: [
{ name: 'email', type: 'string', description: 'Email address' },
{ name: 'name', type: 'string', description: 'Display name' },
],
},
],
}),
export: async (userId) => {
const user = await db.users.findById(userId)
return {
profile: { email: user.email, name: user.name },
}
},
delete: async (userId) => {
await db.users.deleteById(userId)
return { status: 'completed' }
},
})
export default contractExpress Integration
import express from 'express'
import { createDataContract } from '@shellapps/data-contract'
const app = express()
app.use(express.json())
const contract = createDataContract({
secret: process.env.DATA_CONTRACT_SECRET!,
describe: async (userId) => ({
fields: [
{
name: 'profile',
description: 'User profile data',
fields: [
{ name: 'email', type: 'string', description: 'Email' },
{ name: 'displayName', type: 'string', description: 'Name' },
],
},
],
}),
export: async (userId) => {
const user = await db.users.findById(userId)
const orders = await db.orders.findByUser(userId)
return { profile: user, orders }
},
delete: async (userId) => {
const trackingId = await db.deletions.schedule(userId)
return { status: 'pending', trackingId, estimatedCompletionMs: 60000 }
},
})
app.post('/data-contract/:action', contract.handler)
app.listen(3000)Async Deletion
If deletion takes time (cleaning up across multiple services), return a pending status with a trackingId. The platform will poll until completion:
delete: async (userId, trackingId) => {
// If polling an existing deletion
if (trackingId) {
const deletion = await db.deletions.find(trackingId)
return { status: deletion.completed ? 'completed' : 'pending', trackingId }
}
// Start new deletion
const id = await backgroundDelete(userId)
return { status: 'pending', trackingId: id, estimatedCompletionMs: 30000 }
},Testing
Use the built-in test helper to verify your implementation:
import { testDataContract } from '@shellapps/data-contract/testing'
describe('Data Contract', () => {
testDataContract({
handler: contract.handler,
secret: process.env.DATA_CONTRACT_SECRET!,
testUserId: 'usr_test_123',
// Optional: seed test data before running
setup: async () => {
await db.users.create({ id: 'usr_test_123', email: 'test@example.com' })
},
// Optional: clean up after
teardown: async () => {
await db.users.deleteById('usr_test_123')
},
})
})This validates that:
describereturns valid field descriptorsexportreturns data for the test userdeleteremoves the user's data- HMAC authentication is enforced
- Invalid signatures are rejected