A high-performance TypeScript web framework combining HyperExpress, Svelte 5, and Inertia.js for building modern full-stack applications. Features server-side rendering, real-time capabilities, and seamless client-server state management.
Visit laju.dev
npx create-laju-app project-name
cd project-name
npm run dev
Clone the repository
Install dependencies:
npm install
Copy .env.example
to .env
and configure your environment variables:
cp .env.example .env
Set up Google OAuth credentials:
http://localhost:5555
(for development)http://localhost:5555/google/callback
.env
file:GOOGLE_CLIENT_ID=your_client_id_here
GOOGLE_CLIENT_SECRET=your_client_secret_here
Run database migrations:
npx knex migrate:latest
To start the development server:
npm run dev
This will:
To build the application for production:
npm run build
This command will:
/app
- Core application files/middlewares
- Custom middleware functions/services
- Service layer implementations/controllers
- Application controllers/resources
- Frontend resources/views
- Svelte components and views/js
- JavaScript assets and modules/routes
- Route definitions/commands
- Custom CLI commands/migrations
- Database migrations/public
- Static files/dist
- Compiled assets (generated)/build
- Production build outputnpm run dev
- Start development servernpm run build
- Build for productionnode laju make:controller ControllerName
This will create a new controller in app/controllers
with basic CRUD methods.
Example:
node laju make:controller UserController
node laju make:command CommandName
This will create a new command in commands
that can be scheduled with cron jobs.
Example:
node laju make:command SendDailyEmails
To schedule the command with cron, add it to your crontab:
# Run command every day at midnight
0 0 * * * cd /path/to/your/app/build && node commands/SendDailyEmails.js
ISC License
This tutorial will guide you through building a simple application using this framework.
First, let's create a new route and controller for a blog post feature.
app/controllers/PostController.ts
:import { Request, Response } from "../../type";
import DB from "../services/DB";
class Controller {
public async index(request: Request, response: Response) {
const posts = await DB.from("posts");
return response.inertia("posts/index", { posts });
}
public async create(request: Request, response: Response) {
return response.inertia("posts/create");
}
public async store(request: Request, response: Response) {
const { title, content } = request.body;
await DB.table("posts").insert({
title,
content,
created_at: Date.now(),
updated_at: Date.now()
});
return response.redirect("/posts");
}
}
export default new Controller();
routes/web.ts
:import PostController from "../app/controllers/PostController";
// Add these routes with your existing routes
Route.get("/posts", PostController.index);
Route.get("/posts/create", PostController.create);
Route.post("/posts", PostController.store);
Create a migration for the posts table:
npx knex migrate:make create_posts_table
In the generated migration file:
import { Knex } from "knex";
export async function up(knex: Knex): Promise<void> {
await knex.schema.createTable('posts', function (table) {
table.increments('id').primary();
table.string('title').notNullable();
table.text('content').notNullable();
table.bigInteger('created_at');
table.bigInteger('updated_at');
});
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTable('posts');
}
Run the migration:
npx knex migrate:latest
resources/views/posts/index.svelte
:<script>
export let posts = [];
</script>
<div class="max-w-4xl mx-auto p-4">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">Blog Posts</h1>
<a
href="/posts/create"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
>
Create Post
</a>
</div>
<div class="space-y-4">
{#each posts as post}
<div class="border p-4 rounded">
<h2 class="text-xl font-semibold">{post.title}</h2>
<p class="mt-2 text-gray-600">{post.content}</p>
</div>
{/each}
</div>
</div>
resources/views/posts/create.svelte
:<script>
import { router } from '@inertiajs/svelte';
let form = {
title: '',
content: ''
};
function handleSubmit() {
router.post('/posts', form);
}
</script>
<div class="max-w-4xl mx-auto p-4">
<h1 class="text-2xl font-bold mb-6">Create New Post</h1>
<form on:submit|preventDefault={handleSubmit} class="space-y-4">
<div>
<label class="block text-sm font-medium mb-1">Title</label>
<input
type="text"
bind:value={form.title}
class="w-full px-3 py-2 border rounded"
/>
</div>
<div>
<label class="block text-sm font-medium mb-1">Content</label>
<textarea
bind:value={form.content}
class="w-full px-3 py-2 border rounded h-32"
></textarea>
</div>
<div>
<button
type="submit"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
>
Create Post
</button>
</div>
</form>
</div>
Start the development server:
npm run dev
Visit http://localhost:5555/posts
in your browser
Try creating a new post using the form
View the list of posts on the index page
routes/web.ts
using the HyperExpress routerFile Organization
app/controllers
resources/js/Pages
resources/js/Components
migrations
Code Structure
Database
Need help with anything specific? Feel free to ask!