Example API Design: School Activities App MVP

Overview

This document presents an example API design for the School Activities App MVP. The goal is to show how a simple web application backend can be organized around clear resources and predictable endpoints.

This API is based on the simplified data model with three main tables:

  • Users
  • Organizations
  • Activities

In this MVP:

  • users can log in
  • each organization has one representative user
  • only the representative of an organization can create activities for that organization
  • each activity stores its own event information directly

This example assumes a REST-style API built with Node and Express and backed by MariaDB.

Design Goals

A good API design should:

  • use clear and consistent URLs
  • separate resources cleanly
  • use standard HTTP methods
  • return JSON
  • enforce permissions in the backend
  • be simple enough for the frontend to use without confusion

Main Resources

This API is organized around three main resources:

  • users
  • organizations
  • activities

It also includes authentication routes.

Base URL

Example base URL:

/api

So a full endpoint might look like:

/api/organizations

Authentication Endpoints

POST /api/auth/register

Purpose: Create a new user account.

Request body:

{
"first_name": "Jane",
"last_name": "Smith",
"email": "smithj@kenyon.edu",
"password": "mypassword123",
"role": "org_rep"
}

Successful response:

{
"message": "User registered successfully",
"user": {
"id": 12,
"first_name": "Jane",
"last_name": "Smith",
"email": "smithj@kenyon.edu",
"role": "org_rep"
}
}

Notes:

  • Passwords should never be stored directly.
  • Store a password hash in the database.

POST /api/auth/login

Purpose: Log a user in.

Request body:

{
"email": "smithj@kenyon.edu",
"password": "mypassword123"
}

Successful response:

{
"message": "Login successful",
"token": "example_jwt_token_here",
"user": {
"id": 12,
"first_name": "Jane",
"last_name": "Smith",
"email": "smithj@kenyon.edu",
"role": "org_rep"
}
}

Notes:

  • In many systems this token would be a JWT.
  • The frontend stores the token and sends it with future requests.

GET /api/auth/me

Purpose: Return the currently logged-in user.

Headers:

  • Authorization: Bearer token

Successful response:

{
"user": {
"id": 12,
"first_name": "Jane",
"last_name": "Smith",
"email": "smithj@kenyon.edu",
"role": "org_rep"
}
}

User Endpoints

GET /api/users

Purpose: Return a list of users.

Notes:

  • In a real system, this might be admin-only.

Successful response:

[
{
"id": 1,
"first_name": "Jim",
"last_name": "Skon",
"email": "skonj@kenyon.edu",
"role": "admin"
},
{
"id": 12,
"first_name": "Jane",
"last_name": "Smith",
"email": "smithj@kenyon.edu",
"role": "org_rep"
}
]

GET /api/users/:id

Purpose: Return one user by ID.

Successful response:

{
"id": 12,
"first_name": "Jane",
"last_name": "Smith",
"email": "smithj@kenyon.edu",
"role": "org_rep",
"is_active": true
}

PUT /api/users/:id

Purpose: Update a user.

Request body:

{
"first_name": "Jane",
"last_name": "Smith",
"role": "student"
}

Successful response:

{
"message": "User updated successfully"
}

Notes:

  • A user may be allowed to edit their own profile.
  • Changing roles should usually be restricted to admins.

Organization Endpoints

GET /api/organizations

Purpose: Return all organizations.

Successful response:

[
{
"id": 3,
"name": "Chess Club",
"description": "Student club for chess players",
"rep_user_id": 12
},
{
"id": 4,
"name": "Robotics Society",
"description": "Engineering and robotics projects",
"rep_user_id": 15
}
]

GET /api/organizations/:id

Purpose: Return one organization by ID.

Successful response:

{
"id": 3,
"name": "Chess Club",
"description": "Student club for chess players",
"rep_user_id": 12
}

POST /api/organizations

Purpose: Create a new organization.

Request body:

{
"name": "Chess Club",
"description": "Student club for chess players",
"rep_user_id": 12
}

Successful response:

{
"message": "Organization created successfully",
"organization": {
"id": 3,
"name": "Chess Club",
"description": "Student club for chess players",
"rep_user_id": 12
}
}

Notes:

  • This might be limited to admins.
  • Alternatively, a user might propose an organization and an admin approves it later.

PUT /api/organizations/:id

Purpose: Update an organization.

Request body:

{
"name": "Chess and Strategy Club",
"description": "Student club for chess and strategy games",
"rep_user_id": 12
}

Successful response:

{
"message": "Organization updated successfully"
}

Notes:

  • Only the current representative or an admin should be allowed to update the organization.

DELETE /api/organizations/:id

Purpose: Delete an organization.

Successful response:

{
"message": "Organization deleted successfully"
}

Notes:

  • This should usually be admin-only.

Activity Endpoints

GET /api/activities

Purpose: Return all activities.

Successful response:

[
{
"id": 20,
"organization_id": 3,
"title": "Spring Chess Tournament",
"description": "Single-elimination tournament for students",
"event_date": "2026-04-15",
"start_time": "18:00:00",
"end_time": "21:00:00",
"location": "Higley Hall",
"status": "proposed",
"created_by_user_id": 12
}
]

GET /api/activities/:id

Purpose: Return one activity by ID.

Successful response:

{
"id": 20,
"organization_id": 3,
"title": "Spring Chess Tournament",
"description": "Single-elimination tournament for students",
"event_date": "2026-04-15",
"start_time": "18:00:00",
"end_time": "21:00:00",
"location": "Higley Hall",
"status": "proposed",
"created_by_user_id": 12
}

GET /api/organizations/:id/activities

Purpose: Return all activities for one organization.

Successful response:

[
{
"id": 20,
"organization_id": 3,
"title": "Spring Chess Tournament",
"event_date": "2026-04-15",
"location": "Higley Hall",
"status": "proposed"
},
{
"id": 21,
"organization_id": 3,
"title": "Weekly Chess Meetup",
"event_date": "2026-04-22",
"location": "Ascension Lounge",
"status": "scheduled"
}
]

POST /api/activities

Purpose: Create a new activity.

Request body:

{
"organization_id": 3,
"title": "Spring Chess Tournament",
"description": "Single-elimination tournament for students",
"event_date": "2026-04-15",
"start_time": "18:00:00",
"end_time": "21:00:00",
"location": "Higley Hall",
"status": "proposed"
}

Successful response:

{
"message": "Activity created successfully",
"activity": {
"id": 20,
"organization_id": 3,
"title": "Spring Chess Tournament",
"description": "Single-elimination tournament for students",
"event_date": "2026-04-15",
"start_time": "18:00:00",
"end_time": "21:00:00",
"location": "Higley Hall",
"status": "proposed",
"created_by_user_id": 12
}
}

Important business rule:

  • The backend must verify that the logged-in user is the representative of the specified organization before allowing creation.

PUT /api/activities/:id

Purpose: Update an activity.

Request body:

{
"title": "Spring Chess Open",
"location": "Peirce Hall",
"status": "scheduled"
}

Successful response:

{
"message": "Activity updated successfully"
}

Notes:

  • Only the representative for the linked organization or an admin should be able to update it.

DELETE /api/activities/:id

Purpose: Delete an activity.

Successful response:

{
"message": "Activity deleted successfully"
}

Notes:

  • Again, permissions matter.
  • Deleting records permanently may not always be the best design; some teams may prefer soft delete or status changes.

Suggested HTTP Methods

Use standard REST conventions:

  • GET for reading data
  • POST for creating data
  • PUT for replacing or updating data
  • DELETE for deleting data

Suggested Status Codes

Examples:

  • 200 OK for successful reads and updates
  • 201 Created for successful creation
  • 400 Bad Request for invalid input
  • 401 Unauthorized when not logged in
  • 403 Forbidden when logged in but not allowed
  • 404 Not Found when the resource does not exist
  • 500 Internal Server Error for unexpected backend failures

Example Error Responses

Invalid login

{
"error": "Invalid email or password"
}

Unauthorized activity creation

{
"error": "Only the representative of this organization can create activities"
}

Missing required field

{
"error": "Title is required"
}

Permissions Summary

Student

  • can register and log in
  • can view organizations
  • can view activities

Organization Representative

  • can do everything a student can do
  • can update their organization if allowed
  • can create activities for their organization
  • can update activities for their organization

Admin

  • can manage users
  • can manage organizations
  • can manage activities
  • can change roles if needed

Example Frontend-to-Backend Flow

A typical flow might be:

  1. User logs in through /api/auth/login
  2. Frontend stores the returned token
  3. Frontend requests /api/auth/me to get current user info
  4. Frontend loads /api/organizations
  5. Organization representative submits a form to /api/activities
  6. Backend checks whether that user is the representative of the chosen organization
  7. If allowed, the activity is inserted into the database and returned as JSON
Scroll to Top