Data Contract
Implementation Guide

Implementation Guide

Installation

npm install @shellapps/data-contract

Basic 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 contract

Express 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:

  • describe returns valid field descriptors
  • export returns data for the test user
  • delete removes the user's data
  • HMAC authentication is enforced
  • Invalid signatures are rejected

© 2026 Shell Technology. All rights reserved.