Skip to Content
Lesson 3

Designing Flexible APIs

Workplace Context

Your proof-of-concept was a success, and the team is now able to communicate between the frontend and backend in a local development environment. However, looking ahead, your tech lead wants to ensure the API you build is not just functional but also scalable and maintainable. They’ve assigned you the task of establishing a set of best practices for API design. Your goal is to define a clear, consistent, and predictable structure for all future endpoints so that any client — whether it’s the company’s React app, a mobile app, or a partner’s integration — can easily understand and consume your API.


Lesson Objectives

  • Apply RESTful principles to design clear, predictable, and scalable APIs.
  • Implement an API versioning strategy to allow for future changes without breaking existing clients.
  • Design consistent shapes for API requests and responses, including a standardized error object.
  • Briefly describe GraphQL as a popular alternative to REST and understand its core concepts.

The Principles of REST

REST (REpresentational State Transfer) is an architectural style for designing networked applications. It’s not a strict protocol but a set of constraints that, when applied, lead to a system that is performant, scalable, and easy to work with. A well-designed RESTful API feels intuitive to use.

The core principles are:

  1. Client-Server Architecture: The client (e.g., React app) and the server (e.g., Express API) are separate. This separation of concerns allows them to evolve independently.
  2. Statelessness: Each request from a client to the server must contain all the information needed to understand and complete the request. The server does not store any client session state between requests. This is why we send tokens or session cookies with every authenticated request.
  3. Cacheability: Responses from the server should be defined as cacheable or not. This allows clients or intermediate proxies to cache responses, improving performance and scalability.
  4. Uniform Interface: This is the most critical principle for API design. It simplifies and decouples the architecture, which allows each part of the system to evolve independently. It has four sub-constraints:
    • Resource-Based: APIs are designed around resources (e.g., users, products, posts). You interact with these resources using standard HTTP methods.
    • Manipulation of Resources Through Representations: The client holds a representation of a resource (e.g., a JSON object). When it wants to update that resource, it sends this representation to the server.
    • Self-Descriptive Messages: Each request should be self-contained and describe how to process it. For example, using the Content-Type header to specify that the body is JSON (application/json).
    • Hypermedia as the Engine of Application State (HATEOAS): The API response should include links to other related actions or resources. For example, a response for a user might include a link to get all of that user’s posts.

Resource Naming Conventions

  • Use nouns, not verbs (e.g., /products, not /getProducts).
  • Use plural nouns for collections (e.g., /users).
  • Use kebab-case for long resource names (e.g., /product-reviews).
  • Use the resource ID in the path for specific items (e.g., /users/123).

Standard HTTP Methods

MethodActionExample Request
GETRetrieve a resource or a collectionGET /api/users
POSTCreate a new resourcePOST /api/users
PUTReplace/update an existing resourcePUT /api/users/123
PATCHPartially update an existing resourcePATCH /api/users/123
DELETEDelete an existing resourceDELETE /api/users/123

API Versioning

As your application grows, you will inevitably need to make breaking changes to your API. You might need to rename a field, change a data type, or remove an endpoint entirely. To do this without breaking existing client applications, you should version your API.

The most common and straightforward approach is URI path versioning. You simply include the version number in the URL path.

  • https://api.example.com/v1/products
  • https://api.example.com/v2/products

This is clear, explicit, and easy to implement in your Express router:

// server.js const v1ProductRoutes = require('./routes/v1/products'); const v2ProductRoutes = require('./routes/v2/products'); app.use('/api/v1/products', v1ProductRoutes); app.use('/api/v2/products', v2ProductRoutes);

When you introduce v2, you can maintain v1 for a period of time, giving client developers a chance to migrate.


Consistent Request and Response Shapes

A predictable API is an easy-to-use API. You should establish a consistent structure for the data you send and receive.

Standardized Error Responses

Don’t just send a 404 status with the text “Not Found”. Provide a consistent JSON error object that clients can reliably parse.

