Skip to content

atzufuki/alexi

Repository files navigation

Alexi

A Django-inspired full-stack framework for Deno, written in TypeScript.

Alexi brings Django's developer-friendly patterns to the Deno ecosystem: ORM with multiple backends, REST framework, admin panel, and project scaffolding—all with first-class TypeScript support.

Quick Start

Create a new full-stack project in seconds:

deno run -A jsr:@alexi/create my-project
cd my-project
deno task dev

This scaffolds a full-stack blog application (inspired by the Django tutorial) with:

  • Server — SSR + REST API on http://localhost:8000
  • Service Worker — same templates rendered in the browser via offline-capable SW
  • Admin panel — auto-generated at /admin/
  • Desktop — native window via deno task webui
  • Mobile — iOS/Android via deno task mobile:ios / deno task mobile:android

Features

Feature Description
ORM Django-style models, QuerySets, and Managers with SQLite, IndexedDB, and REST backends
REST Framework Serializers, ViewSets, Routers, and filter backends
Admin Panel Auto-generated admin interface for your models
Project Scaffolding @alexi/create generates full-stack projects instantly
URL Routing Django-style path(), include(), and URL patterns
Authentication JWT-based auth with decorators
Desktop Apps WebUI integration for native-like desktop applications
Mobile Apps Capacitor integration for iOS/Android

Installation

Create a New Project (Recommended)

deno run -A jsr:@alexi/create my-project

Add to Existing Project

deno add jsr:@alexi/db jsr:@alexi/restframework jsr:@alexi/core

Or import directly:

import { CharField, Manager, Model } from "jsr:@alexi/db";
import { ModelSerializer, ModelViewSet } from "jsr:@alexi/restframework";

Example

Define a Model

// models.ts
import { AutoField, BooleanField, CharField, Manager, Model } from "@alexi/db";

export class TodoModel extends Model {
  id = new AutoField({ primaryKey: true });
  title = new CharField({ maxLength: 200 });
  completed = new BooleanField({ default: false });

  static objects = new Manager(TodoModel);

  static meta = {
    dbTable: "todos",
  };
}

Create a Serializer

// serializers.ts
import { ModelSerializer } from "@alexi/restframework";
import { TodoModel } from "./models.ts";

export class TodoSerializer extends ModelSerializer {
  static override Meta = {
    model: TodoModel,
    fields: ["id", "title", "completed"],
    readOnlyFields: ["id"],
  };
}

Create a ViewSet

// viewsets.ts
import { ModelViewSet, QueryParamFilterBackend } from "@alexi/restframework";
import { TodoModel } from "./models.ts";
import { TodoSerializer } from "./serializers.ts";

export class TodoViewSet extends ModelViewSet {
  model = TodoModel;
  serializer_class = TodoSerializer;

  // Enable query parameter filtering
  filterBackends = [new QueryParamFilterBackend()];
  filtersetFields = ["id", "completed"];
}

Register Routes

// urls.ts
import { DefaultRouter } from "@alexi/restframework";
import { TodoViewSet } from "./viewsets.ts";

const router = new DefaultRouter();
router.register("todos", TodoViewSet);

export const urlpatterns = router.urls;

Configure Settings

// settings.ts
import { DenoKVBackend } from "@alexi/db/backends/denokv";
import { DbConfig } from "@alexi/db";
import { TodoAppConfig } from "./web.ts";

export const INSTALLED_APPS = [DbConfig, TodoAppConfig];
export const ROOT_URLCONF = () => import("./urls.ts");

export const DATABASES = {
  default: new DenoKVBackend({ name: "todos", path: "./data/todos.db" }),
};

Start the Server

// http.ts
import { getHttpApplication } from "@alexi/core";

export default await getHttpApplication();
deno serve -A --unstable-kv http.ts

Now you have a full REST API:

GET    /api/todos/                    # List all todos
POST   /api/todos/                    # Create a todo
GET    /api/todos/:id/                # Get a todo
PUT    /api/todos/:id/                # Update a todo
DELETE /api/todos/:id/                # Delete a todo
GET    /api/todos/?completed=true     # Filter by completed

Modules

