Skip to content
This page View Markdown Open in ChatGPT Open in Claude

SSR websites

A website of API Maker is a folder of files : pages written in TSX, the components and the code they share, files sent as they are, and its settings. The server renders the pages next to your data and sends HTML : the browser shows a page as it reads it, and nothing runs in it unless you add a script.

A website answers at /ssr/<apiPath>/<name>/ of the server : the path of its account, as the addresses of its APIs have it, then its name, unique in the account. A reverse proxy shows it at the root of a domain of its own : see deployment.

Where Dashboard → SSR Websites : a website from a template, its files, the editor, the preview, its UI libraries, its routes, its settings. The panel.
Pages pages/ : index.tsx is /, blog/[slug].tsx is /blog/:slug, a .ts file is an endpoint. Routing.
Data A loader reads on the server ; ctx.db reaches the tables the website lists, with the rows each visitor may reach. Data.
Visitors Sign-in with the auth providers of the account, access on a page or a layout, groups. Authentication.
Forms An action takes a form ; every form carries a CSRF token by itself. Forms.
Speed Templates compiled when a file is saved, engines kept per website, pages kept by the server. Cache.
Browser Bootstrap, Tailwind, Chart.js, htmx… from a catalog with their hashes. UI libraries.
Git src/ssr/<name>/ of the repository of the account : commit, pull, revert. Git.

Your first website

  1. Dashboard → SSR Websites → New website.
  2. Choose Starter : a page for every feature, each with comments which say why it is written so.
  3. Name it demo. Its address is /ssr/<apiPath>/demo/ : /ssr/acme/demo/ for the account of the path acme.
  4. Open pages/index.tsx, change a word, save with Ctrl+S : the preview reloads by itself.

A page :

pages/products/index.tsx
import { formatCurrency } from 'am-ssr';
import type { PageConfig, PageContext, PageProps } from 'am-ssr';

export const config: PageConfig = {
    title: 'Products',
    // One page for every anonymous visitor, kept a minute.
    cache: { maxAge: 60, scope: 'public' },
};

// Runs on the server before the page renders : what it returns is `data`.
export async function loader(ctx: PageContext) {
    return {
        products: await ctx.db.query({ instance: 'mongodb', database: 'shop', table: 'products', limit: 20 }),
    };
}

export default function Products({ data }: PageProps<typeof loader>) {
    return (
        <ul>
            {data.products.map((product) => (
                <li>
                    <a href={`/products/${product.slug}`}>{product.name}</a> {formatCurrency(product.price, 'USD')}
                </li>
            ))}
        </ul>
    );
}

And its website lists the table it reads, in site.config.ts : a website reaches no table it does not name.

site.config.ts
import { defineSite } from 'am-ssr';

export default defineSite({
    title: 'My shop',
    titleTemplate: '%s · My shop',
    data: { read: ['mongodb/shop/products'] },
});

The files of a website

site.config.ts         the settings : tables ctx.db reaches, auth providers, CSP, cache, SEO, redirects
libraries.json         the UI libraries the pages load in the browser
pages/
  _layout.tsx          the HTML around every page
  _middleware.ts       code which runs before every page and endpoint
  _404.tsx  _error.tsx what a path no page answers shows, and what a page which failed shows
  index.tsx            /
  about.tsx            /about
  blog/[slug].tsx      /blog/:slug
  docs/[...path].tsx   /docs/a/b/c
  (shop)/cart.tsx      /cart, with the layout of (shop)/
  api/items.ts         an endpoint : GET, POST, PUT, PATCH, DELETE
components/            components the pages share : import Card from '@/components/Card'
lib/                   code the pages share
data/                  JSON the pages import : import posts from '@/data/posts.json'
public/                files sent as they are : public/logo.png is /logo.png

What happens to a request

A request to an SSR website : route, access, cache, render, HTML 1 · route /ssr/shop/store/ products compiled at save 2 · access May they open it ? before any code of the page runs 3 · page cache Kept already ? sent as it was kept : no render 4 · render middlewares, the action of a form, the loaders at once, page, layouts 5 · HTML head and SEO, CSRF token, nonce of the CSP a kept page skips the render refused The loaders read the tables of the website on the server, with only the rows each visitor may reach.
  1. The server finds the website of /ssr/<apiPath>/<name>, built for its version : its files were compiled when they were saved.
  2. Redirects of the settings, files of public/, /robots.txt and /sitemap.xml answer first.
  3. The route of the path, and who may open it : the access of its layouts and of the page, read from their code when they were saved. A visitor who may not is refused before any code of the page runs.
  4. A form of an unsafe method needs the CSRF token of its visitor.
  5. A page kept by the cache is sent as it was kept.
  6. The render, in an engine of the website : the middlewares, the action for a form, the loaders of the layouts and of the page at once, the page, then each layout around it.
  7. The server writes the head (title, description, Open Graph, the UI libraries), the CSRF token and the nonce of the CSP into the page, and sends it.

Safe by default

  • Every value is escaped where it is written. HTML which is not is asked for by name (raw()).
  • ctx.db reaches the tables site.config.ts lists, with the row rules it gives each : { customer_id: '$user._id' } keeps every query to the rows of the visitor.
  • Sessions are cookies sealed with AES-256-GCM, HttpOnly, SameSite=Lax, Secure over HTTPS.
  • Every form has a CSRF token ; every <script> of a page has the nonce of the CSP ; javascript: addresses are refused.
  • Each website runs in engines of its own, with a limit of memory and of time.

See security for all of it.

Read on

Page What it says
The panel The files of a website (new files and folders, rename, drag and drop, uploads), the keys of the editor, the preview at the size of a device.
Pages and routing Files and paths, layouts, middlewares, 404 and error pages, endpoints, public files.
Data Loaders, ctx.db, ctx.api, ctx.sys, ctx.fetch, ctx.cache, JSON files.
Forms Actions, checks, messages, the redirect after a post, uploads.
Authentication Sign-in, sign-up, sign-out, access, groups, row rules.
Caching Pages kept by the server, tags, what is never kept.
UI libraries The catalog, npm packages, self-hosting, the CSP.
SEO Title, description, Open Graph, canonical, sitemap, robots, JSON-LD.
Settings Every setting of site.config.ts.
Security What the server does for you, and what is left to you.
Deployment Domains, reverse proxies, the settings of the server.
Git The websites in the repository : commit, pull, revert, editors.
Templates The websites and the files a new one starts from.
Examples Recipes for what websites do most.
Reference The module am-ssr, ctx, PageConfig.
Guide for AI What an assistant needs to write a page right the first time.