🎯 What You Can Build
CI/CD Pipeline Gates
Fail a build when a pull request introduces breaking schema changes. Catch dropped columns and NOT_NULL mismatches before they reach production.
Slack Alerts for Schema Drift
Post a formatted diff summary to Slack whenever staging and production schemas diverge. Include risk scores and migration SQL.
Nightly Drift Monitoring
Run a cron job every night that compares your committed schema against your production database. Detect undocumented changes automatically.
🔑 Step 1 — Get a Pro License Key
Purchase a Pro license
API access requires a SchemaLens Pro license key. Keys are delivered instantly after purchase.
Format
License keys look like SL-XXXX-XXXX-XXXX-XXXX. Pass them in the X-License-Key header or licenseKey body field.
🚀 Step 2 — Make Your First Request
The API has one primary endpoint. Send two SQL schemas and receive a full diff, migration SQL, breaking change analysis, and risk score.
Choose your language
curl -X POST https://schemalens.tech/api/diff \
-H "Content-Type: application/json" \
-H "X-License-Key: SL-XXXX-XXXX-XXXX-XXXX" \
-d '{
"schemaA": "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255));",
"schemaB": "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255), email VARCHAR(255));",
"dialect": "postgres",
"format": "json"
}'
const response = await fetch('https://schemalens.tech/api/diff', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-License-Key': 'SL-XXXX-XXXX-XXXX-XXXX'
},
body: JSON.stringify({
schemaA: 'CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255));',
schemaB: 'CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255), email VARCHAR(255));',
dialect: 'postgres',
format: 'json'
})
});
const data = await response.json();
console.log(data.migration);
// ALTER TABLE "users" ADD "email" VARCHAR ( 255 );
import requests
response = requests.post(
'https://schemalens.tech/api/diff',
headers={
'Content-Type': 'application/json',
'X-License-Key': 'SL-XXXX-XXXX-XXXX-XXXX'
},
json={
'schemaA': 'CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255));',
'schemaB': 'CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255), email VARCHAR(255));',
'dialect': 'postgres',
'format': 'json'
}
)
data = response.json()
print(data['migration'])
# ALTER TABLE "users" ADD "email" VARCHAR ( 255 );
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type DiffRequest struct {
SchemaA string `json:"schemaA"`
SchemaB string `json:"schemaB"`
Dialect string `json:"dialect"`
Format string `json:"format"`
}
func main() {
payload := DiffRequest{
SchemaA: "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255));",
SchemaB: "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255), email VARCHAR(255));",
Dialect: "postgres",
Format: "json",
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://schemalens.tech/api/diff", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-License-Key", "SL-XXXX-XXXX-XXXX-XXXX")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result["migration"])
}
📤 Step 3 — Understand the Response
{
"diff": {
"tablesAdded": [],
"tablesRemoved": [],
"tablesModified": [
{
"name": "users",
"columnsAdded": [{ "name": "email", "type": "VARCHAR(255)", ... }],
"columnsRemoved": [],
"columnsModified": []
}
],
"enumsAdded": [],
"enumsRemoved": [],
"triggersAdded": [],
"triggersRemoved": [],
"triggersModified": [],
"viewsAdded": [],
"viewsRemoved": [],
"viewsModified": [],
"functionsAdded": [],
"functionsRemoved": [],
"functionsModified": []
},
"migration": "ALTER TABLE \"users\" ADD \"email\" VARCHAR ( 255 );",
"breakingChanges": [],
"riskScore": {
"score": 0,
"label": "Safe",
"icon": "🟢"
},
"summary": {
"tablesAdded": 0,
"tablesRemoved": 0,
"tablesModified": 1,
"enumsAdded": 0,
"enumsRemoved": 0,
"triggersAdded": 0,
"triggersRemoved": 0,
"triggersModified": 0,
"viewsAdded": 0,
"viewsRemoved": 0,
"viewsModified": 0,
"functionsAdded": 0,
"functionsRemoved": 0,
"functionsModified": 0,
"breakingChangeCount": 0
}
}
Response Fields
- diff
- Full semantic diff with added, removed, and modified tables, columns, constraints, indexes, enums, triggers, views, and functions.
- migration
- Generated ALTER TABLE / CREATE TABLE SQL to migrate from schemaA to schemaB.
- breakingChanges
- Array of dangerous changes (DROP_TABLE, DROP_COLUMN, ADD_NOT_NULL_NO_DEFAULT, etc.). Empty if safe.
- riskScore
- Object with
score(0-100),label(Safe / Low / Medium / High / Critical), andicon. - summary
- Counts of all object changes for quick dashboard display.
🛠️ Step 4 — Common Patterns
Pattern A: Fail a CI Build on Breaking Changes
#!/bin/bash
set -e
RESPONSE=$(curl -s -X POST https://schemalens.tech/api/diff \
-H "Content-Type: application/json" \
-H "X-License-Key: $SCHEMALENS_KEY" \
-d "{\"schemaA\":$(cat base.sql | jq -Rs .),\"schemaB\":$(cat head.sql | jq -Rs .),\"dialect\":\"postgres\"}")
COUNT=$(echo "$RESPONSE" | jq '.summary.breakingChangeCount')
if [ "$COUNT" -gt 0 ]; then
echo "❌ Breaking changes detected:"
echo "$RESPONSE" | jq '.breakingChanges'
exit 1
fi
echo "✅ Schema diff passed. No breaking changes."
Pattern B: Post Diff Summary to Slack
# First, get the diff
DIFF=$(curl -s -X POST https://schemalens.tech/api/diff \
-H "Content-Type: application/json" \
-H "X-License-Key: $SCHEMALENS_KEY" \
-d '{"schemaA":"...","schemaB":"...","dialect":"postgres"}')
# Then, send to Slack via SchemaLens webhook proxy
curl -X POST https://schemalens.tech/api/slack \
-H "Content-Type: application/json" \
-d "{
\"webhookUrl\": \"$SLACK_WEBHOOK_URL\",
\"diff\": $(echo "$DIFF" | jq '.diff'),
\"migration\": $(echo "$DIFF" | jq '.migration'),
\"breakingChanges\": $(echo "$DIFF" | jq '.breakingChanges'),
\"dialect\": \"postgres\"
}"
Pattern C: Markdown Report for PR Comments
curl -X POST https://schemalens.tech/api/diff \
-H "Content-Type: application/json" \
-H "X-License-Key: SL-XXXX-XXXX-XXXX-XXXX" \
-d '{
"schemaA": "...",
"schemaB": "...",
"dialect": "postgres",
"format": "markdown"
}' | jq -r '.markdown' > schema-diff-report.md
# Post the markdown to GitHub PR comment via gh CLI
gh pr comment 42 --body-file schema-diff-report.md
⚠️ Error Handling
| Status | Meaning | How to Fix |
|---|---|---|
| 400 | Bad Request | Check that schemaA and schemaB are strings and dialect is valid. |
| 401 | Unauthorized | Provide a valid Pro licenseKey or X-License-Key header. |
| 429 | Rate Limited | Wait 60 seconds. Maximum 30 requests per minute per IP. |
| 500 | Server Error | Retry with exponential backoff. Contact support if persistent. |
📋 Parameters Reference
| Parameter | Type | Required | Description |
|---|---|---|---|
schemaA | string | Yes | The old (base) schema SQL. |
schemaB | string | Yes | The new (target) schema SQL. |
dialect | string | No | postgres (default), mysql, sqlite, mssql, oracle |
format | string | No | json (default) or markdown |
licenseKey | string | Yes | SchemaLens Pro license key. Also accepted as X-License-Key header. |
🔒 Privacy & Limits
- Stateless: Schema data is processed in-memory and never stored or logged.
- Rate limit: 30 requests per minute per IP address.
- No external dependencies: The API runs on Vercel's edge network with sub-100ms response times.
- CORS enabled: Call the API from any domain, including localhost during development.
💻 CLI Alternative
For offline use or air-gapped environments, the SchemaLens CLI provides the same engine without network requests.
node ci/schemalens-diff.js schemaA.sql schemaB.sql --dialect=postgres --format=json
Ready to automate your schema reviews?
Get a SchemaLens Pro license and start diffing programmatically today.
Get Pro — $12/mo Full API Docs →