SpiceDB Zed Schema Parser
A TypeScript library for parsing, analyzing, and generating type-safe SDKs from SpiceDB's .zed schema files.
Overview
This library provides a complete toolchain for working with SpiceDB schemas, transforming its schema DSL into type-safe TypeScript APIs. It consists of three main components:
- Schema Parser - Parses
.zedfiles into structured ASTs using Chevrotain - Semantic Analyzer - Performs semantic analysis and type inference on the parsed AST
- SDK Generator - Creates type-safe TypeScript SDKs from analyzed schemas
Additionally, it includes a Fluent Builder Library that provides an ergonomic API for SpiceDB operations, serving as a bridge between the verbose @authzed/authzed-node gRPC client and your type-safe generated SDK.
Problems Solved
1. Type Safety
Convert string-based SpiceDB operations into compile-time checked TypeScript:
await client.checkPermission({
resource: {
objectType: "document",
objectId: "doc1",
},
permission: "edit", // Could be misspelled
subject: {
object: {
objectType: "user",
objectId: "alice",
},
},
});
// Type-safe: generated from your schema
await permissions.document.check.edit("user:alice", "document:doc1").execute();
2. Schema Validation
Catch schema errors early with comprehensive semantic analysis:
- Undefined type references
- Circular dependencies
- Invalid permission expressions
- Duplicate definitions
3. Developer Experience
Replace verbose gRPC objects with fluent, chainable APIs:
(Note: this layer is not type-safe, but you can drop down to it if the type-safe SDK you generate from your schema.zed file is insufficient to the task).
await client.writeRelationships({
updates: [
{
operation: RelationshipUpdate_Operation.TOUCH,
relationship: {
resource: { objectType: "document", objectId: "doc1" },
relation: "editor",
subject: { object: { objectType: "user", objectId: "alice" } },
},
},
],
});
// Fluent builder style
await perms
.grant("editor")
.subject("user:alice")
.resource("document:doc1")
.execute();
4. Code Generation
Automatically generate SDKs that stay in sync with schema changes, preventing runtime errors when schemas evolve.
Installation
Quick Start
Here's a complete example of parsing a schema and generating a type-safe SDK:
import {
parseSpiceDBSchema,
analyzeSpiceDbSchema,
generateSDK,
} from "@schoolai/spicedb-zed-schema-parser";
async function generatePermissionsSDK() {
// 1. Read your schema file
const schemaContent = await fs.readFile("schema.zed", "utf-8");
// 2. Parse the schema
const { ast, errors: parseErrors } = parseSpiceDBSchema(schemaContent);
if (parseErrors.length > 0) {
console.error("Parse errors:", parseErrors);
return;
}
// 3. Analyze the schema
const {
augmentedAst,
errors: analysisErrors,
isValid,
} = analyzeSpiceDbSchema(ast!);
if (!isValid) {
console.error("Analysis errors:", analysisErrors);
return;
}
// 4. Generate TypeScript SDK
const generatedCode = generateSDK(augmentedAst!);
// 5. Write to file
await fs.writeFile("generated/permissions.ts", generatedCode);
console.log(" Type-safe permissions SDK generated!");
}
Example Schema
definition user {}
definition document {
relation owner: user
relation editor: user
relation viewer: user
permission edit = owner + editor
permission view = owner + editor + viewer
}
definition folder {
relation owner: user
relation editor: user
relation parent: folder
permission edit = owner + editor + parent->edit
permission view = owner + editor + parent->view
}
Generated SDK Usage
The generated SDK provides type-safe methods for all your schema operations:
// Type-safe operations - TypeScript will catch typos and invalid combinations
await permissions.document.grant
.editor("user:alice", "document:doc1")
.execute();
await permissions.document.check.view("user:bob", "document:doc1").execute();
await permissions.folder.find.byOwner("user:alice").execute();
// TypeScript errors for invalid operations
await permissions.document.grant.invalidRelation("user:alice", "document:doc1"); // Error!
await permissions.document.check.edit("invalid:type", "document:doc1"); // Error!
Fluent Builder API
For cases where you need dynamic operations or are migrating from string-based APIs, use the fluent builder:
createPermissions,
Operations,
} from "@schoolai/spicedb-zed-schema-parser";
const perms = createPermissions(spicedbClient);
// Grant permissions
await perms
.grant("editor")
.subject("user:alice")
.resource("document:doc1")
.execute();
// Check permissions
const hasPermission = await perms
.check("view")
.subject("user:bob")
.resource("document:doc1")
.execute();
perms.batch()
.add(perms.grant("viewer").subject("user:charlie").resource("folder:f1"))
.add(perms.revoke("editor").subject("user:alice").resource("document:doc1"))
.execute()
// Use static builders for pure operations
const deleteOp = Operations.delete().where({
resourceType: "document",
resourceId: "doc1",
});
await perms.execute(deleteOp);
API Reference
Core Functions
parseSpiceDBSchema(text: string): ParseResult
Parses a SpiceDB schema string into an AST.
analyzeSpiceDbSchema(ast: SchemaAST): SchemaAnalysisResult
Performs semantic analysis on a parsed schema.
generateSDK(schema: AugmentedSchemaAST): string
Generates TypeScript code for a type-safe permissions SDK.
Builder Classes
createPermissions(client: SpiceDBClient): Permissions
Creates a permissions instance with bound SpiceDB client.
Operations (Static Builder)
Provides static methods for creating pure operations:
Operations.grant(relation: string)Operations.revoke(relation: string)Operations.check(permission: string)Operations.find()Operations.delete()Operations.batch()
Schema Features Supported
- Definitions - Object type definitions
- Relations - Direct relations between objects
- Permissions - Computed permissions with complex expressions
- Caveats - Conditional logic (basic support)
- Union expressions -
permission = rel1 + rel2 - Intersection expressions -
permission = rel1 & rel2 - Exclusion expressions -
permission = rel1 - rel2 - Arrow expressions -
permission = rel->permission - Wildcard relations -
relation public: user:* - Sub-relations -
relation editor: user#admin - Doc comments -
/** documentation */
Error Handling
The library provides comprehensive error reporting:
Parse Errors
if (errors.length > 0) {
errors.forEach((err) => {
console.error(`${err.message} at line ${err.line}, column ${err.column}`);
});
}
Semantic Errors
if (!isValid) {
errors.forEach((err) => {
console.error(`${err.code}: ${err.message}`);
});
}
Common error types:
UNDEFINED_TYPE- Referenced type doesn't existCIRCULAR_DEPENDENCY- Circular permission dependenciesDUPLICATE_DEFINITION- Duplicate type namesUNDEFINED_RELATION- Referenced relation doesn't existINVALID_EXPRESSION- Malformed permission expression
Architecture
+-------------+ +--------------+ +-----------------+ +--------------+
| .zed Schema |--->| Parser |--->| Semantic |--->| SDK |
| | | (Chevrotain) | | Analyzer | | Generator |
+-------------+ +--------------+ +-----------------+ +--------------+
| | |
V V V
+--------------+ +-----------------+ +--------------+
| AST | | Augmented AST | | TypeScript |
| | | + Type Info | | SDK Code |
+--------------+ +-----------------+ +--------------+
^
|
+-----------------------------------------------------------------------------+
| Fluent Builder Library |
| +-------------+ +--------------+ +-----------------+ |
| | Operations |--->| Fluent API |--->| SpiceDB Client | |
| | Builder | | (Chainable) | | (gRPC) | |
| +-------------+ +--------------+ +-----------------+ |
+-----------------------------------------------------------------------------+
Development
Building
Testing
Linting
pnpm run lint:fix
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes and add tests
- Run the test suite:
pnpm test - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
License
Open source under the MIT license
Related Projects
- SpiceDB - The authorization system this library supports
- @authzed/authzed-node - Official Node.js client for SpiceDB
- Chevrotain - Parser building toolkit used for
.zedparsing