Data and Entities
You want a backend API for your data. In most frameworks, that means writing route handlers, validation, error handling, and database queries for every resource by hand.
In Slingshot, you define the model once and the framework generates the rest.
Your first entity
Section titled “Your first entity”import { defineApp, defineEntity, definePackage, entity, field } from '@lastshotlabs/slingshot';
const Task = defineEntity('Task', { namespace: 'app', fields: { id: field.string({ primary: true, default: 'uuid' }), title: field.string(), done: field.boolean({ default: false }), },});
const appPackage = definePackage({ name: 'app', entities: [entity({ config: Task })],});
export default defineApp({ port: 3000, packages: [appPackage] });That gives you five working routes:
# Create a taskcurl -X POST http://localhost:3000/tasks \ -H "Content-Type: application/json" \ -d '{ "title": "Ship v1" }'
# List all taskscurl http://localhost:3000/tasks
# Get one taskcurl http://localhost:3000/tasks/<id>
# Update a taskcurl -X PATCH http://localhost:3000/tasks/<id> \ -H "Content-Type: application/json" \ -d '{ "done": true }'
# Delete a taskcurl -X DELETE http://localhost:3000/tasks/<id>Plus full OpenAPI docs at http://localhost:3000/docs.
Add domain behavior with operations
Section titled “Add domain behavior with operations”CRUD is not always enough. A task tracker needs “complete” and “reopen” actions. A blog needs “publish” and “archive”. These are operations:
import { defineEntity, defineOperations, field, op } from '@lastshotlabs/slingshot';
const Task = defineEntity('Task', { namespace: 'app', fields: { id: field.string({ primary: true, default: 'uuid' }), title: field.string(), status: field.enum(['todo', 'done'] as const, { default: 'todo' }), },});
const TaskOps = defineOperations(Task, { complete: op.transition({ field: 'status', from: 'todo', to: 'done', match: { id: 'param:id' }, }), reopen: op.transition({ field: 'status', from: 'done', to: 'todo', match: { id: 'param:id' }, }),});# Complete a taskcurl -X POST http://localhost:3000/tasks/<id>/complete
# Reopen itcurl -X POST http://localhost:3000/tasks/<id>/reopenOperations show up in OpenAPI docs and respect the same auth and permission policies as generated CRUD routes.
Protect routes with auth
Section titled “Protect routes with auth”Add routes.defaults to require authentication on all generated routes:
const Task = defineEntity('Task', { namespace: 'app', fields: { id: field.string({ primary: true, default: 'uuid' }), title: field.string(), done: field.boolean({ default: false }), authorId: field.string(), }, routes: { defaults: { auth: 'userAuth' }, },});Now every generated route requires a valid session. Anonymous requests get a 401.
Swap the storage backend
Section titled “Swap the storage backend”Entities use in-memory storage by default — great for development. Switch to a real database with a one-line adapter change:
entity({ config: Task, operations: TaskOps, adapter: 'sqlite', // or 'postgres', 'mongodb', 'redis'});Your entity definition stays the same. Only the adapter changes. See Storage and Adapter Wiring for the full setup including connection config.
What you get built in
Section titled “What you get built in”GET,POST,PATCH,DELETECRUD routes- Request and response validation from your field definitions
- OpenAPI generation
- Route policy hooks for auth, permissions, middleware, and rate limits
- Adapter-backed storage: memory, SQLite, Postgres, MongoDB, Redis
Next in your journey
Section titled “Next in your journey”You can read and write data. The next concern is who is allowed to:
- Security: Auth and Permissions — identify the actor making the request, then control what they can do.
Go deeper into data
Section titled “Go deeper into data”- Entity System — the full authoring story
- defineEntity — fields, indexes, timestamps, and conventions
- Operations — lookups, transitions, and aggregates
- Route Policy — per-route auth, permissions, and middleware
- Storage and Adapter Wiring — backend setup and swapping
- Generated Routes, Overrides, and Extra Routes — customizing generated behavior