Skip to content

Express Knex Todos

A REST API for managing todos, built with Node.js/Express and PostgreSQL using Knex.js. Demonstrates deploying a database-backed Node.js application on AppMecca.

Overview

Repository git@gitlab.com:appmecca-sample-apps/express-knex-todos.git
Tech Stack Node.js 22 / Express / Knex.js
Services api (port 3000)
Database Yes
Cache None
URL Pattern https://express-knex-todos-api.{env}.appmecca.net

What This App Demonstrates

  • Deploying a database-backed Node.js application on AppMecca
  • Knex.js migrations with JavaScript migration files
  • Entrypoint pattern for Node.js (waiting for database, running migrations)
  • REST API with full CRUD operations against PostgreSQL
  • The database: enabled: true configuration pattern

appmecca.yaml

containers:
  - name: api
    dockerfile: Dockerfile
    context: .

deploy:
  - name: api
    container: api
    port: 3000
    health_check:
      path: /health

database:
  enabled: true

The only addition compared to Node Express API is database: enabled: true. AppMecca provisions a PostgreSQL database on first deploy and injects DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD, and DATABASE_URL into the container automatically. Read DATABASE_URL — the database and user names are generated identifiers, not derived from the app name.

Step-by-Step Deployment

1. Register the app

mecca register git@gitlab.com:appmecca-sample-apps/express-knex-todos.git -e dev

2. Build the container

mecca build trigger -e dev -g git@gitlab.com:appmecca-sample-apps/express-knex-todos.git
Expected output
Build started: build-xxxxxxxx
Cloning repository...
Building container: api
Pushing image to the private registry: express-knex-todos/api
Build complete. Duration: 35s

3. Deploy

mecca app deploy -e dev express-knex-todos
Expected output
Deploying express-knex-todos to dev...
Deployment queued: deploy-xxxxxxxx
Waiting for agents...
Container started: express-knex-todos-api
Health check passed: /health -> 200 OK
Deployment complete.

Services:
  api: https://express-knex-todos-api.dev.appmecca.net

4. Verify

mecca queue status takes the action ID printed when you deployed. To look up the app itself — including the exact URL for your group — use mecca app show:

mecca app show express-knex-todos -e dev

Test the API:

# Health check
curl https://express-knex-todos-api.dev.appmecca.net/health
{"status": "healthy"}
# Create a todo
curl -X POST https://express-knex-todos-api.dev.appmecca.net/api/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "Deploy more apps", "done": false}'

# List all todos
curl https://express-knex-todos-api.dev.appmecca.net/api/todos

# Update a todo
curl -X PUT https://express-knex-todos-api.dev.appmecca.net/api/todos/1 \
  -H "Content-Type: application/json" \
  -d '{"done": true}'

# Delete a todo
curl -X DELETE https://express-knex-todos-api.dev.appmecca.net/api/todos/1

API Endpoints

Method Path Description
GET /health Health check
GET /api/todos List all todos
POST /api/todos Create a todo
PUT /api/todos/:id Update a todo
DELETE /api/todos/:id Delete a todo

Key Concepts

Knex.js migrations (JavaScript migration files)

Knex.js manages database schema changes using JavaScript migration files. Migrations are created with npx knex migrate:make create_todos and live in a migrations/ directory. Each file exports up and down functions that define the schema change and its rollback. The entrypoint script runs npx knex migrate:latest on startup to apply pending migrations before starting the Express server.

Entrypoint pattern for Node.js

The entrypoint script follows the same universal pattern as other languages: wait for the database, run migrations, then start the app. In Node.js, the database readiness check uses pg (the PostgreSQL client) to attempt a connection in a retry loop. Once the database is reachable, npx knex migrate:latest applies schema changes, and node server.js starts the Express application.

Any language, same workflow

AppMecca does not care what language your application is written in. As long as your container listens on the declared port and responds to health checks, the deployment workflow is identical.

What's Next?