Quick Start
You’re going to write one file: app.config.ts. That file declares your app. The CLI starts it.
You’ll add a route, an entity, and auth in three steps.
Install
Section titled “Install”bun add @lastshotlabs/slingshot hono zodStep 1: A route that works
Section titled “Step 1: A route that works”Create app.config.ts at the root of your project:
import { defineApp, definePackage, domain, route } from '@lastshotlabs/slingshot';
const appPackage = definePackage({ name: 'app', domains: [ domain({ name: 'health', basePath: '/health', routes: [ route.get({ path: '/', summary: 'Health check', handler: ({ respond }) => respond.json({ ok: true }), }), ], }), ],});
export default defineApp({ port: 3000, packages: [appPackage],});Boot it:
slingshot startTest it:
curl http://localhost:3000/health# → { "ok": true }Your API docs are already live at http://localhost:3000/docs.
What just happened. slingshot start discovered app.config.ts, imported its default
export, and booted a Hono server. defineApp is a typed identity helper — it’s literally
config => config, but it gives you full autocomplete on the config object. definePackage
created a feature module with one route. The route is typed, shows up in OpenAPI docs, and
can be tested with curl.
Step 2: Add a data model
Section titled “Step 2: Add a data model”Instead of writing five route handlers by hand, define one entity:
import { defineEntity, field } from '@lastshotlabs/slingshot';
export const Task = defineEntity('Task', { namespace: 'app', fields: { id: field.string({ primary: true, default: 'uuid' }), title: field.string(), done: field.boolean({ default: false }), },});Wire it into your config:
import { defineApp, definePackage, entity } from '@lastshotlabs/slingshot';import { Task } from './src/packages/app/task.entity';
const appPackage = definePackage({ name: 'app', entities: [entity({ config: Task })],});
export default defineApp({ port: 3000, packages: [appPackage],});Five routes, generated from one definition:
# Createcurl -X POST http://localhost:3000/tasks \ -H "Content-Type: application/json" \ -d '{ "title": "Ship v1" }'
# Listcurl http://localhost:3000/tasks
# Get onecurl http://localhost:3000/tasks/<id>
# Updatecurl -X PATCH http://localhost:3000/tasks/<id> \ -H "Content-Type: application/json" \ -d '{ "done": true }'
# Deletecurl -X DELETE http://localhost:3000/tasks/<id>All five routes show up in OpenAPI docs with request/response schemas.
Step 3: Add auth
Section titled “Step 3: Add auth”bun add @lastshotlabs/slingshot-authUpdate your entity to require login:
import { defineEntity, field } from '@lastshotlabs/slingshot';
export const Task = defineEntity('Task', { namespace: 'app', fields: { id: field.string({ primary: true, default: 'uuid' }), title: field.string(), done: field.boolean({ default: false }), }, routes: { defaults: { auth: 'userAuth' }, // protect all CRUD routes },});Then add the auth plugin to your config:
import { defineApp, definePackage, entity } from '@lastshotlabs/slingshot';import { createAuthPlugin } from '@lastshotlabs/slingshot-auth';import { Task } from './src/packages/app/task.entity';
const appPackage = definePackage({ name: 'app', entities: [entity({ config: Task })],});
export default defineApp({ port: 3000, security: { signing: { secret: 'dev-secret-change-in-prod' } }, plugins: [createAuthPlugin({ auth: { roles: ['user'], defaultRole: 'user' } })], packages: [appPackage],});Now your entity routes require a valid session, and you get auth routes for free:
# Registercurl -X POST http://localhost:3000/auth/register \ -H "Content-Type: application/json" \ -d '{ "email": "alice@example.com", "password": "supersecret" }'
# Login — returns a JWTcurl -X POST http://localhost:3000/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "alice@example.com", "password": "supersecret" }'
# Use the JWT on protected routescurl http://localhost:3000/tasks \ -H "Authorization: Bearer <token>"Already have auth upstream or want to use your own? You don’t need slingshot-auth — see
Bring Your Own Auth.
What to learn next
Section titled “What to learn next”You have a running app with one entity and CRUD. The canonical next step is Composing an App — it walks you through how packages, custom routes, contracts, events, and plugins fit together as your app grows.
If you’d rather pick a single feature first, jump in here:
| I want to… | Read |
|---|---|
| Add domain operations (publish, archive, complete) | Data and Entities |
| Send events across packages (welcome email, search sync) | Events and the Event Bus |
| Push live updates to the browser | Realtime |
| Add OAuth, MFA, or passkeys | Auth |
| Control who can do what | Permissions |
| Run background jobs | Jobs and Orchestration |
| Use Postgres, SQLite, or MongoDB | Production Databases |
| Organize code as the app grows | Package-First Authoring |
| See a complete app | Examples |
| Understand the framework model | Authoring Model |