// A good error response { "status": "error", "error": { "message": "The requested product could not be found.", "code": "RESOURCE_NOT_FOUND" } }

You can create a custom error handling middleware in Express to ensure all errors are formatted this way.

Consistent Success Responses

Similarly, for successful requests, you might want to wrap your data in a consistent structure.

// GET /api/v1/users { "status": "success", "data": [ { "id": 1, "name": "Alice" }, { "id": 2, "name": "Bob" } ] }

While not as critical as error shapes, this can help clients differentiate between the metadata of the response (status) and the actual data payload.


Introduction to GraphQL

GraphQL is a query language for your API and a server-side runtime for executing those queries. It was developed by Facebook and is now a popular alternative to REST.

Unlike REST, which has many endpoints for different resources, a GraphQL API typically has a single endpoint. The client sends a “query” to this endpoint that specifies exactly what data it needs.

Key Differences from REST:

FeatureRESTGraphQL
EndpointsMultiple endpoints (/users, /posts)Typically a single endpoint (/graphql)
Data FetchingServer defines the data shape; often leads to over/under-fetching.Client specifies the exact data it needs; no over-fetching.
Request StructureUses HTTP methods (GET, POST, etc.) and URL paths.Uses a custom query language (sent via a POST request).
Strongly TypedNo built-in type system (requires external tools like OpenAPI).Has a built-in, strongly-typed schema that defines the API.

Example GraphQL Query:

A client can ask for a user and their posts in a single request, and only get the fields they care about:

query { user(id: "1") { id name posts { title } } }

This query would return a JSON object with the exact same shape. This solves the problem of over-fetching (getting fields you don’t need) and under-fetching (having to make multiple requests to get all the data you need).


Activities

Activity 1: API Design Workshop

Given the requirements below, design a complete RESTful API. Define all endpoints, HTTP methods, URL paths, and expected request/response bodies.

Scenario: You are designing the API for a project management application called “TaskFlow.” The application needs to support the following features:

  1. Users can create, view, update, and delete projects
  2. Each project has a name, description, status (active/archived), and owner
  3. Projects contain multiple tasks
  4. Tasks have a title, description, priority (low/medium/high), status (todo/in-progress/done), due date, and assignee
  5. Users can add comments to tasks
  6. Comments have content, author, and timestamp
  7. Users can filter tasks by status, priority, and assignee
  8. Users can search projects by name

Design the endpoints for all CRUD operations and any additional query endpoints. Consider:

  • Resource naming conventions
  • Proper HTTP methods
  • URL structure for nested resources
  • Query parameters for filtering/searching

Activity 2: AI-Assisted API Design

Translating product requirements into RESTful API designs is a practical AI use case in professional development. AI tools can rapidly scaffold endpoint structures, suggest naming conventions, and generate request/response shapes. This allows developers to focus on edge cases, validation rules, and consistency decisions.

If you haven't already done so, you should install an AI assistant.

We recommend Claude for VS Code , but you can use any AI assistant you prefer.

Scenario: The product team has provided detailed requirements for a new E-Learning Platform API. Your task is to design a comprehensive RESTful API that any client (web, mobile, third-party) can consume.

E-Learning Platform Requirements:

COURSE MANAGEMENT - Instructors can create, update, and delete courses - Courses have: title, description, category, difficulty level (beginner/intermediate/advanced), price, thumbnail image URL, published status (draft/published/archived) - Courses contain ordered modules (sections) - Modules contain ordered lessons - Lessons have: title, content type (video/article/quiz), duration, content URL ENROLLMENT - Students can enroll in courses (free or paid) - Students can view their enrolled courses - Track student progress per course (percentage complete) - Students can mark lessons as complete USER MANAGEMENT - Users have roles: student, instructor, admin - Users have: name, email, bio, avatar URL, created date - Instructors have additional fields: expertise areas, rating REVIEWS AND RATINGS - Students can review courses they're enrolled in (1-5 stars + text) - One review per student per course - Reviews can be updated or deleted by the author - Get average rating for a course SEARCH AND DISCOVERY - Search courses by title or description - Filter courses by category, difficulty, price range - Get featured/popular courses - Get courses by instructor PROGRESS TRACKING - Get all lessons completed by a student in a course - Get overall progress percentage for a course - Get student's learning history (recently accessed courses)

