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.
In this tutorial, we'll create a backend that includes:
First, create a new directory for your project and navigate to it:
mkdir todo-backend
cd todo-backend
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
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=
For production, use nself prod
to generate secure passwords automatically.
Generate the Docker configuration and project structure:
nself build
This creates:
docker-compose.yml
- Service orchestrationhasura/
- GraphQL configurationpostgres/
- Database initializationnginx/
- Proxy configurationschema.dbml
- Database schema fileEdit 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
}
}
Generate SQL migrations from your schema:
nself db run
This analyzes your DBML schema and creates migration files in hasura/migrations/
.
Start all services:
nself up
Wait for all services to start (usually 30-60 seconds for first run).
Apply the database migrations:
nself db migrate:up
Your backend is now running! Access the services:
http://localhost:8080/v1/graphql
Or use the secure local domains:
https://api.local.nself.org
- GraphQL APIhttps://auth.local.nself.org
- Authenticationhttps://storage.local.nself.org
- File storagehttps://dashboard.local.nself.org
- DashboardOpen 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
}
}
Configure authentication rules in Hasura Console:
{ "user_id": { "_eq": "X-Hasura-User-Id" } }
Add a NestJS API for custom business logic:
# Edit .env.local
NESTJS_SERVICES=api
# Rebuild and restart
nself build && nself restart
When ready for production:
# Generate production config
nself prod
# Edit .env.prod-template with your domain
# Deploy to your server
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
Congratulations! You've built a complete backend with:
Next, explore: