palinxdocs

Scaffolding

Every palinx project starts with px scaffold: one command that writes a working project from a template, installs dependencies, and leaves you ready to run. There are five templates. Start with minimal, the smallest app, to see how palinx works with nothing else in the way. The other four are fuller starting points for common project shapes: a full-stack app, a frontend, an API, and a background-jobs service. They all sit on the same framework, so picking one is a starting point, not a commitment; moving between shapes means adding directories, not migrating.

The scaffold workflow

Install the CLI once, globally. That puts the px command on your PATH, and every command from here on is plain px:

bun add -g @palinx/cli

Run px scaffold bare and it goes interactive: a menu picks the template, asks which styling to use for webapp and frontend (plain CSS is preselected), and prompts for the project name.

px scaffold

Or name the template and project directly:

px scaffold minimal my-app

webapp and frontend take a styling choice. Plain CSS is the default; Sass and Tailwind are opt-in:

px scaffold webapp my-app --styles sass
px scaffold webapp my-app --styles tailwind

All three use the same markup and look the same; an option only changes where the stylesheet lives and what compiles it. Styling covers each one, and how to switch later.

To scaffold into a directory that already has files (a README.md, a .git/ history), pass --into-existing. It writes per-file and skips anything that already exists, so nothing you manage gets clobbered:

px scaffold webapp . --into-existing

Prefer not to install globally? bunx @palinx/cli <command> runs any command without installing.

Every project comes with the same four scripts in its package.json: dev, build, test, and start, which runs the production build with px serve.

The trees below are generated from the templates themselves, so they list every file the scaffold writes. Installing dependencies adds node_modules/ beside them.

minimal: the smallest app

Start here. One page, one API endpoint and a test for it, and nothing else: no config file, no services, no styling. It's for seeing how palinx works before you build anything real, or for trying an idea in a few minutes. The page calls the endpoint and shows what it returns. Learn from scratch builds an app like this one by hand, a concept at a time.

my-app/
├── src/
│   ├── api/
│   │   └── hello.ts                 # the JSON endpoint at /api/hello
│   └── routes/
│       └── index.route.ts           # the page at /, a class-based route
├── tests/
│   └── hello.test.ts                # checks the endpoint's answer; run it with bun run test
├── .gitignore
├── bun.lock
├── package.json
├── README.md                        # what px dev does when it starts, step by step
└── tsconfig.json                    # lets your editor accept the @api and @get decorators

palinx handles decorators itself, so tsconfig.json isn't needed to run the app. It's there so TypeScript and your editor understand them too, instead of underlining them as errors. The README.md walks through what px dev does when it starts, from reading config to answering a request, which makes this template a good companion to the rest of these guides.

px scaffold minimal my-app

When you need more, grow it: add directories as you need them, such as src/services/ for shared classes, src/scheduled/ for jobs (after bun add @palinx/scheduler), or a palinx.config.ts to change the defaults. Or start your next project from one of the bigger templates below, which come with those pieces already in place.

webapp: the full-stack app

The batteries-included starting point: pages, components, API endpoints, services with dependency injection, a stylesheet for every page, and a small task-tracker demo showing the pieces working together. Choose it when you're building an application people open in a browser. It contains every other shape, so it's the template to pick when you know you'll need most of them.

my-app/
├── src/
│   ├── api/                         # /api/tasks and /api/user endpoints
│   │   ├── tasks.ts
│   │   └── user.ts
│   ├── components/                  # navbar, task-card
│   │   ├── icons.ts
│   │   ├── navbar.ts
│   │   └── task-card.ts
│   ├── lib/
│   │   └── csrf-fetch.ts
│   ├── routes/                      # pages; the filesystem is the router
│   │   ├── tasks/                   # the task-tracker demo
│   │   │   ├── _guard.ts            # protects everything under /tasks
│   │   │   └── index.route.ts
│   │   ├── _layout.ts               # wraps every page
│   │   ├── [...catchall].route.ts   # the 404 page
│   │   ├── about.route.ts
│   │   ├── index.route.ts
│   │   ├── login.route.ts
│   │   └── register.route.ts
│   ├── services/                    # auth + db (bun:sqlite), wired with DI
│   │   ├── auth-api.service.ts
│   │   ├── auth.service.ts
│   │   └── db.service.ts
│   └── styles/
│       └── global.css               # the app's styles, linked on every page
├── tests/
│   ├── components.test.ts
│   └── sessions-migration.test.ts
├── .env.example                     # environment variables the app reads
├── .gitignore
├── bun.lock
├── bunfig.toml
├── package.json
├── palinx.config.ts                 # server, router, and security options
├── README.md
└── tsconfig.json

