Skip to main content
RESTful Principles
CHAPTER 21 Beginner

Connecting REST APIs with MySQL

Updated: May 13, 2026
5 min read

# CHAPTER 21

Connecting REST APIs with MySQL

1. Introduction

An API without a database is just a calculator. To build real applications, you need persistent storage. In Chapter 21, we will integrate MySQL into the PHP routing engine we built in the previous chapter. We will use PHP Data Objects (PDO) to securely connect to the database, execute queries, and format the SQL results directly into JSON arrays.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Establish a secure PDO connection to a MySQL database.
  • Use PDO Prepared Statements to prevent SQL injection.
  • Fetch database records and output them as JSON arrays.
  • Insert new records from a JSON API payload into MySQL.
  • Handle database connection errors gracefully without leaking credentials.

3. Beginner-Friendly Explanation

Think of PDO as a highly trained translator. PHP speaks one language, and MySQL speaks another. If PHP tries to talk to MySQL directly using raw strings, it is dangerous and prone to misunderstandings (SQL injection). PDO sits between them. You hand PDO your data securely, and PDO translates it into a perfectly safe, optimized command that MySQL understands. Furthermore, when MySQL replies with a table of data, PDO neatly packages it into a PHP array, which you can then instantly convert into JSON.

4. Real-World Examples

  • Fetching Users: GET /users tells PHP to ask PDO to run SELECT * FROM users. PDO returns an array of users, and PHP json_encodes it back to the client.
  • Creating a Post: POST /posts takes the JSON payload, gives the variables to PDO, and PDO runs INSERT INTO posts...

5. Detailed Code Examples

Let's look at a complete, secure database connection and a basic GET/POST implementation.

1. The Database Connection (db.php):

php
1234567891011121314151617181920212223
<?php
$host = &#039;127.0.0.1';
$db   = &#039;api_database';
$user = &#039;root';
$pass = &#039;';
$charset = &#039;utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Fetch as associative arrays!
    PDO::ATTR_EMULATE_PREPARES   => false,
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
    // DO NOT output $e->getMessage() in production! It leaks DB passwords.
    http_response_code(500);
    echo json_encode(["error" => "Database connection failed."]);
    exit;
}
?>

2. Integrating PDO into the API:

php
123456789101112131415161718192021222324252627282930
<?php
require &#039;db.php'; // Include the PDO connection

// Assume $method and $uri are set up like Chapter 20

if ($method === &#039;GET' && $uri[2] === 'users') {
    // GET /users
    $stmt = $pdo->query(&#039;SELECT id, name, email FROM users');
    $users = $stmt->fetchAll(); // PDO::FETCH_ASSOC is default now
    
    echo json_encode(["data" => $users]);
} 

elseif ($method === &#039;POST' && $uri[2] === 'users') {
    // POST /users
    $input = json_decode(file_get_contents(&#039;php://input'), true);
    
    // Always use Prepared Statements!
    $sql = "INSERT INTO users (name, email) VALUES (:name, :email)";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        &#039;:name' => $input['name'],
        &#039;:email' => $input['email']
    ]);
    
    $newId = $pdo->lastInsertId();
    http_response_code(201);
    echo json_encode(["message" => "User created", "id" => $newId]);
}
?>

6. Request/Response Examples

GET /users Response: Notice how the database columns map perfectly to the JSON keys because we used PDO::FETCH_ASSOC.
json
1234567891011121314
{
  "data": [
    {
      "id": 1,
      "name": "Alice",
      "email": "alice@example.com"
    },
    {
      "id": 2,
      "name": "Bob",
      "email": "bob@example.com"
    }
  ]
}

7. HTTP Examples

The API now fully connects the HTTP method to the database logic:
  • GET triggers $pdo->query() or $stmt->fetchAll().
  • POST triggers $pdo->prepare(INSERT...) and $pdo->lastInsertId().
  • DELETE triggers $pdo->prepare(DELETE...) and $stmt->rowCount() to see if a row was actually deleted.

8. JSON Examples

When updating data, you check if the database actually changed anything.
php
12345678910
// Inside PUT /users/5
$stmt = $pdo->prepare("UPDATE users SET name = :name WHERE id = :id");
$stmt->execute([&#039;:name' => $input['name'], ':id' => $userId]);

if ($stmt->rowCount() > 0) {
    echo json_encode(["success" => true, "message" => "User updated"]);
} else {
    // If rowCount is 0, the ID didn't exist OR the new name was the same as the old one
    echo json_encode(["success" => false, "message" => "No changes made"]);
}

9. Best Practices

  • PDO::FETCHASSOC: Always set this as your default fetch mode. It forces PDO to return arrays with column names as keys (['name' => 'Alice']), which jsonencode perfectly translates into JSON objects.
  • Never expose passwords: When selecting data (e.g., SELECT * FROM users), explicitly name the columns you want (SELECT id, name, email). If you use *, you might accidentally jsonencode the user's hashed password and send it to the frontend!

10. Common Mistakes

  • mysqli vs PDO: Beginners often use the older mysqliquery functions. PDO is highly preferred for APIs because it allows named parameters (:name) and returns clean arrays perfectly suited for JSON.
  • Leaking DB Errors: If a SQL query fails (e.g., a duplicate email), PDO throws an exception. If you don't catch it, PHP outputs an HTML error. You must wrap your execute() in try...catch blocks to return a clean JSON 500 or 400 error.

11. Mini Exercises

  1. 1. Look at this bad code: $pdo->query("SELECT * FROM users WHERE name = '" . $GET['name'] . "'");. Why is this incredibly dangerous?
*(Answer: It is vulnerable to SQL Injection. A hacker could pass '; DROP TABLE users;-- in the URL and delete the database).*

12. Coding Challenges

Challenge 1: Write the PDO logic for DELETE /users/5. Prepare the statement, bind the ID, execute it, and return a JSON message confirming deletion.

13. MCQs with Answers

Question 1

What PHP extension is the industry standard for connecting to databases in modern APIs?

Question 2

Why is PDO::FETCHASSOC the best fetch mode for REST APIs?

Question 3

How do you safely insert API JSON data into MySQL?

14. Interview Questions

  • Q: Explain why Prepared Statements are mandatory when building APIs that accept user data.
  • Q: If a PDO connection fails, why is it a security risk to output the raw PDOException message to the API client?
  • Q: Describe how you handle the output of PDO::lastInsertId() in a REST API POST response.

15. FAQs

Q: Can I use an ORM like Eloquent or Doctrine instead of raw PDO? A: Absolutely! ORMs (Object-Relational Mappers) sit on top of PDO and make querying much easier (e.g., User::find(5)). For learning purposes, raw PDO is essential, but in production, ORMs are heavily utilized.

16. Summary

In Chapter 21, we brought our API to life by connecting it to MySQL using PDO. We learned how to configure PDO to return associative arrays for perfect JSON encoding, how to safely insert JSON payloads into the database using prepared statements to prevent SQL injection, and how to structure our queries securely.

17. Next Chapter Recommendation

We can now handle text and numbers perfectly. But what if a user wants to upload a profile picture? Proceed to Chapter 22: File Upload APIs to learn how to handle multipart/form-data and securely store files on your server.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·