Trongate Way Docs

Creating Records

The create() endpoint accepts a JSON payload, validates it, inserts a new record, and returns a 201 Created response. This is the first time we need to read a JSON request body.

Reading JSON Input

HTML forms send data as application/x-www-form-urlencoded, which PHP reads via $_POST or the post() helper. But API clients send data as application/json, which arrives as a raw request body accessed via php://input.

Our controller includes a helper method to parse JSON input:

PHP
public function get_post_data(): array|null {
    block_url('countries_api/get_post_data');

    $raw_input = file_get_contents('php://input');
    $data = json_decode($raw_input, true);

    if (!is_array($data) || empty($data)) {
        return null;
    }

    return $data;
}

This method reads the raw request body, decodes it from JSON, and returns an associative array. If the JSON is invalid or empty, it returns null, which the controller interprets as a 400 Bad Request.

Validation

The validation helper checks the required fields and returns an array of error messages:

PHP
public function validate(array $data): array {
    block_url('countries_api/validate');

    $errors = [];

    if (!isset($data['country_title'])
        || trim($data['country_title']) === '') {
        $errors['country_title'] = 'Country name is required.';
    } elseif (strlen(trim($data['country_title'])) < 2) {
        $errors['country_title']
            = 'Country name must be at least 2 characters.';
    } elseif (strlen(trim($data['country_title'])) > 100) {
        $errors['country_title']
            = 'Country name must not exceed 100 characters.';
    }

    if (!isset($data['country_code'])
        || trim($data['country_code']) === '') {
        $errors['country_code'] = 'Country code is required.';
    } elseif (strlen(trim($data['country_code'])) !== 2) {
        $errors['country_code']
            = 'Country code must be exactly 2 characters.';
    }

    return $errors;
}

Each field error is keyed by the field name, making it easy for the client to display errors next to the correct input.

The Controller Method

PHP
public function create(): void {
    $this->authenticate();

    $data = $this->get_post_data();

    if ($data === null) {
        http_response_code(400);
        echo json_encode([
            'error' => 'Invalid or missing JSON payload.'
        ]);
        return;
    }

    $validation_errors = $this->validate($data);

    if (!empty($validation_errors)) {
        http_response_code(400);
        echo json_encode([
            'error' => 'Validation failed.',
            'fields' => $validation_errors
        ]);
        return;
    }

    $new_id = $this->model->insert($data);

    http_response_code(201);
    echo json_encode([
        'message' => 'Country created successfully.',
        'id' => $new_id,
        'country' => $data
    ]);

    $this->log_request('create');
}

The 201 Created Status

Note the use of http_response_code(201). The 201 status code specifically means "Created" - it tells the client that a new resource was successfully created as a result of their request. This is more precise than 200 (OK) and is a REST best practice.

The Model Insert Method

PHP
public function insert(array $data): int {
    $insert_data = [
        'country_title' => trim($data['country_title']),
        'country_code' => strtoupper(
            trim($data['country_code'])
        )
    ];

    return $this->db->insert($insert_data, 'countries');
}

The model method cleans the data before inserting: it trims whitespace and uppercases the country code for consistent formatting. The $this->db->insert() method returns the ID of the newly inserted record.

Testing

BASH
# Successful creation
curl -X POST \
  -H "Trongatetoken: your-token" \
  -H "Content-Type: application/json" \
  -d '{"country_title":"New Country","country_code":"NC"}' \
  http://localhost/countries_api/create

# Missing required field
curl -X POST \
  -H "Trongatetoken: your-token" \
  -H "Content-Type: application/json" \
  -d '{"country_title":"Incomplete Country"}' \
  http://localhost/countries_api/create

# Invalid JSON
curl -X POST \
  -H "Trongatetoken: your-token" \
  -H "Content-Type: application/json" \
  -d 'not valid json' \
  http://localhost/countries_api/create

Each of these test cases exercises a different response path: 201 for success, 400 with field errors for validation failure, and 400 for invalid JSON.

We're continually improving the Trongate documentation. If anything is incorrect, unclear, incomplete, or could be better, we'd genuinely appreciate your input.

Share your thoughts in the Documentation Feedback.

Leave Feedback About This Page