@constela/start

Full-stack meta-framework for Constela applications with file-based routing, SSR, and edge runtime support

Overview

@constela/start provides everything you need to build production-ready Constela applications:

  • File-based routing: Routes are automatically generated from the src/routes/ directory structure
  • SSR: Server-side rendering using @constela/server's renderToString
  • SSG: Static site generation with getStaticPaths support
  • API Routes: Full support for GET/POST/PUT/DELETE/PATCH endpoints
  • Middleware: Koa/Hono-style next() pattern for request handling
  • Edge Runtime: Deploy to Cloudflare Workers, Vercel Edge, or Deno
  • Vite-based: Development server with HMR support

Constela Ecosystem

SolidJSConstelaPurpose
solid-js@constela/coreCore type definitions
solid-js/web@constela/runtimeClient-side renderer
solid-js/server@constela/serverSSR renderer
SolidStart@constela/startMeta-framework

Installation

bash
npm install @constela/start

Or create a new project:

bash
npx create-constela my-app
cd my-app
npm install
npm run dev

Project Structure

A typical @constela/start project has the following structure:

text
my-app/
├── src/
│   ├── routes/
│   │   ├── index.json       # /
│   │   ├── about.json       # /about
│   │   ├── users/
│   │   │   ├── index.json   # /users
│   │   │   └── [id].json    # /users/:id
│   │   └── api/
│   │       └── users.ts     # /api/users (API route)
│   └── layouts/
│       └── main.json        # Layout definition
├── constela.config.ts
└── package.json

File-based Routing

Routes are determined by the file structure inside src/routes/:

File PathRoute
src/routes/index.json/
src/routes/about.json/about
src/routes/users/index.json/users
src/routes/users/[id].json/users/:id
src/routes/posts/[...slug].json/posts/* (catch-all)

Page Definition

Each page is a JSON file with the Constela DSL:

json
{
  "version": "1.0",
  "route": {
    "path": "/",
    "layout": "main",
    "meta": {
      "title": "Home",
      "description": "Welcome to my app"
    }
  },
  "state": {
    "count": { "type": "number", "initial": 0 }
  },
  "actions": [
    {
      "name": "increment",
      "steps": [
        { "do": "update", "target": "count", "operation": "increment" }
      ]
    }
  ],
  "view": {
    "kind": "element",
    "tag": "div",
    "children": [
      { "kind": "text", "value": { "expr": "state", "name": "count" } },
      {
        "kind": "element",
        "tag": "button",
        "props": { "onClick": { "event": "click", "action": "increment" } },
        "children": [
          { "kind": "text", "value": { "expr": "lit", "value": "+1" } }
        ]
      }
    ]
  }
}

Dynamic Routes

Use square brackets for dynamic segments:

json
{
  "version": "1.0",
  "route": {
    "path": "/users/:id"
  },
  "view": {
    "kind": "element",
    "tag": "div",
    "children": [
      { "kind": "text", "value": { "expr": "lit", "value": "User ID: " } },
      { "kind": "text", "value": { "expr": "route", "name": "id" } }
    ]
  }
}

Catch-all Routes

Use [...slug] for catch-all routes:

text
src/routes/docs/[...slug].json
# Matches /docs/a, /docs/a/b, /docs/a/b/c, etc.

SSR & Hydration

@constela/start provides server-side rendering out of the box. Pages defined in JSON are automatically SSR'd on the server and hydrated on the client for optimal performance and SEO.

SSG (Static Site Generation)

For static pages with dynamic routes, use the getStaticPaths field:

json
{
  "version": "1.0",
  "route": {
    "path": "/posts/:slug",
    "getStaticPaths": {
      "source": "posts",
      "params": {
        "slug": {
          "expr": "get",
          "base": { "expr": "var", "name": "item" },
          "path": "slug"
        }
      }
    }
  },
  "data": {
    "posts": {
      "type": "glob",
      "pattern": "content/posts/*.mdx",
      "transform": "mdx"
    }
  },
  "view": { ... }
}

Build Output

bash
npx constela-start build
# Generates:
# dist/posts/hello-world.html
# dist/posts/another-post.html
# ...

API Routes

Create API endpoints by creating TypeScript files with HTTP method handlers:

typescript
// src/routes/api/users.ts
import type { APIContext } from '@constela/start';

export async function GET({ params, query }: APIContext) {
  const users = await db.users.findMany();
  return Response.json({ users });
}

export async function POST({ request }: APIContext) {
  const body = await request.json();
  const user = await db.users.create(body);
  return Response.json({ created: user }, { status: 201 });
}

export async function PUT({ request, params }: APIContext) {
  const body = await request.json();
  const user = await db.users.update(params.id, body);
  return Response.json({ updated: user });
}

export async function DELETE({ params }: APIContext) {
  await db.users.delete(params.id);
  return new Response(null, { status: 204 });
}

APIContext

PropertyTypeDescription
requestRequestThe incoming request object
paramsRecord<string, string>Route parameters
queryURLSearchParamsQuery string parameters
envobjectEnvironment bindings (for edge runtimes)

Middleware

Create middleware using the next() pattern:

typescript
// src/middleware.ts
import type { MiddlewareHandler } from '@constela/start';

export const middleware: MiddlewareHandler = async (ctx, next) => {
  // Before request
  console.log(`${ctx.request.method} ${ctx.request.url}`);

  const start = Date.now();

  // Call next middleware or route handler
  const response = await next();

  // After request
  console.log(`Completed in ${Date.now() - start}ms`);

  return response;
};

Route-specific Middleware

typescript
// src/routes/admin/_middleware.ts
import type { MiddlewareHandler } from '@constela/start';

export const middleware: MiddlewareHandler = async (ctx, next) => {
  const token = ctx.request.headers.get('Authorization');

  if (!isValidToken(token)) {
    return new Response('Unauthorized', { status: 401 });
  }

  return next();
};

CLI Commands

Development Server

bash
npx constela-start dev
OptionDescription
--port, -pPort number (default: 3000)
--hostHost to bind (default: localhost)
--openOpen browser automatically

Production Build

bash
npx constela-start build
OptionDescription
--targetBuild target: node, cloudflare, vercel, deno
--outDirOutput directory (default: dist)

Production Server

bash
npx constela-start start

Edge Runtime

@constela/start supports deployment to edge platforms:

Cloudflare Workers

typescript
// constela.config.ts
import { defineConfig } from '@constela/start';

export default defineConfig({
  target: 'cloudflare',
  // Cloudflare-specific options
});

Vercel Edge

typescript
// constela.config.ts
import { defineConfig } from '@constela/start';

export default defineConfig({
  target: 'vercel',
  edge: true
});

Deno Deploy

typescript
// constela.config.ts
import { defineConfig } from '@constela/start';

export default defineConfig({
  target: 'deno'
});

Configuration Options

OptionTypeDescription
target'node' | 'cloudflare' | 'vercel' | 'deno'Deployment target
edgebooleanEnable edge runtime (Vercel)
ssrbooleanEnable server-side rendering (default: true)
prerenderstring[]Routes to pre-render at build time

Summary

@constela/start provides:

  • Zero-config development: Just create JSON routes and start building
  • File-based routing: Intuitive directory-to-URL mapping
  • Full-stack capabilities: SSR, SSG, and API routes in one framework
  • Edge-ready: Deploy anywhere with platform-specific adapters
  • JSON-first pages: Define pages in Constela JSON DSL, use TypeScript only for server logic