Your First Project


Let's build your first nself project step by step. We'll create a complete backend for a todo application with user authentication, real-time data, and file storage.

What We'll Build

In this tutorial, we'll create a backend that includes:

  • PostgreSQL database with todo items table
  • GraphQL API with Hasura
  • User authentication with JWT tokens
  • File storage for attachments
  • Real-time subscriptions for live updates

Step 1: Create Project Directory

First, create a new directory for your project and navigate to it:

mkdir todo-backend
cd todo-backend

Step 2: Initialize nself

Initialize a new nself project. This creates the .env.local configuration file:

nself init

You should see:

[INFO] Initializing nself project...
[SUCCESS] Project initialized!
[INFO] Please edit .env.local to configure your project

Step 3: Configure Your Project

Open .env.local in your editor and customize the configuration:

# Project Configuration
PROJECT_NAME=todo-backend
ENVIRONMENT=development

# Database
POSTGRES_DB=todos
POSTGRES_PASSWORD=secure_password_here

# Enable Services
NHOST_DASHBOARD_ENABLED=true
REDIS_ENABLED=true

# Optional: Add microservices later
NESTJS_SERVICES=
PYTHON_SERVICES=

Security Note

For production, use nself prod to generate secure passwords automatically.

Step 4: Build Project Structure

Generate the Docker configuration and project structure:

nself build

This creates:

  • docker-compose.yml - Service orchestration
  • hasura/ - GraphQL configuration
  • postgres/ - Database initialization
  • nginx/ - Proxy configuration
  • schema.dbml - Database schema file

Step 5: Define Database Schema

Edit schema.dbml to define your todo application schema:

// Todo Application Schema

Table users {
  id uuid [pk, default: `gen_random_uuid()`]
  email varchar(255) [unique, not null]
  display_name varchar(255)
  created_at timestamp [default: `now()`]
  updated_at timestamp [default: `now()`]
}

Table todos {
  id uuid [pk, default: `gen_random_uuid()`]
  user_id uuid [not null, ref: > users.id]
  title varchar(255) [not null]
  description text
  completed boolean [default: false]
  due_date timestamp
  created_at timestamp [default: `now()`]
  updated_at timestamp [default: `now()`]
  
  indexes {
    user_id
    completed
    (user_id, completed) [name: "idx_user_completed"]
  }
}

Table todo_attachments {
  id uuid [pk, default: `gen_random_uuid()`]
  todo_id uuid [not null, ref: > todos.id]
  file_url varchar(500) [not null]
  file_name varchar(255) [not null]
  file_size integer
  mime_type varchar(100)
  uploaded_at timestamp [default: `now()`]
  
  indexes {
    todo_id
  }
}

Step 6: Generate Migrations

Generate SQL migrations from your schema:

nself db run

This analyzes your DBML schema and creates migration files in hasura/migrations/.

Step 7: Start Services

Start all services:

nself up

Wait for all services to start (usually 30-60 seconds for first run).

Step 8: Apply Migrations

Apply the database migrations:

nself db migrate:up

Step 9: Access Your Services

Your backend is now running! Access the services:

Or use the secure local domains:

  • https://api.local.nself.org - GraphQL API
  • https://auth.local.nself.org - Authentication
  • https://storage.local.nself.org - File storage
  • https://dashboard.local.nself.org - Dashboard

Step 10: Test Your API

Open Hasura Console and try this GraphQL query:

mutation CreateTodo {
  insert_todos_one(object: {
    title: "Learn nself"
    description: "Complete the tutorial"
    user_id: "00000000-0000-0000-0000-000000000000"
  }) {
    id
    title
    created_at
  }
}

Next Steps

Add Authentication

Configure authentication rules in Hasura Console:

  1. Go to Data → todos → Permissions
  2. Add role "user"
  3. Set row-level security: { "user_id": { "_eq": "X-Hasura-User-Id" } }

Add a Microservice

Add a NestJS API for custom business logic:

# Edit .env.local
NESTJS_SERVICES=api

# Rebuild and restart
nself build && nself restart

Deploy to Production

When ready for production:

# Generate production config
nself prod

# Edit .env.prod-template with your domain
# Deploy to your server

Complete Project Files

Your project structure should now look like:

todo-backend/
├── .env.local           # Configuration
├── docker-compose.yml   # Service orchestration
├── schema.dbml          # Database schema
├── hasura/
│   ├── metadata/        # GraphQL metadata
│   └── migrations/      # SQL migrations
├── postgres/
│   └── init.sql         # Database initialization
├── nginx/
│   └── default.conf     # Proxy configuration
└── seeds/
    ├── development.sql  # Dev seed data
    └── production.sql   # Prod seed data

Summary

Congratulations! You've built a complete backend with:

  • ✅ PostgreSQL database with custom schema
  • ✅ GraphQL API with Hasura
  • ✅ Authentication service ready
  • ✅ File storage configured
  • ✅ Local SSL domains working
  • ✅ Development environment running

Next, explore: