Creating Your First Application

Goal: Build a complete Webspresso application with a server-rendered page, an API endpoint, and a service within 5 minutes.


1. Project Initialization #

Create a new Webspresso project in a clean directory:

BASH
npx webspresso new quickstart-app --install
cd quickstart-app

The scaffolding creates a minimal, ready-to-run application structure with Tailwind CSS support preconfigured.


2. Server Entry Point (server.js) #

Open server.js. This is where createApp() configures Express, Nunjucks, routing, and optional plugins:

JAVASCRIPT
const path = require('path');
require('./config/load-env');
const { createApp } = require('webspresso');

const { app } = createApp({
  pagesDir: path.join(__dirname, 'pages'),
  viewsDir: path.join(__dirname, 'views'),
  publicDir: path.join(__dirname, 'public'),
  server: {
    shutdown: { enabled: true, timeout: 10000 },
    compression: true,
  },
});

const PORT = process.env.PORT || 7331;
app.listen(PORT, () => {
  console.log(`Server listening at http://localhost:${PORT}`);
});

3. Creating Your First SSR Page #

Webspresso uses file-based routing. Any .njk file placed in the pages/ directory automatically maps to a URL route.

Create pages/index.njk:

HTML
{% extends "views/layout.njk" %}

{% block content %}
<main class="max-w-4xl mx-auto py-12 px-4">
  <div class="bg-white dark:bg-slate-800 shadow rounded-lg p-8">
    <h1 class="text-3xl font-bold text-slate-900 dark:text-white mb-4">
      {{ message }}
    </h1>
    <p class="text-slate-600 dark:text-slate-300 mb-6">
      Welcome to your new Webspresso application!
    </p>
    <div class="p-4 bg-slate-50 dark:bg-slate-700 rounded border border-slate-200 dark:border-slate-600">
      <span class="font-semibold">Server Time:</span> {{ timestamp }}
    </div>
  </div>
</main>
{% endblock %}

Adding Server-Side Data (definePage() or load()) #

To fetch data on the server before rendering the template or to build pure JavaScript pages, export with definePage():

Create or edit pages/index.js (or companion loader for pages/index.njk):

JAVASCRIPT
const { definePage } = require('webspresso');

module.exports = definePage({
  async load({ req, res, ctx }) {
    return {
      message: 'Hello from Webspresso!',
      timestamp: new Date().toISOString(),
    };
  },
});

4. Creating a JSON API Endpoint #

API routes are defined under pages/api/ (or src/api/) using HTTP method suffixes in the filename wrapped with defineApi().

Create pages/api/health.get.js:

JAVASCRIPT
const { defineApi } = require('webspresso');

module.exports = defineApi({
  handler: async (req, res) => {
    return {
      status: 'ok',
      uptime: process.uptime(),
      timestamp: Date.now(),
    };
  },
});

This automatically mounts GET /api/health with automatic JSON serialization.

Adding Zod Schema Validation #

To validate request query parameters, body, or params, export a schema with defineApi():

Create pages/api/greet.post.js:

JAVASCRIPT
const { defineApi, z } = require('webspresso');

module.exports = defineApi({
  schema: {
    body: z.object({
      name: z.string().min(2),
      email: z.string().email().optional(),
    }),
  },

  handler: async (req, res) => {
    // req.input.body is strictly validated by Zod
    const { name, email } = req.input.body;
    return {
      greeting: `Hello, ${name}!`,
      sentTo: email || 'not provided',
    };
  },
});

5. Creating a Reusable Service #

Webspresso provides a file-based Services layer for business logic.

Create services/user/greet.js:

JAVASCRIPT
const { defineService } = require('webspresso');

module.exports = defineService({
  schema({ z }) {
    return {
      name: 'string',
    };
  },

  async handler(input, ctx) {
    return {
      message: `Welcome, ${input.name}!`,
    };
  },
});

Call this service from any route or SSR loader via ctx.service() or req.service():

JAVASCRIPT
const result = await req.service('user.greet', { name: 'Alice' });

6. Running the Development Server #

Start the application with live reloading:

BASH
npm run dev
  • Open http://localhost:7331 in your browser to see your SSR page.
  • Test the API: curl http://localhost:7331/api/health.

Next Steps #

Created · Updated