createServer and createApp
Every Slingshot app starts with one of two functions: createServer() for production, or
createApp() for tests and custom hosting.
createServer — the production entrypoint
Section titled “createServer — the production entrypoint”// @skip-typecheckimport { createServer } from '@lastshotlabs/slingshot';import { createAuthPlugin } from '@lastshotlabs/slingshot-auth';import { blogPackage } from './packages/blog';
await createServer({ port: 3000, security: { signing: { secret: process.env.JWT_SECRET! }, cors: { origin: 'https://myapp.com' }, }, db: { default: { adapter: 'postgres', url: process.env.DATABASE_URL! }, }, plugins: [createAuthPlugin({ auth: { roles: ['user', 'admin'], defaultRole: 'user' } })], packages: [blogPackage],});createServer() does everything:
- Validates your config
- Resolves secrets
- Connects databases
- Boots plugins and packages in dependency order
- Registers middleware, routes, and OpenAPI docs
- Starts the HTTP server
- Starts WebSocket and SSE transports
- Wires graceful shutdown
Run it:
bun run src/index.tsYour API is live at http://localhost:3000, docs at http://localhost:3000/docs.
createApp — for tests
Section titled “createApp — for tests”createApp() does everything except start the network server. Use it in tests:
// @skip-typecheckimport { beforeEach, describe, expect, test } from 'bun:test';import { createApp, getContext } from '@lastshotlabs/slingshot';import { blogPackage } from '../src/packages/blog';
describe('blog', () => { let app: Awaited<ReturnType<typeof createApp>>['app']; let ctx: ReturnType<typeof getContext>;
beforeEach(async () => { const result = await createApp({ packages: [blogPackage], }); app = result.app; ctx = getContext(result.app); });
test('GET /posts returns empty list', async () => { const res = await app.request('/posts'); expect(res.status).toBe(200); const body = (await res.json()) as { items: unknown[] }; expect(body.items).toHaveLength(0); });
test('POST /posts creates a post', async () => { const res = await app.request('/posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: 'Hello', body: 'World' }), }); expect(res.status).toBe(201); });});No port binding, no network overhead. app.request() calls your routes in-process.
What each function returns
Section titled “What each function returns”createServer
Section titled “createServer”Returns a promise that resolves when the server is running. The server process stays alive
until shutdown (SIGTERM, SIGINT, or ctx.destroy()).
createApp
Section titled “createApp”Returns { app, ctx }:
| Property | What it is |
|---|---|
app | The assembled Hono app — call app.request() for tests |
ctx | The SlingshotContext — events, persistence, cleanup |
Use ctx for cleanup:
// Reset in-memory state between testsawait ctx.clear();
// Tear down connections when doneawait ctx.destroy();When to use which
Section titled “When to use which”| Scenario | Use |
|---|---|
| Running your app in production | createServer() |
| Running your app in dev | createServer() |
| Unit and integration tests | createApp() |
| Custom server (e.g., Lambda) | createApp() |
| Embedding in another framework | createApp() |
Minimal examples
Section titled “Minimal examples”Dev server with in-memory storage
Section titled “Dev server with in-memory storage”await createServer({ port: 3000, packages: [appPackage],});No db config means in-memory storage. Data resets on restart.
Production server with Postgres
Section titled “Production server with Postgres”// @skip-typecheckawait createServer({ port: Number(process.env.PORT ?? 3000), security: { signing: { secret: process.env.JWT_SECRET! }, cors: { origin: process.env.CORS_ORIGIN! }, csrf: { enabled: true }, }, db: { default: { adapter: 'postgres', url: process.env.DATABASE_URL! }, }, logging: { enabled: true }, metrics: { enabled: true }, plugins: [createAuthPlugin({ auth: { roles: ['user', 'admin'], defaultRole: 'user' } })], packages: [blogPackage, adminPackage],});- App Config — all the config options
- Context and Request Model — app-scoped vs request-scoped state
- Testing — full testing guide with auth, entities, and SQLite