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:
- User logs in through
/api/auth/login - Frontend stores the returned token
- Frontend requests
/api/auth/meto get current user info - Frontend loads
/api/organizations - Organization representative submits a form to
/api/activities - Backend checks whether that user is the representative of the chosen organization
- If allowed, the activity is inserted into the database and returned as JSON
