Introduction
Having architected scalable applications for various industries, I've seen firsthand how CodeIgniter simplifies back-end development. According to the 2024 Stack Overflow Developer Survey, PHP is used by over 26% of developers, making it a crucial skill for many applications. CodeIgniter, being a lightweight PHP framework, enables developers to create robust applications efficiently, which is essential in today’s fast-paced tech environment.
This tutorial teaches the essentials of back-end development with CodeIgniter 4 (4.x). You will set up a reproducible environment, create a RESTful JSON API with input validation, implement secure user authentication, and manage your database and tests. The examples provide concrete routes, controllers, models, and Docker configuration so you can follow along and extend these minimal, secure patterns for production.
To get started with CodeIgniter, run this command (once):
composer create-project codeigniter4/appstarter my-app
This command creates a new CodeIgniter application in a directory named 'my-app'.
Introduction to CodeIgniter: What You Need to Know
Overview of CodeIgniter
CodeIgniter is a PHP framework designed for building web applications. It offers a simple and elegant toolkit for developers seeking to create dynamic websites. It follows the Model-View-Controller (MVC) architectural pattern, which helps separate application logic from presentation. This separation enhances maintainability and scalability, making it a popular choice among developers. CodeIgniter's lightweight nature allows for rapid development without sacrificing performance.
Another compelling feature of CodeIgniter is its straightforward installation process. It provides clear documentation and built-in libraries and helpers to simplify common tasks, such as form validation, sessions, and routing. By leveraging these features, developers can focus on building unique functionality rather than boilerplate plumbing.
- MVC architecture for better organization
- Lightweight and fast performance
- Simple installation process
- Extensive library of built-in functionalities
Setting Up Your Development Environment for CodeIgniter
Installation Steps
Setting up a development environment for CodeIgniter requires a few steps. First, ensure you have PHP installed on your machine. CodeIgniter 4 requires PHP 7.2 or higher; many teams now standardize on PHP 8.0+ or 8.1 for performance and security improvements.
php -v
Install Composer, which is a dependency manager for PHP. Composer simplifies the installation of libraries and frameworks like CodeIgniter.
Once you have PHP and Composer set up, you can create a new CodeIgniter project using the Composer command in the Introduction. After installation, configure your web server (Apache, Nginx) to point its document root to the public directory of your CodeIgniter application—this improves security by keeping app code out of the web root. For development, using a containerized stack provides parity with production (example below).
- Ensure PHP is installed (7.2+, prefer 8.0 or 8.1)
- Install Composer for dependency management
- Create a new CodeIgniter project using Composer (see the Intro)
- Configure your web server to point to the public directory
Understanding MVC Architecture in CodeIgniter
What is MVC?
The Model-View-Controller (MVC) architecture is a design pattern used in CodeIgniter to separate application logic from user interface concerns. In this structure, the Model represents the data and business logic, the View is the user interface, and the Controller handles user input and interactions. This separation promotes organized code and facilitates maintenance.
In CodeIgniter, models are responsible for interacting with the database. Views are simple PHP files that display data to the user, while controllers manage user requests. For instance, when a user submits a form, the controller processes the input, communicates with the model to fetch or update data, and finally loads a view to display the results.
- Model: Handles data and business logic
- View: Displays information to users
- Controller: Manages user requests and interactions
- Promotes organized code structure
Building Your First Application: Step-by-Step Guide
Local Server and Initial Database Configuration
This section focuses on preparing a local server and initializing your first database—distinct from the global environment setup above. Use XAMPP, MAMP, or a Docker-based stack. For parity with production environments and reproducibility across teams, I recommend Docker: it ensures the same PHP, FPM, and database versions locally and in CI pipelines, and it simplifies onboarding and dependency management.
Create a database for your app and grant access for your development user. From a MySQL client or phpMyAdmin, create the database as shown below:
CREATE DATABASE codeigniter_db;
Update app/Config/Database.php (CodeIgniter 4 location) or equivalent environment variables so your database credentials are loaded via .env for local development. Point your web server document root to public/ to avoid exposing framework files.
- Use a development-specific database (do not share production DB credentials)
- Prefer Docker containers for reproducible environments (example below)
- Store credentials in
.envand never commit them
CLI scaffolding with php spark
CodeIgniter 4 includes a CLI tool, spark, to speed up scaffolding and common tasks. Example commands (run from your project root):
php spark make:controller Auth
php spark make:model UserModel
php spark migrate
These commands generate controller and model boilerplate and run available migrations. Using php spark keeps a consistent project structure and is especially useful when pairing with CI pipelines.
Example: Minimal docker-compose for local development
Below is a compact docker-compose.yml for local development that uses PHP-FPM, Nginx, MySQL, and phpMyAdmin. These images and tags are common choices for a modern PHP stack.
version: '3.8'
services:
php:
image: php:8.1-fpm
volumes:
- ./:/var/www/html
working_dir: /var/www/html
web:
image: nginx:1.23-alpine
ports:
- "8080:80"
volumes:
- ./:/var/www/html:ro
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: codeigniter_db
volumes:
- db_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin:5.1
environment:
PMA_HOST: db
ports:
- "8081:80"
volumes:
db_data:
Place this docker-compose.yml at your project root (the same directory where composer.json and your CodeIgniter app live). To start the stack, run:
docker-compose up -d
After the services are up, point your browser to the host/port for the web service (commonly localhost:8080) to view the app and localhost:8081 for phpMyAdmin. Configure your app .env to point to the db service and set the DB credentials accordingly.
Building a RESTful API
Routes, Controllers, and JSON Responses
CodeIgniter 4 includes RESTful helpers such as ResourceController which simplifies mapping CRUD operations to HTTP verbs. Below is a minimal example: define a resource route and a controller that returns JSON.
Add a resource route in app/Config/Routes.php:
$routes->resource('users');
Create a model at app/Models/UserModel.php:
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $allowedFields = ['email', 'password', 'name'];
protected $returnType = 'array';
protected $useTimestamps = true;
}
Create a RESTful controller at app/Controllers/Users.php using ResourceController:
namespace App\Controllers;
use CodeIgniter\RESTful\ResourceController;
use App\Models\UserModel;
class Users extends ResourceController
{
protected $modelName = UserModel::class;
protected $format = 'json';
public function index()
{
return $this->respond($this->model->findAll());
}
public function create()
{
$data = $this->request->getJSON(true);
if (! $this->model->insert($data)) {
return $this->failValidationErrors($this->model->errors());
}
return $this->respondCreated(['id' => $this->model->getInsertID()]);
}
public function update($id = null)
{
$data = $this->request->getJSON(true);
$this->model->update($id, $data);
return $this->respondUpdated();
}
public function delete($id = null)
{
$this->model->delete($id);
return $this->respondDeleted();
}
}
Input validation is essential for API security and data integrity. You can declare validation rules in the model or validate JSON payloads in the controller. Below are two minimal examples: an enhanced model with built-in validation rules, and a controller-side validation approach that works for raw JSON payloads.
Enhanced UserModel with validation rules (Model-based validation)
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $allowedFields = ['email', 'password', 'name'];
protected $returnType = 'array';
protected $useTimestamps = true;
// Model-based validation (CodeIgniter will validate on insert/update)
protected $validationRules = [
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|min_length[8]',
'name' => 'permit_empty|max_length[100]'
];
protected $validationMessages = [
'email' => [
'is_unique' => 'This email is already registered.'
]
];
}
Note: is_unique checks the database and is convenient during registration but requires careful handling for updates. For update flows, use conditional rules or custom validation to skip uniqueness if the email belongs to the current user.
Controller-side validation for JSON payloads (recommended for API endpoints)
use Config\Services;
public function create()
{
$data = $this->request->getJSON(true);
$rules = [
'email' => 'required|valid_email',
'password' => 'required|min_length[8]',
'name' => 'permit_empty|max_length[100]'
];
$validation = Services::validation();
if (! $validation->run($data, $rules)) {
return $this->failValidationErrors($validation->getErrors());
}
// Always hash passwords before storing
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
if (! $this->model->insert($data)) {
return $this->failServerError('Unable to create user');
}
return $this->respondCreated(['id' => $this->model->getInsertID()]);
}
Security and troubleshooting notes for validation:
- Always validate server-side even if the client performs validation. Never trust client input.
- For JSON APIs, ensure clients send the correct Content-Type (
application/json) and parse with$this->request->getJSON(true)orgetBody(). - Return consistent error payloads (status, errors) so clients can handle validation failures predictably.
- Be mindful of
is_uniquewhen updating records—use conditional rules or custom callbacks to avoid false positives.
Troubleshooting tips:
- If validation always fails for JSON payloads, confirm headers and that
getJSON(true)returns an array, not null. - Check database constraints (unique indexes) along with model validation to avoid race conditions.
How to test the API
For beginners, testing endpoints with cURL or a simple JavaScript fetch call is the fastest way to validate behavior. Below are examples for the common CRUD actions exposed by the resource controller.
GET all users (cURL):
curl -X GET "http://localhost:8080/users" -H "Accept: application/json"
GET single user (cURL):
curl -X GET "http://localhost:8080/users/1" -H "Accept: application/json"
POST create user (cURL):
curl -X POST "http://localhost:8080/users" \
-H "Content-Type: application/json" \
-d '{"email":"jane@example.com","password":"s3cret123","name":"Jane"}'
PUT update user (cURL):
curl -X PUT "http://localhost:8080/users/1" \
-H "Content-Type: application/json" \
-d '{"name":"Jane Doe"}'
DELETE user (cURL):
curl -X DELETE "http://localhost:8080/users/1"
Simple JavaScript fetch examples (useful for testing from a front-end or Node REPL):
// GET users
fetch('http://localhost:8080/users', { headers: { 'Accept': 'application/json' } })
.then(r => r.json())
.then(data => console.log(data));
// POST create user
fetch('http://localhost:8080/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'jane@example.com', password: 's3cret123', name: 'Jane' })
}).then(r => r.json()).then(console.log);
Implementing User Authentication
Registration, Login, and Session Management
This section demonstrates a pragmatic, secure approach to user authentication suitable for many CodeIgniter applications. It uses PHP's built-in password_hash() and password_verify(), session storage for login state, and server-side validation. For APIs, consider switching to short-lived JWTs or token-based auth; the examples below focus on session-based patterns for web apps.
Minimal HTML forms (registration & login)
These simple forms demonstrate the flow from the browser to the authentication controller. In production, include = csrf_field() ?> or the equivalent to enable CSRF protection — here we show the fields and actions a beginner needs to wire up.
<!-- Registration form: save as app/Views/auth/register.php -->
<form action="/auth/register" method="post">
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
<label for="password">Password</label>
<input type="password" id="password" name="password" required />
<button type="submit">Register</button>
</form>
<!-- Login form: save as app/Views/auth/login.php -->
<form action="/auth/login" method="post">
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
<label for="password">Password</label>
<input type="password" id="password" name="password" required />
<button type="submit">Login</button>
</form>
Registration controller (minimal example):
namespace App\Controllers;
use App\Models\UserModel;
use CodeIgniter\Controller;
class Auth extends Controller
{
public function register()
{
$userModel = new UserModel();
$data = $this->request->getPost();
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
$userModel->insert($data);
return redirect()->to('/login');
}
public function login()
{
$userModel = new UserModel();
$email = $this->request->getPost('email');
$password = $this->request->getPost('password');
$user = $userModel->where('email', $email)->first();
$session = session();
if ($user && password_verify($password, $user['password'])) {
$session->set('isLoggedIn', true);
$session->set('user', ['id' => $user['id'], 'email' => $user['email']]);
return redirect()->to('/dashboard');
}
return redirect()->back()->with('error', 'Invalid credentials');
}
public function logout()
{
$session = session();
$session->destroy();
return redirect()->to('/login');
}
}
Security best practices for authentication:
- Always hash passwords with
password_hash()and verify withpassword_verify(). - Serve authentication endpoints only over HTTPS in production.
- Enable CSRF protection for forms (CodeIgniter has built-in CSRF in
app/Config/Filters.php/Config\Security). - Throttle or lock accounts after repeated failed login attempts to mitigate brute force attacks.
- Use HTTP-only, secure session cookies and regenerate session IDs on login to prevent fixation.
Troubleshooting tips:
- If sessions are not persisting, verify cookie settings (
app/Config/App.php) and ensure thecookie_secure/cookie_httponlyflags are correctly configured. - For API-only apps, prefer JWTs or opaque tokens stored server-side (e.g., Redis) rather than session cookies.
Working with Databases: Connecting CodeIgniter to MySQL
Establishing Database Connections
Connecting CodeIgniter to MySQL is straightforward. As previously mentioned, you modify the app/Config/Database.php configuration file or set credentials in .env. Ensure your settings match the database you created. Using the correct host, username, password, and database name is crucial. For example, if you're using the default MySQL settings in a local Docker stack, point your host at the service name (e.g., db in docker-compose).
Before running queries, create a minimal users table or use CodeIgniter migrations. Example SQL to create a simple users table:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
name VARCHAR(100),
created_at DATETIME DEFAULT NULL,
updated_at DATETIME DEFAULT NULL
);
Once configured, you can run queries. In models, you can use CodeIgniter's Model methods or the Query Builder for CRUD operations. Examples below show common Create, Read, Update, Delete patterns using both the model and builder API.
- Edit
app/Config/Database.phpor.envfor connection details - Use a service name (like
db) for containerized hosts - Utilize a dedicated DB user for each environment
- Implement database methods in models
- Prefer prepared statements via the model to avoid SQL injection
Key Takeaways
This beginner guide covered practical steps you can use immediately when building back-end services with CodeIgniter 4:
- Use PHP 8.x (8.0 or 8.1 recommended) for performance and security benefits; CodeIgniter 4 requires PHP >= 7.2 but modern projects should standardize on 8.x.
- Follow the MVC pattern: keep models responsible for data, controllers for request handling, and views for presentation.
- Validate input server-side. For APIs, prefer controller-side validation for JSON and ensure consistent error responses.
- Hash passwords with
password_hash()and verify withpassword_verify(); protect auth endpoints with HTTPS and CSRF where applicable. - Use Docker Compose in development to match production environments and simplify CI pipelines; map service names (e.g.,
db) in.env. - Use migrations and version-controlled schema changes to keep environments reproducible.
- Test APIs with cURL, fetch, or tools like Postman; include automated tests in CI for regressions.
- Consult the official documentation at codeigniter.com and PHP references at php.net for authoritative guidance.
Frequently Asked Questions
- Which PHP version should I use with CodeIgniter 4?
- CodeIgniter 4 supports PHP 7.2+, but you should use PHP 8.0 or 8.1 in new projects for improved performance, better type support, and security fixes.
- Should I use CodeIgniter for building APIs?
- Yes. CodeIgniter's
ResourceControllerand JSON helpers make API development straightforward. For complex API needs you may combine CodeIgniter with middleware, token-based auth (JWT), and rate limiting via reverse proxies or application logic. - Where should I put validation rules: model or controller?
- Both are valid. Model-based validation is convenient for CRUD consistency, but controller-side validation is often preferable for JSON APIs where you need request-specific rules or conditional validation logic.
- Should I use sessions or JWTs for authentication?
- Use sessions for traditional web apps with server-rendered pages. For SPA or third-party API use, prefer short-lived JWTs or opaque tokens stored server-side (Redis) and rotated frequently. Always use HTTPS and secure cookie flags where applicable.
- How do I keep secrets out of source control?
- Store environment-specific credentials in
.env(excluded from VCS) and use secrets management in production (environment variables, Vault, or cloud provider secret stores). Never commit plaintext credentials. - How do I migrate database changes safely?
- Use CodeIgniter migrations (
php spark migrate) and include them in CI/CD pipelines. Review and test migrations locally in a clone of production data when possible, and back up production databases before applying changes.
Conclusion
CodeIgniter 4 is a pragmatic framework for developers who want a lightweight, fast foundation for web and API back ends. This guide has shown how to set up a reproducible environment with Docker, scaffold controllers and models, validate input safely, implement basic authentication, and connect to a MySQL database. From here, add automated tests, integrate CI/CD, and harden security settings for production.
Next steps: build a small CRUD API, add integration tests using PHPUnit, and practice deploying with a container registry and a cloud host. For authoritative documentation and further examples, visit codeigniter.com and php.net.