Skip to main content
POST
/
api
/
taskCategories
Create Category
curl --request POST \
  --url https://api.example.com/api/taskCategories \
  --header 'Content-Type: application/json' \
  --data '
{
  "name": "<string>"
}
'
{
  "_id": "<string>",
  "name": "<string>"
}

Endpoint

POST /api/taskCategories
Creates a new task category in the system. The API automatically generates a UUID for the category.

Request Body

name
string
required
The category name.Validation:
  • Minimum length: 2 characters
  • Maximum length: 25 characters
  • Automatically trimmed (whitespace removed)
  • Should be unique (recommended but not enforced)
Example: "Work", "Personal", "Shopping"

Response

Returns the newly created category object.
_id
string
Auto-generated unique identifier (UUID v4).
name
string
The category name from the request.

Status Codes

  • 201 Created - Category successfully created
  • 400 Bad Request - Validation failed (invalid or missing name)
  • 500 Internal Server Error - Server-side error occurred

Examples

curl -X POST http://localhost:3000/api/taskCategories \
  -H "Content-Type: application/json" \
  -d '{"name":"Work"}'

Request Examples

{
  "name": "Work"
}

Response Examples

{
  "_id": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
  "name": "Work"
}

Validation Rules

All validation is performed server-side using Zod schemas. Invalid requests will return detailed error messages.
FieldRequiredTypeMinMax
namestring225

Common Validation Errors

  • Name too short: Minimum 2 characters required
  • Name too long: Maximum 25 characters allowed
  • Missing name: The name field is required
  • Invalid type: Name must be a string

Usage Notes

Auto-Generated ID: Never include _id in your request body. The API automatically generates a UUID v4 for each new category.
Unique Names: While the API doesn’t enforce unique category names at the database level, it’s recommended to check for duplicates before creating a new category to avoid confusion.
Trimming: The API automatically trims whitespace from the beginning and end of the name, so " Work " becomes "Work".

Common Patterns

Create Category and Use Immediately

async function createTaskInNewCategory(categoryName, taskData) {
  // Create the category
  const categoryResponse = await fetch('http://localhost:3000/api/taskCategories', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: categoryName })
  });
  
  if (!categoryResponse.ok) {
    console.error('Failed to create category');
    return null;
  }
  
  const category = await categoryResponse.json();
  
  // Create task in that category
  const taskResponse = await fetch('http://localhost:3000/api/tasks', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      ...taskData,
      categoryId: category._id
    })
  });
  
  if (!taskResponse.ok) {
    console.error('Failed to create task');
    return null;
  }
  
  const task = await taskResponse.json();
  
  return { category, task };
}

// Usage
const result = await createTaskInNewCategory('Project X', {
  title: 'Initial planning',
  description: 'Define scope and timeline'
});

Bulk Create Categories

async function createMultipleCategories(categoryNames) {
  const results = await Promise.all(
    categoryNames.map(async (name) => {
      const response = await fetch('http://localhost:3000/api/taskCategories', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name })
      });
      
      if (response.status === 201) {
        return await response.json();
      }
      
      return null;
    })
  );
  
  const successful = results.filter(r => r !== null);
  console.log(`Created ${successful.length} of ${categoryNames.length} categories`);
  
  return successful;
}

// Usage
const categories = await createMultipleCategories([
  'Work',
  'Personal',
  'Shopping',
  'Health',
  'Finance'
]);

Create with Validation

function validateCategoryName(name) {
  if (!name || typeof name !== 'string') {
    return { valid: false, error: 'Name is required and must be a string' };
  }
  
  const trimmed = name.trim();
  
  if (trimmed.length < 2) {
    return { valid: false, error: 'Name must be at least 2 characters' };
  }
  
  if (trimmed.length > 25) {
    return { valid: false, error: 'Name must be at most 25 characters' };
  }
  
  return { valid: true, name: trimmed };
}

async function createCategoryWithValidation(name) {
  const validation = validateCategoryName(name);
  
  if (!validation.valid) {
    console.error('Validation error:', validation.error);
    return null;
  }
  
  const response = await fetch('http://localhost:3000/api/taskCategories', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: validation.name })
  });
  
  if (response.status === 201) {
    return await response.json();
  }
  
  return null;
}

Get or Create Category

async function getOrCreateCategory(name) {
  // Try to find existing category
  const response = await fetch('http://localhost:3000/api/taskCategories');
  const categories = await response.json();
  
  const existing = categories.find(
    cat => cat.name.toLowerCase() === name.toLowerCase()
  );
  
  if (existing) {
    return existing;
  }
  
  // Create new category
  const createResponse = await fetch('http://localhost:3000/api/taskCategories', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name })
  });
  
  if (createResponse.status === 201) {
    return await createResponse.json();
  }
  
  return null;
}

// Usage
const category = await getOrCreateCategory('Work');

Integration Examples

React Form Component

import { useState } from 'react';

function CreateCategoryForm({ onCategoryCreated }) {
  const [name, setName] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    
    try {
      const response = await fetch('http://localhost:3000/api/taskCategories', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name })
      });
      
      if (response.status === 400) {
        const errorData = await response.json();
        setError('Invalid category name');
        return;
      }
      
      if (!response.ok) {
        throw new Error('Failed to create category');
      }
      
      const newCategory = await response.json();
      setName(''); // Clear form
      onCategoryCreated(newCategory);
      
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Category name"
        minLength={2}
        maxLength={25}
        required
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Creating...' : 'Create Category'}
      </button>
      {error && <div style={{color: 'red'}}>{error}</div>}
    </form>
  );
}

Best Practices

Check for Duplicates: Before creating a category, fetch all categories and check if one with the same name already exists to avoid duplicates.
Client-Side Validation: Implement client-side validation to provide immediate feedback to users before making the API call.
Handle All Status Codes: Always handle 201 (success), 400 (validation error), and 500 (server error) status codes appropriately.
Reserved Names: Avoid creating a category named “uncategorized” as this is the special default value used by the system.

Build docs developers (and LLMs) love