Comprehensive guide for developing in a Svelte SvelteKit project with vertical architecture, OpenAPI integration, and mandatory browser testing workflow
A comprehensive skill for developing in a SvelteKit project that follows vertical architecture with minimal abstractions, OpenAPI-generated types, and mandatory browser testing.
**šØ NEVER IMPLEMENT CODE WITHOUT IMMEDIATELY TESTING IN BROWSER šØ**
Follow this workflow for ALL development tasks:
This application follows a **vertical architecture** with **minimal abstractions**:
```
src/lib/app/{feature}/
āāā {feature}-schema.ts # Zod v4 validation schemas + OpenAPI types
āāā {feature}-constants.ts # Feature constants (statuses, enums, etc.)
āāā config.ts # Feature configuration
āāā index.ts # Public exports (barreled)
āāā components/ # Route-specific UI components
ā āāā {item}-form.svelte
ā āāā {item}-card.svelte
ā āāā {item}-table.svelte
āāā actions/ # Business logic (commands & queries)
āāā get-{items}.ts # List query
āāā get-{item}.ts # Single item query
āāā create-{item}.ts # Create command
āāā update-{item}.ts # Update command
āāā delete-{item}.ts # Delete command
src/routes/{feature}/
āāā +page.svelte # List page
āāā +page.server.ts # List loader
āāā create/
ā āāā +page.svelte
ā āāā +page.server.ts
āāā [id]/
āāā +page.svelte
āāā +page.server.ts
āāā +server.ts # API endpoint (e.g. DELETE)
āāā edit/
āāā +page.svelte
āāā +page.server.ts
```
Use OpenAPI-generated types directly in schema files:
```typescript
// {feature}-schema.ts
import type { components } from '$lib/api/billing/v1';
// Use OpenAPI generated types directly
export type Item = components['schemas']['Item'];
export type GetItemsResult = Item;
export type CreateUpdateItemRequest = components['schemas']['CreateItemCommand'];
// Custom types only when needed for client-side logic
export interface ItemSummary {
totalItems: number;
totalValue: number;
}
// Form validation schemas
export const createUpdateItemSchema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters'),
amount: z.number().positive('Amount must be greater than 0')
});
export type CreateUpdateItemSchema = typeof createUpdateItemSchema;
```
```typescript
// actions/get-{item}.ts
import { ok, error, type ActionResult } from '$lib/utils/action-result';
import client from '$lib/api/billing';
import type { Item } from '../{item}-schema';
export const getItemQuery = async (id: string): Promise<ActionResult<Item>> => {
if (!id?.trim()) {
return error('Item ID is required', 400);
}
const { data, response } = await client.GET('/Items/{id}', {
params: { path: { id } }
});
if (data) {
return ok(data);
}
if (response?.status === 404) {
return error('Item not found', 404);
}
return error('Failed to load item. Please try again later.', response?.status || 500);
};
```
```typescript
// actions/get-{items}.ts
import { ok, error, type ActionResult } from '$lib/utils/action-result';
import client from '$lib/api/billing';
import type { GetItemsResult } from '../{item}-schema';
export interface GetItemsQuery {
page?: number;
pageSize?: number;
search?: string;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
status?: string;
}
export const getItemsQuery = async (query?: GetItemsQuery): Promise<ActionResult<GetItemsResult[]>> => {
const { data, response } = await client.GET('/Items', {
params: { query: query as any }
});
if (data) {
const items = (data || []) as GetItemsResult[];
items.sort((a, b) => a.name.localeCompare(b.name));
return ok(items);
}
return error('Failed to load items. Please try again later.', response?.status || 500);
};
```
```typescript
// actions/create-{item}.ts
import { ok, error, type ActionResult } from '$lib/utils/action-result';
import client from '$lib/api/billing';
import type { Item } from '../{item}-schema';
export interface CreateItemParams {
name: string;
amount: number;
currency?: string;
dueDate?: string;
cashierId?: string;
}
export const createItemCommand = async (params: CreateItemParams): Promise<ActionResult<Item>> => {
const { data, response } = await client.POST('/Items', {
body: params as any
});
if (data) {
return ok(data);
}
if (response?.status === 400) {
return error('Invalid request data', 400);
}
if (response?.status === 409) {
return error('Item already exists', 409);
}
return error('Failed to create item. Please try again later.', response?.status || 500);
};
```
```typescript
// actions/update-{item}.ts
export interface UpdateItemParams {
id: string;
name: string;
amount: number;
currency?: string;
dueDate?: string;
cashierId?: string;
}
export const updateItemCommand = async (params: UpdateItemParams): Promise<ActionResult<Item>> => {
const { id, ...updateData } = params;
if (!id?.trim()) {
return error('Item ID is required', 400);
}
const { data, response } = await client.PUT('/Items/{id}', {
params: { path: { id } },
body: updateData as any
});
if (data) {
return ok(data);
}
if (response?.status === 404) {
return error('Item not found', 404);
}
if (response?.status === 400) {
return error('Invalid request data', 400);
}
return error('Failed to update item. Please try again later.', response?.status || 500);
};
```
```typescript
// actions/delete-{item}.ts
export const deleteItemCommand = async (id: string): Promise<ActionResult<void>> => {
if (!id?.trim()) {
return error('Item ID is required', 400);
}
const { error: apiError, response } = await client.DELETE('/Items/{id}', {
params: { path: { id } }
});
if (!apiError) {
return ok();
}
if (response?.status === 404) {
return error('Item not found', 404);
}
return error('Failed to delete item. Please try again later.', response?.status || 500);
};
```
```typescript
import { error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ params }) => {
const result = await getItemQuery(params.id);
if (!result.error) {
return {
item: result.data
};
}
return error(result.error.code, result.error.message);
};
```
Follow this mechanical pattern:
1. **Create feature directory**: `src/lib/app/{feature}/`
2. **Define types and schemas**: `{feature}-schema.ts` (import from OpenAPI)
3. **Create actions**: `actions/` directory with queries and commands
4. **Add configuration**: `config.ts` for feature settings
5. **Export public API**: `index.ts` with barrel exports
6. **Create routes**: `src/routes/{feature}/` with pages and loaders
7. **Build UI components**: `components/` within the feature directory
8. **šØ TEST IMMEDIATELY**: Open browser, navigate to feature, verify all functionality
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/svelte-sandbox-development-guide/raw