Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ jobs:
run: |
cd templates/express && pnpm prepare
cd ../h3 && pnpm prepare
cd ../fastify && pnpm prepare

- name: Run linter
run: pnpm lint
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ jobs:
run: |
cd templates/express && pnpm prepare
cd ../h3 && pnpm prepare
cd ../fastify && pnpm prepare

- name: Run linter
run: pnpm lint
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Read `SKILL.md` first, then use these workflows to combine the available skills
- Start from the scaffolded app root.
- Inspect the local `package.json` before running commands.
- Use the package manager indicated by the lockfile.
- Detect whether the app is Express or H3 before editing routes, middleware, or runtime code.
- Detect whether the app is Express, H3 or Fastify before editing routes, middleware, or runtime code.
- Detect whether the app is full or lean before using database, auth, resource, or controller workflows.
- Prefer `ark` generators when the project structure supports them.
- Ask before destructive database, storage, overwrite, delete, publish, or release operations.
Expand All @@ -21,6 +21,7 @@ Read `SKILL.md` first, then use these workflows to combine the available skills
3. Identify the runtime:
- Express: `@arkstack/driver-express`.
- H3: `@arkstack/driver-h3`.
- Fastify: `@arkstack/driver-fastify`.
4. Identify the scope:
- Full: has `src/app`, `src/database`, `src/routes/api.ts`, and `arkormx.config.ts`.
- Lean: lacks those full-template files.
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
[![Console Slim Downloads](https://img.shields.io/npm/dt/@arkstack/console-slim?style=flat-square&label=@arkstack/console-slim&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F@arkstack/console-slim)](https://www.npmjs.com/package/@arkstack/console-slim)
[![Console Slim Downloads](https://img.shields.io/npm/dt/@arkstack/filesystem?style=flat-square&label=@arkstack/filesystem&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F@arkstack/filesystem)](https://www.npmjs.com/package/@arkstack/filesystem)
[![Driver Express Downloads](https://img.shields.io/npm/dt/@arkstack/driver-express?style=flat-square&label=@arkstack/driver-express&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F@arkstack/driver-express)](https://www.npmjs.com/package/@arkstack/driver-express)
[![Driver Fastify Downloads](https://img.shields.io/npm/dt/@arkstack/driver-fastify?style=flat-square&label=@arkstack/driver-fastify&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F@arkstack/driver-fastify)](https://www.npmjs.com/package/@arkstack/driver-fastify)
[![Driver H3 Downloads](https://img.shields.io/npm/dt/@arkstack/driver-h3?style=flat-square&label=@arkstack/driver-h3&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F@arkstack/driver-h3)](https://www.npmjs.com/package/@arkstack/driver-h3)
[![Auth Downloads](https://img.shields.io/npm/dt/@arkstack/auth?style=flat-square&label=@arkstack/auth&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F@arkstack/auth)](https://www.npmjs.com/package/@arkstack/auth)
[![HTTP Downloads](https://img.shields.io/npm/dt/@arkstack/http?style=flat-square&label=@arkstack/http&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F@arkstack/http)](https://www.npmjs.com/package/@arkstack/http)
Expand All @@ -26,7 +27,7 @@

Arkstack is a runtime-agnostic TypeScript backend framework for building structured, production-ready server applications.

It gives you a complete application layer with routing conventions, console commands, authentication, notifications, views, hooks, shared contracts, and runtime drivers for Express, H3, and future transports.
It gives you a complete application layer with routing conventions, console commands, authentication, notifications, views, hooks, shared contracts, and runtime drivers for Express, H3, Fastify, and future transports.

Arkstack is opinionated about application architecture while staying flexible about the HTTP runtime beneath it.

Expand All @@ -38,7 +39,7 @@ It prioritizes the Arkstack application model first, runtime adapters second.

Most backend frameworks are tightly coupled to one HTTP runtime. Arkstack is designed around a stable application model, clean architecture, and transport-layer abstraction.

- Runtime driver support (Express, H3 — more coming)
- Runtime driver support (Express, H3, Fastify — more coming)
- Opinionated framework conventions without runtime lock-in
- Clean and scalable application structure
- TypeScript native
Expand Down Expand Up @@ -127,7 +128,7 @@ Switching frameworks should not require rewriting business logic.
| --------- | ------- |
| Express | Stable |
| H3 | Stable |
| Fastify | Planned |
| Fastify | Beta |
| Bun | Planned |

New adapters can be added without affecting the application layer.
Expand All @@ -141,7 +142,7 @@ New adapters can be added without affecting the application layer.
- `@arkstack/encryption`: isomorphic AES-256-GCM and ECDH primitives shared by server and browser.
- `@arkstack/console`: shared console runtime used by kits.

Each runtime kit (Express, H3, future Fastify/Bun) implements a framework-specific driver that conforms to the same contract.
Each runtime kit (Express, H3, Fastify, future Bun) implements a framework-specific driver that conforms to the same contract.

---

Expand Down Expand Up @@ -216,7 +217,6 @@ npm start

## Roadmap

- Fastify adapter
- Bun adapter
- CLI scaffolding generators
- Plugin system
Expand Down
5 changes: 4 additions & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This file is for AI agents working inside an application created with `pnpm create arkstack`, `npm init arkstack`, `yarn create arkstack`, or `npx create-arkstack`.

Assume the agent is in a scaffolded Arkstack project, not in the Arkstack framework monorepo. The project normally contains one runtime template, either Express or H3, plus any files the user has added.
Assume the agent is in a scaffolded Arkstack project, not in the Arkstack framework monorepo. The project normally contains one runtime template, Express, H3 or Fastify, plus any files the user has added.

## First Checks

Expand All @@ -17,6 +17,7 @@ Before changing code:
- Detect the runtime driver from dependencies and imports:
- `@arkstack/driver-express`: Express runtime.
- `@arkstack/driver-h3`: H3 runtime.
- `@arkstack/driver-fastify`: Fastify runtime.
- Detect project scope:
- Full project: has `src/app`, `src/database`, `src/routes/api.ts`, `arkormx.config.ts`, and database/auth dependencies.
- Lean project: usually lacks `src/app`, `src/database`, `src/routes/api.ts`, `arkormx.config.ts`, and database/auth dependencies.
Expand Down Expand Up @@ -122,6 +123,7 @@ Use the runtime-specific router import already present in the project:

- Express apps import `Router` from `@arkstack/driver-express`.
- H3 apps import `Router` from `@arkstack/driver-h3`.
- Fastify apps import `Router` from `@arkstack/driver-fastify`.

Default route locations:

Expand Down Expand Up @@ -244,6 +246,7 @@ Runtime-specific middleware imports differ:

- Express apps use `@arkstack/driver-express/middlewares`.
- H3 apps use `@arkstack/driver-h3/middlewares`.
- Fastify apps use `@arkstack/driver-fastify/middlewares`; Fastify plugins such as CORS go in `src/config/plugins.ts`.

Add app-specific middleware under `src/app/http/middlewares` in full templates. In lean templates, keep middleware small inside config or create a local directory only if the task justifies it.

Expand Down
7 changes: 6 additions & 1 deletion create-arkstack/src/Commands/CreateArkstackCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class CreateArkstackCommand extends Command {
})),
],
default: 'none',
when: () => !options.stack,
when: () => !options.stack && templates.find((e) => e.alias === (options.kit || template))?.inertia !== false,
},
])
.catch((err) => {
Expand Down Expand Up @@ -186,6 +186,11 @@ export class CreateArkstackCommand extends Command {
process.exit(1)
}

if (kit.inertia === false && stack !== 'none') {
this.error(`ERROR: Inertia is not available for the ${kit.name} kit yet`)
process.exit(1)
}

const kitName = (kit.baseAlias ?? kit.alias).replace(/-lean$/i, '') as KitName
const source: string = pre && kit.prereleaseSource ? kit.prereleaseSource! : kit.source
const actions = new Actions(join(process.cwd(), location), appName, description)
Expand Down
7 changes: 4 additions & 3 deletions create-arkstack/src/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ export const catalog: Record<string, string> = {
"@h3ravel/collect.js": "^5.4.0",
"@faker-js/faker": "^10.4.0",
"@arkormx/plugin-clear-router": "^0.1.57",
"@resora/plugin-clear-router": "^1.0.72",
"@resora/plugin-clear-router": "^1.0.73",
"@kanun-hq/plugin-file": "^0.1.12",
"@kanun-hq/plugin-phone": "^0.1.8",
"arkormx": "^2.12.8",
"clear-router": "^2.9.3",
"clear-router": "^2.9.4",
"dotenv": "^17.4.2",
"express": "^5.2.1",
"fastify": "^5.8.5",
"h3": "^2.0.1-rc.22",
"kysely": "^0.29.2",
"kanun": "^1.2.0",
"parasito": "^0.2.6",
"pg": "^8.22.0",
"resora": "^1.3.35",
"resora": "^1.3.36",
"tsdown": "^0.22.14"
}

Expand Down
1 change: 1 addition & 0 deletions create-arkstack/src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const depsList: Record<string, string> = {
'@arkstack/contract': '^0.21.1',
'@arkstack/database': '^0.21.1',
'@arkstack/driver-express': '^0.21.1',
'@arkstack/driver-fastify': '^0.21.1',
'@arkstack/driver-h3': '^0.21.1',
'@arkstack/filesystem': '^0.21.1',
'@arkstack/http': '^0.21.1',
Expand Down
7 changes: 7 additions & 0 deletions create-arkstack/src/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ export const templates: Template[] = [
hint: 'Arkstack application running on H3',
source: 'github:arkstack-hq/arkstack',
},
{
name: 'Fastify',
alias: 'fastify',
hint: 'Arkstack application running on Fastify',
source: 'github:arkstack-hq/arkstack',
inertia: false,
},
// {
// name: 'H3 Lean Runtime Template',
// alias: 'h3-lean',
Expand Down
6 changes: 4 additions & 2 deletions create-arkstack/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ export type KitName = 'express' | 'h3' | 'fastify' | 'koa' | 'nest' | 'next' | '

export interface Template {
name: string
alias: 'express' | 'h3' | 'express-lean' | 'h3-lean'
alias: 'express' | 'h3' | 'fastify' | 'express-lean' | 'h3-lean'
hint: string
source: string
lean?: boolean
locked?: boolean
baseAlias?: 'express' | 'h3'
baseAlias?: 'express' | 'h3' | 'fastify'
/** Set to false when the runtime has no Inertia adapter yet. */
inertia?: boolean
prereleaseSource?: string
}

Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ export default defineConfig({
items: [
{ text: 'Express', link: '/guide/express-runtime' },
{ text: 'H3', link: '/guide/h3-runtime' },
{ text: 'Fastify', link: '/guide/fastify-runtime' },
],
},
{
Expand Down
133 changes: 133 additions & 0 deletions docs/guide/fastify-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Fastify Runtime Interaction

Arkstack's Fastify runtime integration runs the Arkstack application model on a Fastify instance. Routes, controllers, resources, validation and the console work the same as on Express and H3; this page covers what is specific to Fastify.

## Bootstrap Exports

From `src/core/bootstrap.ts`:

- `fastifyApp`: raw Fastify instance
- `app`: Arkstack `Application` instance

```ts
import { app, fastifyApp } from 'src/core/bootstrap'
```

## Interaction Options

### 1. Raw runtime: `fastifyApp`

Use this for direct Fastify APIs. Anything that changes routes, hooks or plugins must happen before the server starts, because Fastify locks the instance once it is ready.

```ts
import { fastifyApp } from 'src/core/bootstrap'

fastifyApp.decorate('serviceName', 'api')
fastifyApp.addHook('onSend', async (_req, reply) => {
reply.header('x-runtime', 'fastify')
})
```

### 2. Instance runtime accessor: `app.getAppInstance()`

```ts
import { app } from 'src/core/bootstrap'

const runtime = app.getAppInstance()
runtime.addHook('onRequest', async (req) => {
req.log.info({ url: req.url }, 'incoming request')
})
```

### 3. Driver interaction: `app.getDriver()`

```ts
import { app } from 'src/core/bootstrap'

const driver = app.getDriver()
await driver.applyMiddleware(app.getAppInstance(), async (req, reply) => {
reply.header('x-request-id', req.id)
})
```

### 4. Router contract interaction: `app.getRouter()`

```ts
import { app } from 'src/core/bootstrap'

const routes = await app.getRouter().list({ path: '/api' })
console.log(routes)
```

## Middleware

Arkstack middleware runs as Fastify hooks. The positions in `src/config/middleware.ts` map to:

| Position | Fastify hook | Runs |
| -------- | ------------ | ------------------------------- |
| `global` | `onRequest` | before the body is parsed |
| `before` | `preHandler` | after the body is parsed |
| `after` | `onResponse` | once the response has been sent |

A middleware can be an async `(req, reply)` function, a callback `(req, reply, done)` function, or a class with a `handle` method. A callback middleware that calls `done` inside an async context (such as `localeMiddleware()`) keeps that context for the route handler.

```ts
import { formdata, requestLogger, resora } from '@arkstack/driver-fastify/middlewares'

export default (): MiddlewareConfig => ({
global: [],
before: [formdata(), resora()],
after: [requestLogger()],
})
```

## Plugins

Fastify plugins are registered when the app is created, from `src/config/plugins.ts`. CORS uses `@fastify/cors`, and the driver's `limiter()` returns a `@fastify/rate-limit` entry:

```ts
import fastifyCors from '@fastify/cors'
import { limiter } from '@arkstack/driver-fastify/middlewares'

export default (): FastifyPluginEntry[] => [[fastifyCors, { origin: true, credentials: true }], limiter(100, 900)]
```

JSON, urlencoded and multipart bodies are parsed out of the box.

## Uploads

`formdata()` reads multipart fields into `req.body` and files into `req.uploads`, shaped like multer files, so file validation works the same as on Express:

```ts
;async (req) => {
useExpressUploadContext({ files: req.uploads })
}
```

## Unified HTTP Context

Clear Router handlers receive the native Fastify `req` and `reply` objects together with request-scoped Arkstack `clearRequest` and `clearResponse` objects:

```ts
Router.get('/account', ({ req, reply, clearRequest }) => {
reply.header('x-request-id', req.id)

return { token: clearRequest.bearerToken() }
})
```

Resources returned from routes are sent through the reply, including the status and headers set in `withResponse()`.

## Static Assets

The `public` directory is served with `@fastify/static` during `app.boot(port)`, with `Cache-Control: public, max-age=31536000, immutable` and permissive CORS headers. Override `mountPublicAssets` when constructing `FastifyDriver` to change this.

## Host & Port Binding

Host, port, dev HTTPS (`APP_SECURE`) and tunnels (`TUNNEL`, `TUNNEL_URL`) work the same as on the [Express runtime](/guide/express-runtime#host-port-binding).

## Notes

- Inertia is not available on Fastify yet.
- The error handler is set when the app is created, so routes added at any point use it.
- `app.boot(port)` sets the app up once; later calls only start the server.
3 changes: 2 additions & 1 deletion docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Arkstack is a runtime-agnostic TypeScript backend framework for building structured, production-ready server applications.

It gives you a complete application layer with routing conventions, console commands, authentication, notifications, views, hooks, shared contracts, and runtime drivers for Express, H3, and future transports.
It gives you a complete application layer with routing conventions, console commands, authentication, notifications, views, hooks, shared contracts, and runtime drivers for Express, H3, Fastify, and future transports.

Arkstack is opinionated about application architecture while staying flexible about the HTTP runtime beneath it.

Expand Down Expand Up @@ -63,6 +63,7 @@ $ yarn create arkstack@latest my-app
- `express-lean` - lean Express-driver app stripped of database features and app scaffolding
- `h3` - full Arkstack app on the H3 driver with database features
- `h3-lean` - lean H3-driver app stripped of database features and app scaffolding
- `fastify` - full Arkstack app on the Fastify driver with database features (no Inertia support yet)

## Install and Run

Expand Down
12 changes: 6 additions & 6 deletions docs/more/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This roadmap tracks Arkstack progress and upcoming priorities.
- ✅ Driver packages for current runtimes
- `@arkstack/driver-express`
- `@arkstack/driver-h3`
- `@arkstack/driver-fastify`
- ✅ Shared console command surface
- `route:list`
- `make:controller`
Expand All @@ -21,7 +22,7 @@ This roadmap tracks Arkstack progress and upcoming priorities.
- `dev`
- `build`
- ✅ Lean and full starter profiles in create-arkstack
- `express`, `h3`
- `express`, `h3`, `fastify`
- `express-lean`, `h3-lean`
- ✅ Lean profile stripping of app/api/database scaffolding
- Removes `src/app`
Expand All @@ -35,11 +36,10 @@ This roadmap tracks Arkstack progress and upcoming priorities.

## Next Priorities

1. 🔲 Add first additional runtime driver (Fastify candidate)
2. 🔲 Add a plugin/extension model for driver and console integration
3. 🔲 Ship richer end-to-end command and scaffolding tests
4. 🔲 Publish package-level versioning/release workflow guidance
5. 🔲 Add migration and upgrade guides between template versions
1. 🔲 Add a plugin/extension model for driver and console integration
2. 🔲 Ship richer end-to-end command and scaffolding tests
3. 🔲 Publish package-level versioning/release workflow guidance
4. 🔲 Add migration and upgrade guides between template versions

## Future

Expand Down
Loading
Loading