Post

ExpressJS Web App Following the 12 Factor App Methodology

In this article, we will walk through creating a simple ExpressJS web application that adheres to the 12 Factor App methodology. We’ll use Prisma as the ORM to interact with a SQLite database and build a CRUD application for collecting ideas for content. Let’s dive in!

Here is what we are going to implement

graph TD;
    A[Node.js Application] --> B[ExpressJS Framework];
    B --> C[Prisma ORM];
    C --> D[SQLite Database];
    B --> E[HTTP Routes];
    
    E --> |GET| F[Retrieve Ideas];
    E --> |POST| G[Create New Idea];
    E --> |PUT| H[Update Existing Idea];
    E --> |DELETE| I[Delete Idea];

    A --> J[Environment Variables /.env];
    A --> K[Prisma Schema];
    A --> L[Package.json - Dependencies];

    style A fill:#FFC300,stroke:#333,stroke-width:2px, color:#000000;
    style D fill:#28A745,stroke:#333,stroke-width:2px, color:#000000;

Prerequisites

  • Basic understanding of JavaScript and Node.js
  • Node.js and npm installed
  • A code editor like VS Code

Step 1: Setting Up Your Environment

First, ensure you have Node.js and npm installed. You can verify your setup by running:

1
2
node -v
npm -v

Create a new directory for your project and initialize a new Node.js application:

1
2
3
mkdir express-prisma-ideas
cd express-prisma-ideas
npm init -y

Step 2: Installing Dependencies

We’ll need ExpressJS, Prisma, and SQLite dependencies. Install them by running:

1
npm install express prisma @prisma/client sqlite3

Next, initialize Prisma:

1
npx prisma init

This command will set up a new Prisma project with a prisma directory containing a schema.prisma file and update your .env file with the database connection URL.

Configuring SQLite Database

In your .env file, set up the DATABASE_URL to use a SQLite database:

1
DATABASE_URL="file:./dev.db"

Step 3: Defining the Data Model

Edit prisma/schema.prisma to define your data model:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Idea {
  id        Int      @id @default(autoincrement())
  title     String
  content   String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

Run the following commands to create the SQLite database and generate the Prisma client:

1
2
npx prisma migrate dev --name init
npx prisma generate

Step 4: Setting Up ExpressJS Application

Create an entry point file named index.js and set up ExpressJS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const express = require('express');
const { PrismaClient } = require('@prisma/client');
const bodyParser = require('body-parser');

const app = express();
const prisma = new PrismaClient();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const PORT = process.env.PORT || 3000;

// Routes
app.get('/ideas', async (req, res) => {
  const ideas = await prisma.idea.findMany();
  res.json(ideas);
});

app.post('/ideas', async (req, res) => {
  const { title, content } = req.body;
  const newIdea = await prisma.idea.create({ data: { title, content } });
  res.json(newIdea);
});

app.put('/ideas/:id', async (req, res) => {
  const { id } = req.params;
  const { title, content } = req.body;
  const updatedIdea = await prisma.idea.update({ where: { id: parseInt(id) }, data: { title, content } });
  res.json(updatedIdea);
});

app.delete('/ideas/:id', async (req, res) => {
  const { id } = req.params;
  await prisma.idea.delete({ where: { id: parseInt(id) } });
  res.json({ message: 'Idea deleted' });
});

// Start server
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

Step 5: Adhering to the 12 Factor App Methodology

I. Codebase

The project must be tracked in a version control system like Git.

1
git init

II. Dependencies

All dependencies are declared in package.json.

III. Config

Environment-specific configurations should be stored in environment variables, as seen in the .env file.

IV. Backing Services

Use Prisma to manage database connections and interactions.

V. Build, Release, Run

Separate the build and release stages. Note: In simple projects, package.json scripts can ensure the app runs correctly.

VI. Processes

Ensure the app is stateless by keeping all necessary state in the database.

VII. Port Binding

Use environment variables to define the port, and bind the app to a specified port.

VIII. Concurrency

Handle different workloads by scaling app processes.

IX. Disposability

Ensure the app can start up fast and shut down gracefully. Implement any necessary cleanup codes.

X. Dev/Prod Parity

Keep development, staging, and production environments as similar as possible.

XI. Logs

Treat logs as event streams.

XII. Admin Processes

Run one-off admin processes as needed using a minimalistic approach, usually by scripts.

Conclusion

We’ve built a simple ExpressJS application using Prisma with a SQLite database while ensuring it follows the 12 Factor App methodology. This application model is scalable, maintainable, and easy to deploy across different environments.

Happy coding!

This post is licensed under CC BY 4.0 by the author.