Getting Started#
Build your first AT Protocol app in minutes with the Slices CLI.
Quick Start#
Install the CLI#
You'll need Deno installed first. Get it at deno.com.
# Install from JSR
deno install -g -A jsr:@slices/cli --name slices
Login to Slices#
Before creating a project, authenticate with your AT Protocol account:
slices login
This will open a browser window where you can authorize the CLI with your AT Protocol handle.
Create Your Project#
# Create a new project with automatic setup
slices init my-vinyl-app
# Or let us generate a name/domain for you
slices init
The slices init command does everything for you:
- Creates a full-stack Deno app with OAuth authentication
- Automatically creates a slice on the network
- Sets up OAuth credentials
- Pulls standard lexicons (including Bluesky profiles)
- Generates a TypeScript SDK
- Initializes a git repository
Start Developing#
cd my-vinyl-app
deno task dev
Visit http://localhost:8080 and you're live!
What You Get#
The slices init command creates a production-ready app with:
More templates/examples coming soon (i.e. React, Expo, Astro, etc)
Full-Stack Deno Application#
- Server-side rendering with Preact and JSX
- OAuth authentication with PKCE flow and automatic token refresh
- HTMX integration for dynamic UI without complex JavaScript
- Tailwind CSS for beautiful, responsive styling
- SQLite sessions for secure session management
- Feature-based architecture for scalable code organization
AT Protocol Integration#
- Configured slice with your own namespace
- Generated TypeScript SDK from your lexicons
- Automatic sync capabilities
- Real-time updates via Jetstream
Development Experience#
- Hot reload in development
- Type safety throughout
- Environment variables pre-configured
- Git repository initialized
Project Structure#
Here's what the generated project structure looks like:
my-vinyl-app/
├── slices.json # Slice configuration
├── .env # Your credentials (auto-generated)
├── deno.json # Deno configuration
├── lexicons/ # AT Protocol schemas
│ ├── com/
│ │ └── recordcollector/
│ │ └── album.json
│ └── app/
│ └── bsky/
│ └── actor/
│ └── profile.json
└── src/
├── main.ts # Server entry point
├── config.ts # App configuration
├── generated_client.ts # Your TypeScript SDK
├── routes/ # HTTP routes
├── features/ # Feature modules
│ ├── auth/ # OAuth implementation
│ └── dashboard/ # Main app UI
├── shared/ # Reusable components
└── utils/ # Helper functions
CLI Commands#
The Slices CLI is your command center:
Project Management#
# Create a new project
slices init my-app
# Check your authentication status
slices status
# Authenticate with Slices network
slices login
Lexicon Management#
# Pull lexicons from your slice
slices lexicon pull
# Push local lexicons to your slice
slices lexicon push
# List lexicons in your slice
slices lexicon list
Code Generation#
# Generate TypeScript SDK from lexicons
slices codegen
# The SDK is created at src/generated_client.ts
Monitoring#
# View real-time Jetstream logs
slices logs
# See sync activity and data flow
slices logs --verbose
Working with Lexicons#
Lexicons define your data structure. The init command includes Bluesky profile lexicons to get you started, but you'll want to add your own custom lexicons for your app.
Modify an Existing Lexicon#
Edit lexicons/com/recordcollector/album.json:
{
"lexicon": 1,
"id": "com.recordcollector.album",
"defs": {
"main": {
"type": "record",
"description": "A vinyl album in my collection",
"record": {
"type": "object",
"required": ["title", "artist", "releaseDate"],
"properties": {
"title": { "type": "string" },
"artist": { "type": "string" },
"releaseDate": {
"type": "string",
"format": "datetime"
},
"genre": {
"type": "array",
"items": { "type": "string" }
},
"condition": {
"type": "string",
"enum": [
"Mint",
"Near Mint",
"Very Good Plus",
"Very Good",
"Good",
"Fair",
"Poor"
]
},
"notes": { "type": "string" }
}
}
}
}
}
Create a New Lexicon#
Create lexicons/com/recordcollector/review.json:
{
"lexicon": 1,
"id": "com.recordcollector.review",
"defs": {
"main": {
"type": "record",
"description": "Album review",
"record": {
"type": "object",
"required": ["albumUri", "rating", "content"],
"properties": {
"albumUri": {
"type": "string",
"format": "at-uri"
},
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5
},
"content": { "type": "string" },
"createdAt": {
"type": "string",
"format": "datetime"
}
}
}
}
}
}
Update Your Slice#
After modifying lexicons:
# Push changes to your slice
slices lexicon push
# Regenerate the TypeScript SDK
slices codegen
Your SDK at src/generated_client.ts now includes the new types and methods!
Important: You must push your lexicons to the slice before you can create records. The slice needs to know about your schema in order to validate and store records.
Using the Generated SDK#
The generated SDK provides a type-safe client for your slice:
import { AtprotoClient } from "./generated_client.ts";
// Initialize the client
const client = new AtprotoClient({
baseUrl: "https://api.slices.network",
sliceUri: Deno.env.get("SLICE_URI")!,
});
// List albums with filtering and sorting
const albums = await client.com.recordcollector.album.getRecords({
where: {
genre: { contains: "jazz" },
},
sortBy: [{ field: "releaseDate", direction: "desc" }],
limit: 20,
});
// Add a new album
const newAlbum = await client.com.recordcollector.album.createRecord({
title: "Kind of Blue",
artist: "Miles Davis",
releaseDate: "1959-08-17T00:00:00Z",
genre: ["jazz", "modal jazz"],
condition: "Near Mint",
notes: "Original Columbia pressing",
});
// Get a specific album
const album = await client.com.recordcollector.album.getRecord({
uri: newAlbum.uri,
});
// Update an album
await client.com.recordcollector.album.updateRecord({
uri: album.uri,
record: {
...album.value,
notes: "Verified as first pressing!",
},
});
// Delete an album
await client.com.recordcollector.album.deleteRecord({
uri: album.uri,
});
External Collections#
Since the init command included Bluesky profile lexicons, your SDK has methods for querying them:
// Query users by display name (from included Bluesky lexicons)
const profiles = await client.app.bsky.actor.profile.getRecords({
where: {
displayName: { contains: "vinyl collector" },
},
});
Any record-type lexicon you add to your slice will generate corresponding SDK
methods when you run slices codegen.
Syncing Data#
Once your app is running, you can sync data from the AT Protocol network.
User Authentication Sync#
When users log in via OAuth, you can sync their data using the
syncUserCollections method. This discovers and imports their external
collections (like Bluesky profiles and posts).
// After user logs in
await client.network.slices.slice.syncUserCollections();
Manual Bulk Sync#
Use the web interface at https://slices.network to start a bulk sync job. Navigate to your slice's Sync tab to configure which collections and repositories to sync.
Note: If you created new lexicons, you'll be the only one with records initially. As more users adopt your app and write records to their own PDSs, you can sync from their repositories to grow your network.
Real-time Updates#
Jetstream automatically tracks creates, updates, and deletes across the network:
# Monitor real-time sync
slices logs --slice $SLICE_URI
Deployment#
Your app is ready for production deployment.
Deno Deploy#
Create a free account at deno.com/deploy. Push your code to GitHub, then connect your repository through the Deno Deploy dashboard to deploy your app.
For production use with Deno Deploy, switch from SQLite to Deno KV for OAuth and session storage:
import { DenoKVOAuthStorage } from "@slices/oauth";
import { DenoKVAdapter } from "@slices/session";
// Configure OAuth with Deno KV storage
const oauthClient = new OAuthClient({
clientId: Deno.env.get("OAUTH_CLIENT_ID")!,
clientSecret: Deno.env.get("OAUTH_CLIENT_SECRET")!,
authBaseUrl: Deno.env.get("OAUTH_AIP_BASE_URL")!,
redirectUri: Deno.env.get("OAUTH_REDIRECT_URI")!,
storage: new DenoKVOAuthStorage(), // Uses Deno KV
});
// Configure sessions with Deno KV adapter
const sessionStore = new SessionStore({
adapter: new DenoKVAdapter(), // Uses Deno KV
cookieOptions: {
secure: true,
httpOnly: true,
},
});
Deno KV provides serverless-compatible storage that scales automatically with your deployment.
Manual Setup (Advanced)#
If you prefer to set things up manually or need custom configuration:
1. Create a Slice via Web UI#
Visit https://slices.network and:
- Log in with your AT Protocol account
- Click "Create Slice"
- Choose your namespace (e.g.,
com.recordcollector)
2. Create OAuth Credentials#
In your slice dashboard:
- Go to Settings → OAuth Clients
- Create a new client
- Set redirect URI:
http://localhost:8080/oauth/callback - Copy the Client ID and Secret
3. Set Up Your Project#
Use any framework you prefer. You can use the generated TypeScript SDK (works with any JavaScript/TypeScript environment) or call the XRPC endpoints directly from any language:
# Configure environment
cp .env.example .env
# Edit .env with your credentials
# Start your project
# (commands depend on your framework choice)
Next Steps#
- Core Concepts: Understand slices, lexicons, sync, and code generation
- API Reference: Detailed endpoint documentation
- SDK Usage: Advanced SDK patterns and examples
- Examples: Sample applications and use cases
Need Help?#
- Join our Discord community
- Check out example apps
- Read the AT Protocol docs
- Report issues on Tangled