# Lockness Getting Started Build your first web application with Lockness. ## Quick Start ```bash # Initialize project deno run -A jsr:@lockness/cli init cd my-app # Start development deno task dev ``` Server runs at `http://localhost:8888` ## Create Your First Controller ```bash deno task cli make:controller Welcome --view ``` Creates: - `src/controller/welcome_controller.tsx` - Controller - `src/view/pages/welcome/index.tsx` - View **Controller (`welcome_controller.tsx`):** ```typescript import { Controller, Get, Context } from '@lockness/core' import { WelcomeIndexPage } from '@view/pages/welcome/index.tsx' @Controller('/welcome') export class WelcomeController { @Get('/') index(c: Context) { return c.html() } } ``` **View (`view/pages/welcome/index.tsx`):** ```typescript export const WelcomeIndexPage = () => { return ( Welcome to Lockness

Hello from Lockness!

) } ``` Visit `http://localhost:8888/welcome` ## Add Database Support ### 1. Create Model ```bash deno task cli make:model Post ``` **Model (`src/model/post.ts`):** ```typescript import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core' import { createInsertSchema, createSelectSchema } from 'drizzle-zod' export const post = pgTable('posts', { id: serial('id').primaryKey(), title: text('title').notNull(), content: text('content').notNull(), createdAt: timestamp('created_at').defaultNow(), }) export const postInsertSchema = createInsertSchema(post) export const postSelectSchema = createSelectSchema(post) ``` ### 2. Generate Migration ```bash deno task cli db:generate deno task cli db:migrate ``` ### 3. Create Repository ```bash deno task cli make:repository Post ``` **Repository (`src/repository/post_repository.ts`):** ```typescript import { Service, Inject } from '@lockness/core' import { Database } from '@lockness/drizzle' import { post } from '@model/post.ts' import { eq } from 'drizzle-orm' @Service() export class PostRepository { @Inject(Database) accessor db!: Database async findAll() { return await this.db.select().from(post) } async findById(id: number) { const [result] = await this.db.select().from(post).where(eq(post.id, id)) return result } async create(data: typeof post.$inferInsert) { const [result] = await this.db.insert(post).values(data).returning() return result } async update(id: number, data: Partial) { const [result] = await this.db .update(post) .set(data) .where(eq(post.id, id)) .returning() return result } async delete(id: number) { await this.db.delete(post).where(eq(post.id, id)) } } ``` ### 4. Create CRUD Controller ```bash deno task cli make:controller Post --view deno task cli make:action Post show --view deno task cli make:action Post store ``` **Controller (`src/controller/post_controller.tsx`):** ```typescript import { Controller, Get, Post, Validate, Context, Inject } from '@lockness/core' import { PostRepository } from '@repository/post_repository.ts' import { postInsertSchema } from '@model/post.ts' import { PostIndexPage } from '@view/pages/post/index.tsx' @Controller('/posts') export class PostController { @Inject(PostRepository) accessor postRepository!: PostRepository @Get('/') async index(c: Context) { const posts = await this.postRepository.findAll() return c.html() } @Get('/:id') async show(c: Context) { const id = Number(c.req.param('id')) const post = await this.postRepository.findById(id) if (!post) { return c.notFound() } return c.json({ post }) } @Post('/') @Validate(postInsertSchema) async store(c: Context) { const data = c.req.valid('json') const post = await this.postRepository.create(data) return c.json({ post }, 201) } } ``` ## Add Authentication ```bash deno task cli make:auth ``` Creates: - `AuthController` with login/register/logout - `UserProvider` for authentication - User model and repository - Auth views **Protected Route:** ```typescript import { Controller, Get, Auth, Context, auth } from '@lockness/core' @Controller('/dashboard') @Auth() export class DashboardController { @Get('/') index(c: Context) { const user = auth(c).user() return c.html() } } ``` ## Development Workflow ### Three Terminals ```bash # Terminal 1: CSS compilation npm run dev:css # Terminal 2: Route watching deno task dev:routes # Terminal 3: Dev server with hot reload deno task dev ``` ### Database Workflow ```bash # 1. Modify model # 2. Generate migration deno task cli db:generate # 3. Review migration in database/migrations/ # 4. Apply migration deno task cli db:migrate # 5. Seed database deno task cli db:seed ``` ## Project Structure ``` my-app/ ├── src/ │ ├── kernel.tsx # App config │ ├── routes.ts # Route registry │ ├── controller/ │ │ ├── post_controller.tsx │ │ └── auth_controller.tsx │ ├── middleware/ │ │ └── auth_middleware.ts │ ├── model/ │ │ ├── post.ts │ │ └── user.ts │ ├── repository/ │ │ ├── post_repository.ts │ │ └── user_repository.ts │ ├── service/ │ │ └── email_service.ts │ └── view/ │ ├── layouts/ │ ├── pages/ │ └── components/ ├── database/ │ ├── migrations/ │ └── seeders/ └── public/ ├── css/ └── images/ ``` ## Common Patterns ### RESTful API ```typescript @Controller('/api/posts') export class PostApiController { @Get('/') // GET /api/posts async index() {} @Get('/:id') // GET /api/posts/:id async show() {} @Post('/') // POST /api/posts async store() {} @Put('/:id') // PUT /api/posts/:id async update() {} @Delete('/:id') // DELETE /api/posts/:id async destroy() {} } ``` ### Form Handling with Validation ```typescript import { session } from '@lockness/core' @Post('/') @Validate(postInsertSchema) async store(c: Context) { const data = c.req.valid('json') try { const post = await this.postRepository.create(data) session(c).flash('success', 'Post created!') return c.redirect('/posts') } catch (error) { session(c).flash('error', error.message) return c.redirect('/posts/create') } } ``` ### Dependency Injection ```typescript import { Service, Inject } from '@lockness/core' @Service() export class PostService { @Inject(PostRepository) accessor postRepository!: PostRepository @Inject(EmailService) accessor emailService!: EmailService async publishPost(id: number) { const post = await this.postRepository.findById(id) await this.emailService.send({ to: 'admin@example.com', subject: `New post: ${post.title}`, }) return post } } ``` ## Next Steps - Read [Routing & Controllers](/docs/routing) - Learn about [Models & Database](/docs/models) - Explore [Authentication](/docs/authentication) - Check [CLI commands](/docs/cli) - Browse [Packages](/docs/packages) ## Tips - Use `deno task cli list` to see all available commands - Controllers auto-register in development mode - Use `--view` flag to generate views with controllers - Database migrations are reversible - Sessions work out of the box with cookies - JSX templates render server-side (no client JS)