Getting Started
This guide will walk you through creating your first Lockness application, from setup to deployment.
📁 Project Structure
Your project will have the following structure, after the initialization:
my-app/
├── app/
│ ├── kernel.tsx # Application bootstrap
│ ├── routes.ts # Auto-generated routes registry
│ ├── controller/ # HTTP controllers
│ │ └── app_controller.tsx
│ ├── middleware/ # Custom middleware
│ ├── model/ # Drizzle database schemas
│ ├── repository/ # Data access layer
│ ├── service/ # Business logic services
│ └── view/
│ ├── assets/ # CSS and client-side assets
│ │ └── app.css
│ ├── components/ # Reusable JSX components
│ ├── layouts/ # Page layouts
│ └── pages/ # Page components
├── database/
│ ├── migrations/ # SQL migration files
│ └── seeders/ # Database seeders
├── public/ # Static files (served directly)
│ ├── css/ # Compiled CSS output
│ ├── img/ # Images
│ └── js/ # Client-side JavaScript
├── tests/ # Test files
├── deno.json # Deno configuration & tasks
├── drizzle.config.ts # Drizzle ORM configuration
└── main.ts # Application entry point
Key Directories
| Directory | Purpose |
|---|---|
app/controller/ | HTTP request handlers with route decorators |
app/model/ | Database table schemas (Drizzle ORM) |
app/repository/ | Data access layer for database operations |
app/service/ | Business logic, injectable via @Service() |
app/view/ | JSX templates, components, and layouts |
database/ | Migrations and seeders |
public/ | Static assets served at root URL |
🎯 Your First Controller
Let's create a simple API endpoint. Use the CLI to scaffold a controller:
# API endpoint (returns JSON)
deno task cli make:controller Hello
# Web page (renders JSX view)
deno task cli make:controller Hello --view
The --view flag automatically creates a view in app/view/pages/hello.tsx and
generates a controller that renders it.
This creates app/controller/hello_controller.ts. Edit it:
import { Context, Controller, Get } from '@lockness/core'
@Controller('/api/hello')
export class HelloController {
@Get('/')
index(c: Context) {
return c.json({
message: 'Hello from Lockness!',
timestamp: new Date().toISOString(),
})
}
@Get('/:name')
greet(c: Context) {
const name = c.req.param('name')
return c.json({ message: `Hello, ${name}!` })
}
}
Test it: http://localhost:5173/api/hello
🗄️ Adding a Database Model
Create a model with repository, controller, and seeder in one command:
deno task cli make:model Post -a
The -a flag generates:
app/model/post.ts- Drizzle schema + Zod validationapp/repository/post_repository.ts- Data access layerapp/controller/post_controller.ts- REST API with validationapp/seeder/post_seeder.ts- Database seeder
📊 Run Migrations
Generate and apply database migrations:
deno task cli db:generate
deno task cli db:migrate
🌱 Seed the Database
Populate your database with test data:
deno task cli db:seed
🎨 Create a View
Generate a JSX page component:
deno task cli make:view posts/index
This creates app/view/pages/posts/index.tsx. Use it in your controller:
import { IndexPage } from '@view/pages/posts/index.tsx'
import { Context, Controller, Get } from '@lockness/core'
@Controller('/posts')
export class PostController {
@Get('/')
index(c: Context) {
return c.html(<IndexPage />)
}
}
🔒 Add Authentication
Scaffold a complete auth system:
deno task cli make:auth
Configure in app/kernel.tsx:
import { DeclareGlobalMiddleware, Kernel } from '@lockness/core'
import { sessionMiddleware } from '@lockness/session'
import { initializeAuthMiddleware, SessionGuard } from '@lockness/auth'
import { UserProvider } from '@auth/user_provider.ts'
@Kernel({
session: { driver: 'cookie', secret: Deno.env.get('APP_KEY') || 'secret' },
controllersDir: './app/controller',
})
export class AppKernel {
@DeclareGlobalMiddleware()
globalMiddlewares = [
sessionMiddleware(),
initializeAuthMiddleware({
default: 'web',
guards: {
web: (ctx) => new SessionGuard('web', ctx, new UserProvider()),
},
}),
]
}
Protect routes with @Auth() decorator:
@Auth()
@Controller('/dashboard')
export class DashboardController {
@Get('/')
async index(c: Context) {
const user = await auth(c).user()
return c.json({ user })
}
}