That's the default, plain CSS. With --styles sass or --styles tailwind, src/styles/ holds app.css plus main.scss or input.css, compiled to public/styles.css; see Styling.

Scheduled tasks run the moment you add a src/scheduled/ directory.

px scaffold webapp my-app

frontend: pages and components only

The client-facing slice: routes, components, and reactivity with no server-side code, and a stylesheet for every page. Choose it for sites and UIs that don't need their own backend. It grows the same way every template does; add a src/api/ directory when the project earns one, and the endpoints join the same app.

my-site/
├── src/
│   ├── components/                  # nav, footer
│   │   ├── footer.ts
│   │   ├── icons.ts
│   │   └── nav.ts
│   ├── routes/                      # pages; the filesystem is the router
│   │   ├── _layout.ts               # wraps every page
│   │   ├── about.route.ts
│   │   └── index.route.ts
│   └── styles/
│       └── global.css               # the site's styles, linked on every page
├── tests/                           # component render tests
│   └── components.test.ts
├── .gitignore
├── bun.lock
├── bunfig.toml                      # preloads the test DOM
├── package.json
├── palinx.config.ts
├── README.md
├── test-setup.ts                    # happy-dom registration for tests
└── tsconfig.json

As with webapp, --styles sass or --styles tailwind swaps the stylesheet setup; the pages stay the same.

Component tests work out of the box: the template ships a happy-dom test setup (bunfig.toml preloads it), so px test renders and asserts against components with nothing extra to install.

px scaffold frontend my-site

api: the JSON service

An API-only service: @api endpoint classes, services with dependency injection, and tests, with no pages and no client bundle. Choose it for microservices and backends that speak JSON.

my-service/
├── src/
│   ├── api/                         # /api/widgets CRUD demo + /api/health
│   │   ├── health.ts
│   │   └── widgets.ts
│   ├── routes/
│   │   ├── .gitkeep
│   │   └── index.route.ts           # a status page listing the endpoints
│   └── services/                    # widgets + db (bun:sqlite), wired with DI
│       ├── db.service.ts
│       └── widgets.service.ts
├── tests/                           # endpoint tests
│   └── widgets.test.ts
├── .gitignore
├── bun.lock
├── package.json
├── palinx.config.ts
├── README.md
└── tsconfig.json

The only page is a status page at / that lists the endpoints, so opening the service in a browser shows it's up. Add route files next to it and the service grows a front-end without changing shape.

px scaffold api my-service

taskrunner: background jobs

A scheduled-jobs service: @Cron and @Interval tasks, services, tests, and a minimal health endpoint so you can tell it's alive. Choose it when the work runs on a clock rather than in response to requests. Scheduled tasks covers writing the tasks themselves.

my-jobs/
├── src/
│   ├── api/                         # /api/health
│   │   └── health.ts
│   ├── routes/
│   │   ├── .gitkeep
│   │   └── index.route.ts           # a status page listing the tasks
│   ├── scheduled/                   # daily-cleanup (@Cron) and heartbeat (@Interval)
│   │   ├── daily-cleanup.scheduled.ts
│   │   └── heartbeat.scheduled.ts
│   └── services/                    # heartbeat state (bun:sqlite)
│       └── heartbeat.service.ts
├── tests/
│   └── heartbeat.test.ts
├── .env.example
├── .gitignore
├── bun.lock
├── package.json
├── palinx.config.ts
├── README.md
└── tsconfig.json
px scaffold taskrunner my-jobs

What's in the box

minimal and frontend deliberately leave out @palinx/scheduler: minimal keeps to the core packages, and a pure client project has no use for a job scheduler. bun add @palinx/scheduler brings it in if that changes. webapp, api, and taskrunner include it, so scheduled tasks are one src/scheduled/ directory away with nothing to install.

Next

Scaffolded and running, your project's pages come from the filesystem. Routing & layouts covers how files become URLs.