Part 1: Manual API Design (20 min)

Design the RESTful API yourself. For each resource, define:

  1. All CRUD endpoints with HTTP methods and paths
  2. Query endpoints with filter/search parameters
  3. Request body structure (for POST/PUT/PATCH)
  4. Response body structure
  5. Appropriate HTTP status codes

Use this template for your design:

RESOURCE: Courses ───────────────── GET /api/v1/courses - List all courses (with filters) Query params: ?category=&difficulty=&minPrice=&maxPrice=&search= Response: { status: "success", data: [...courses], meta: { total, page, limit } } GET /api/v1/courses/:id - Get single course with modules/lessons POST /api/v1/courses - Create course (instructor only) PUT /api/v1/courses/:id - Update course DELETE /api/v1/courses/:id - Delete course [Continue for other resources...]

Consider these design decisions:

  • How do you handle nested resources (modules within courses, lessons within modules)?
  • Where do you put enrollment endpoints? /courses/:id/enroll or /enrollments?
  • How do you structure progress tracking endpoints?

Part 2: AI-Assisted Design (15 min)

Use an AI assistant to generate the API design from the same requirements.

Prompt the AI with:

“Design a RESTful API for an e-learning platform with these requirements: [paste requirements above]. Include all CRUD operations, proper HTTP methods, URL paths following REST conventions, nested resource handling, query parameters for filtering/searching, and consistent request/response JSON shapes. Use API versioning. Include standard error response format.”

Part 3: Compare and Evaluate (10 min)

Compare your manual design with the AI-generated version.

Questions to Consider:

  1. Did the AI use /courses/:courseId/modules/:moduleId/lessons or a flatter structure? Which is better for this use case?
  2. How did the AI handle the “mark lesson complete” action? As a PUT to the lesson or a POST to a progress resource?
  3. Did the AI include pagination for list endpoints?
  4. How did the AI structure the reviews endpoints? Did it enforce “one review per student per course”?
  5. Did the AI suggest any endpoints you didn’t think of?

Design Patterns to Discuss:

// Nested resources - Two common approaches: // Approach 1: Deeply nested (explicit hierarchy) GET /api/v1/courses/:courseId/modules/:moduleId/lessons/:lessonId // Approach 2: Flatter with query params (simpler URLs) GET /api/v1/lessons/:lessonId GET /api/v1/lessons?courseId=123&moduleId=456 // Action endpoints - Two common approaches: // Approach 1: Verb in URL (less RESTful but clear) POST /api/v1/courses/:id/enroll POST /api/v1/lessons/:id/complete // Approach 2: Pure REST (resource-based) POST /api/v1/enrollments { courseId: 123 } POST /api/v1/progress { lessonId: 456, completed: true }
Important

AI tools are excellent at rapidly scaffolding API designs from requirements. They consistently apply REST conventions and generate comprehensive endpoint lists. However, API design involves trade-offs that depend on your specific use case, like how clients will consume the API, query patterns, and team preferences. Use AI-generated designs as a starting point, then refine based on your application’s unique needs and the discussions with your team.


Summary

In this lesson, you learned the principles of designing robust and developer-friendly APIs. We covered the core constraints of REST, including resource-based URLs and the proper use of HTTP methods. You learned the importance of versioning your API to support future changes and the value of designing consistent JSON shapes for both successful responses and errors. Finally, you were introduced to GraphQL as a powerful alternative to REST that gives clients the power to request exactly the data they need, solving common issues like over- and under-fetching.


References

Additional Resources