Skip to content

Minimal Blog

Beginner
api database backend

A complete blog API backend with authentication, CRUD endpoints, and database integration using Habits bits.

Build a production-ready blog API in minutes with the Minimal Blog example. This showcases how Habits can create full backend applications with minimal code.

Features

  • User Authentication: JWT-based login and registration
  • Blog Posts CRUD: Create, read, update, and delete posts
  • Database Integration: SQLite/PostgreSQL with automatic migrations
  • API Documentation: Well-structured RESTful endpoints

Perfect for learning how to build API backends with Habits, or as a starter template for your own content management systems.


Run Your .habit File

Run on Mobile

  • [ ] Download the Cortex App from store or the downloads page
  • [ ] Open the Cortex App on your device
  • [ ] Tap "Open Habit" or "+" button
  • [ ] Select your .habit file from your device storage
  • [ ] The habit will be loaded and ready to run

Run on Desktop

  • [ ] Download the Cortex App for your platform from the downloads page
  • [ ] Install and open the Cortex App
  • [ ] Click "Open Habit" or drag & drop your .habit file
  • [ ] The habit will be loaded and ready to run
  • [ ] Optional: Place a .env file in the same directory as your .habit file to override environment variables

Run on Server

Run your .habit file as a server using the Cortex CLI:

bash
# Install and run in one command
npx @ha-bits/cortex --config ./your-app.habit
  • [ ] Make sure Node.js 20+ is installed
  • [ ] Run the command above with your .habit file path
  • [ ] Server will start on the specified port (default: 3000)
  • [ ] Access the app at http://localhost:3000
  • [ ] Optional: Place a .env file next to your .habit file - it will automatically override any embedded environment variables

Run Serverless

For serverless or containerized deployments, we recommend using Docker:

bash
# Using Docker (recommended for serverless)
docker run -p 3000:3000 -v $(pwd)/your-app.habit:/app/habit.habit \
  node:20-alpine npx @ha-bits/cortex --config /app/habit.habit --host 0.0.0.0

Or create a Dockerfile:

dockerfile
FROM node:20-alpine
WORKDIR /app
COPY your-app.habit ./
COPY .env ./ # Optional: include environment variables
RUN npm install -g @ha-bits/cortex
EXPOSE 3000
CMD ["cortex", "--config", "./your-app.habit", "--host", "0.0.0.0"]
  • [ ] Create a Dockerfile or use the Docker run command above
  • [ ] Deploy to your preferred cloud provider (AWS, GCP, Azure, etc.)
  • [ ] Configure environment variables via your cloud provider's secrets management
  • [ ] Set up health checks at /habits/base/api endpoint

Workflow Visualization

Requirements

  • Node.js 18+

Key Files

yaml
version: "1.0"

workflows:
  - id: login
    path: ./habits/login.yaml
    enabled: true

  - id: get-posts
    path: ./habits/get-posts.yaml
    enabled: true
    
  - id: get-post
    path: ./habits/get-post.yaml
    enabled: true
    
  - id: create-post
    path: ./habits/create-post.yaml
    enabled: true
    
  - id: delete-post
    path: ./habits/delete-post.yaml
    enabled: true
    
  - id: send-contact
    path: ./habits/send-contact.yaml
    enabled: true

server:
  port: 13000
  host: "0.0.0.0"
  frontend: ./frontend
  openapi: true

logging:
  level: info
  outputs: [console]
  format: text
  colorize: true
example
# Minimal Blog Example Environment Variables
# Copy this file to .env and fill in your values

# SMTP configuration for contact form
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-smtp-username
SMTP_PASSWORD=your-smtp-password
SMTP_FROM=blog@example.com
CONTACT_EMAIL=contact@example.com

# Authentication
JWT_SECRET=your-jwt-secret-key-change-this-in-production
yaml
id: create-post
name: Create New Post
description: Create a new blog post (admin function - requires authentication)

input:
  - id: title
    type: string
    required: true
  - id: content
    type: string
    required: true
  - id: status
    type: string
    required: false

nodes:
  # Verify JWT from auth_token cookie
  - id: verify-auth
    type: bits
    data:
      framework: bits
      source: npm
      module: "@ha-bits/bit-auth"
      operation: verify
      params:
        token: "{{habits.cookies.auth_token}}"
        secret: "{{habits.env.JWT_SECRET}}"

  - id: insert-post
    type: bits
    data:
      framework: bits
      source: npm
      module: "@ha-bits/bit-database"
      resource: insert
      operation: insert
      params:
        collection: "posts"
        document:
          title: "{{habits.input.title}}"
          content: "{{habits.input.content}}"
          status: "published"
          createdAt: "{{habits.now}}"
          author: "{{verify-auth.payload.username}}"

edges:
  - source: verify-auth
    target: insert-post

output:
  authorized: "{{verify-auth.valid}}"
  success: "{{insert-post.success}}"
  id: "{{insert-post.id}}"
  post: "{{insert-post.document}}"
  authError: "{{verify-auth.error}}"
yaml
id: delete-post
name: Delete Post
description: Delete a blog post by ID (admin function - requires authentication)

input:
  - id: postId
    type: string
    required: true

nodes:
  # Verify JWT from auth_token cookie
  - id: verify-auth
    type: bits
    data:
      framework: bits
      source: npm
      module: "@ha-bits/bit-auth"
      operation: verify
      params:
        token: "{{habits.cookies.auth_token}}"
        secret: "{{habits.env.JWT_SECRET}}"

  - id: remove-post
    type: bits
    data:
      framework: bits
      source: npm
      module: "@ha-bits/bit-database"
      resource: delete
      operation: delete
      params:
        collection: "posts"
        key: "{{habits.input.postId}}"

edges:
  - source: verify-auth
    target: remove-post

output:
  authorized: "{{verify-auth.valid}}"
  success: "{{remove-post.success}}"
  deleted: "{{remove-post.deleted}}"
  authError: "{{verify-auth.error}}"
yaml
id: get-post
name: Get Single Post
description: Retrieve a single post by its ID

input:
  - id: postId
    type: string
    required: true

nodes:
  - id: fetch-post
    type: bits
    data:
      framework: bits
      source: npm
      module: "@ha-bits/bit-database"
      resource: get
      operation: get
      params:
        collection: "posts"
        key: "{{habits.input.postId}}"

edges: []

output:
  found: "{{fetch-post.found}}"
  post: "{{fetch-post.value}}"

Quick Start

Run using the Habits CLI wrapper, recommended if you develop local Habits

# First, download the example files
npx habits@latest cortex --config ./minimal-blog/stack.yaml

Released under the Apache 2.0 License.