Module Description
@alexi/create Project scaffolding CLI
@alexi/db ORM with DenoKV, SQLite, IndexedDB, and REST backends
@alexi/restframework REST API framework (Serializers, ViewSets, Routers, Filters)
@alexi/core HTTP application, management commands, and utilities
@alexi/admin Auto-generated admin panel
@alexi/auth JWT authentication
@alexi/middleware CORS, logging, and error handling middleware
@alexi/urls URL routing utilities
@alexi/views Template engine and class-based views
@alexi/storage File storage backends
@alexi/staticfiles Static file handling and bundling
@alexi/webui Desktop app support via WebUI
@alexi/capacitor Mobile app support via Capacitor
@alexi/types Shared TypeScript type definitions

ORM

Querying

// Get all todos
const todos = await TodoModel.objects.all().fetch();

// Filter
const completed = await TodoModel.objects
  .filter({ completed: true })
  .fetch();

// Chained filters with lookups
const recent = await TodoModel.objects
  .filter({ title__contains: "important" })
  .orderBy("-createdAt")
  .limit(10)
  .fetch();

// Get single object
const todo = await TodoModel.objects.get({ id: 1 });

// Create
const newTodo = await TodoModel.objects.create({
  title: "Learn Alexi",
  completed: false,
});

// Update
todo.completed.set(true);
await todo.save();

// Delete
await todo.delete();

Multiple Backends

// settings.ts
import { DenoKVBackend } from "@alexi/db/backends/denokv";
import { RestBackend } from "@alexi/db/backends/rest";

export const DATABASES = {
  default: new DenoKVBackend({ name: "myapp", path: "./data/myapp.db" }),
  api: new RestBackend({ apiUrl: "https://api.example.com/api" }),
};

// Use specific backend
const remote = await TodoModel.objects.using("api").all().fetch();

REST Framework

Filter Backends

import {
  ModelViewSet,
  OrderingFilter,
  QueryParamFilterBackend,
  SearchFilter,
} from "@alexi/restframework";

class ArticleViewSet extends ModelViewSet {
  model = ArticleModel;
  serializer_class = ArticleSerializer;

  filterBackends = [
    new QueryParamFilterBackend(),
    new OrderingFilter(),
    new SearchFilter(),
  ];

  filtersetFields = ["id", "status", "author"];
  orderingFields = ["createdAt", "title"];
  searchFields = ["title", "body"];
  ordering = ["-createdAt"]; // default ordering
}

// Supports:
// GET /api/articles/?status=published
// GET /api/articles/?ordering=-createdAt
// GET /api/articles/?search=typescript
// GET /api/articles/?title__contains=guide

Custom Actions

import { action, ModelViewSet } from "@alexi/restframework";
import type { ViewSetContext } from "@alexi/restframework";

class TodoViewSet extends ModelViewSet {
  model = TodoModel;
  serializer_class = TodoSerializer;

  @action({ detail: true, methods: ["POST"] })
  async toggle(context: ViewSetContext): Promise<Response> {
    const todo = await this.getObject(context);
    todo.completed.set(!todo.completed.get());
    await todo.save();
    return Response.json(await new TodoSerializer({ instance: todo }).data);
  }
}

// POST /api/todos/:id/toggle/

Project Structure

When you run deno run -A jsr:@alexi/create my-project, you get:

my-project/
├── manage.ts                    # Management CLI entry point
├── deno.jsonc                   # Workspace configuration
├── project/
│   ├── settings.ts              # Shared settings
│   ├── web.settings.ts          # Web server settings
│   ├── ui.settings.ts           # UI settings
│   └── desktop.settings.ts      # Desktop settings
└── src/
    ├── my-project-web/          # Backend REST API
    │   ├── app.ts
    │   ├── models.ts
    │   ├── serializers.ts
    │   ├── viewsets.ts
    │   └── urls.ts
    ├── my-project-ui/           # Frontend SPA
    │   ├── main.ts
    │   ├── models.ts
    │   ├── views.ts
    │   ├── templates/
    │   └── components/
    └── my-project-desktop/      # Desktop app
        └── app.ts

Management Commands

deno run -A --unstable-kv manage.ts runserver --settings ./project/settings.ts
deno run -A --unstable-kv manage.ts makemigrations myapp --settings ./project/settings.ts
deno run -A --unstable-kv manage.ts migrate --settings ./project/settings.ts
deno run -A --unstable-kv manage.ts createsuperuser --settings ./project/settings.ts

Requirements

  • Deno 2.0+
  • --unstable-kv flag for DenoKV backend

Documentation

See the docs/ directory for detailed guides:

Getting Started

Core Concepts

Database

REST Framework

Additional Modules

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting PRs.

License

MIT License - see LICENSE for details.

Links

About

Django-inspired full-stack framework for TypeScript

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages