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.

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 directly using Cortex package, recommended for production runs, does not inlcude base or extra depdencies.

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

Released under the Apache 2.0 License.