API Design with OpenAPI
Master the creation of robust, well-documented APIs using OpenAPI specifications and AI-assisted design. Learn to design APIs that are developer-friendly, maintainable, and production-ready.
Learning Objectives
By the end of this module, you will be able to:
✅ Design RESTful APIs - Create well-structured REST endpoints following best practices
✅ Write OpenAPI Specifications - Document APIs comprehensively using OpenAPI 3.0
✅ Implement API Versioning - Plan for API evolution and backward compatibility
✅ Design for Performance - Optimize API design for scale and efficiency
✅ Ensure API Security - Implement authentication, authorization, and security best practices
API Design Process
RESTful API Design Principles
Resource-Based Design
Design your API around resources, not actions:
# ❌ Action-based (RPC-style) - Avoid
POST /api/getUserProfile
POST /api/updateUserEmail
POST /api/deleteUser
# ✅ Resource-based (RESTful) - Preferred
GET /api/users/{id}/profile
PUT /api/users/{id}/email
DELETE /api/users/{id}
HTTP Methods and Status Codes
Use HTTP methods appropriately:
# User Management API Examples
# Create a new user
POST /api/users
# Returns: 201 Created with user data
# Get user by ID
GET /api/users/{id}
# Returns: 200 OK with user data or 404 Not Found
# Update user (full replacement)
PUT /api/users/{id}
# Returns: 200 OK with updated data or 404 Not Found
# Partial update
PATCH /api/users/{id}
# Returns: 200 OK with updated data or 404 Not Found
# Delete user
DELETE /api/users/{id}
# Returns: 204 No Content or 404 Not Found
# List users with pagination
GET /api/users?page=1&limit=20&sort=created_at
# Returns: 200 OK with paginated list
URL Structure Standards
Follow consistent URL patterns:
# Collection and Resource Pattern
/api/{version}/{collection} # GET, POST
/api/{version}/{collection}/{id} # GET, PUT, PATCH, DELETE
/api/{version}/{collection}/{id}/{sub-collection} # Nested resources
# Examples
GET /api/v1/users # List users
POST /api/v1/users # Create user
GET /api/v1/users/123 # Get specific user
PUT /api/v1/users/123 # Update user
GET /api/v1/users/123/orders # User's orders
POST /api/v1/users/123/orders # Create order for user
OpenAPI Specification Example
Here's a comprehensive example for a user management API:
openapi: 3.0.3
info:
title: Digital Library User Management API
description: |
RESTful API for managing library users, authentication, and profiles.
This API supports the Digital Library Management System and provides
secure endpoints for user registration, authentication, and profile management.
version: 1.0.0
contact:
name: API Support
email: api-support@library.com
url: https://docs.library.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.library.com/v1
description: Production server
- url: https://staging-api.library.com/v1
description: Staging server
- url: http://localhost:3000/v1
description: Development server
paths:
/users:
get:
summary: List library users
description: |
Retrieve a paginated list of library users. Requires admin privileges.
tags:
- Users
security:
- BearerAuth: []
parameters:
- name: page
in: query
description: Page number (1-based)
required: false
schema:
type: integer
minimum: 1
default: 1
- name: limit
in: query
description: Number of users per page
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: search
in: query
description: Search term for name or email
required: false
schema:
type: string
maxLength: 100
- name: status
in: query
description: Filter by user status
required: false
schema:
type: string
enum: [active, inactive, suspended]
responses:
'200':
description: Successfully retrieved users
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
examples:
success:
summary: Successful response
value:
data:
- id: "user_123"
email: "john.doe@email.com"
firstName: "John"
lastName: "Doe"
status: "active"
membershipType: "adult"
registrationDate: "2024-01-15T10:30:00Z"
pagination:
page: 1
limit: 20
total: 150
totalPages: 8
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
$ref: '#/components/responses/ForbiddenError'
'500':
$ref: '#/components/responses/InternalServerError'
post:
summary: Create new user
description: |
Register a new library user. Email must be unique.
Password requirements: minimum 8 characters, at least one uppercase,
one lowercase, one number, and one special character.
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
examples:
adult_user:
summary: Adult user registration
value:
email: "jane.smith@email.com"
password: "SecurePass123!"
firstName: "Jane"
lastName: "Smith"
dateOfBirth: "1990-05-15"
address:
street: "123 Main St"
city: "Springfield"
state: "IL"
zipCode: "62701"
phone: "+1-555-0123"
membershipType: "adult"
minor_user:
summary: Minor user registration
value:
email: "parent@email.com"
password: "ParentPass456!"
firstName: "Tommy"
lastName: "Johnson"
dateOfBirth: "2010-03-20"
membershipType: "minor"
parentGuardian:
name: "Sarah Johnson"
email: "parent@email.com"
phone: "+1-555-0124"
responses:
'201':
description: User created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/UserResponse'
'400':
$ref: '#/components/responses/ValidationError'
'409':
description: Email already exists
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "EMAIL_EXISTS"
message: "A user with this email already exists"
details:
field: "email"
value: "jane.smith@email.com"
'500':
$ref: '#/components/responses/InternalServerError'
/users/{userId}:
get:
summary: Get user by ID
description: Retrieve detailed information about a specific user
tags:
- Users
security:
- BearerAuth: []
parameters:
- name: userId
in: path
required: true
description: Unique user identifier
schema:
type: string
pattern: '^user_[a-zA-Z0-9]+$'
example: "user_123"
responses:
'200':
description: User retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/UserResponse'
'404':
$ref: '#/components/responses/NotFoundError'
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
put:
summary: Update user
description: |
Update user information. Requires admin privileges or self-update permission.
tags:
- Users
security:
- BearerAuth: []
parameters:
- name: userId
in: path
required: true
schema:
type: string
pattern: '^user_[a-zA-Z0-9]+$'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
responses:
'200':
description: User updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/UserResponse'
'400':
$ref: '#/components/responses/ValidationError'
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
$ref: '#/components/responses/ForbiddenError'
'404':
$ref: '#/components/responses/NotFoundError'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
summary: Delete user
description: |
Permanently delete a user account. This action cannot be undone.
Requires admin privileges.
tags:
- Users
security:
- BearerAuth: []
parameters:
- name: userId
in: path
required: true
schema:
type: string
pattern: '^user_[a-zA-Z0-9]+$'
responses:
'204':
description: User deleted successfully
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
$ref: '#/components/responses/ForbiddenError'
'404':
$ref: '#/components/responses/NotFoundError'
'409':
description: Cannot delete user with active loans
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "USER_HAS_ACTIVE_LOANS"
message: "Cannot delete user with active book loans"
details:
activeLoans: 3
dueDate: "2024-02-15T23:59:59Z"
'500':
$ref: '#/components/responses/InternalServerError'
/auth/login:
post:
summary: User authentication
description: |
Authenticate a user with email and password. Returns JWT token on success.
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
example:
email: "john.doe@email.com"
password: "SecurePass123!"
responses:
'200':
description: Login successful
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
example:
user:
id: "user_123"
email: "john.doe@email.com"
firstName: "John"
lastName: "Doe"
membershipType: "adult"
token:
accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
tokenType: "Bearer"
expiresIn: 3600
refreshToken: "refresh_token_here"
'400':
$ref: '#/components/responses/ValidationError'
'401':
description: Invalid credentials
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "INVALID_CREDENTIALS"
message: "Email or password is incorrect"
'429':
description: Too many login attempts
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "TOO_MANY_ATTEMPTS"
message: "Too many login attempts. Please try again in 15 minutes."
details:
retryAfter: 900
'500':
$ref: '#/components/responses/InternalServerError'
components:
schemas:
User:
type: object
properties:
id:
type: string
description: Unique user identifier
pattern: '^user_[a-zA-Z0-9]+$'
example: "user_123"
email:
type: string
format: email
description: User's email address
example: "john.doe@email.com"
firstName:
type: string
minLength: 1
maxLength: 50
description: User's first name
example: "John"
lastName:
type: string
minLength: 1
maxLength: 50
description: User's last name
example: "Doe"
status:
type: string
enum: [active, inactive, suspended]
description: Current user status
example: "active"
membershipType:
type: string
enum: [adult, minor, senior, student]
description: Type of library membership
example: "adult"
registrationDate:
type: string
format: date-time
description: Date when user registered
example: "2024-01-15T10:30:00Z"
lastLoginDate:
type: string
format: date-time
nullable: true
description: Date of last login
example: "2024-01-20T14:22:30Z"
profile:
$ref: '#/components/schemas/UserProfile'
required:
- id
- email
- firstName
- lastName
- status
- membershipType
- registrationDate
UserProfile:
type: object
properties:
dateOfBirth:
type: string
format: date
description: User's date of birth
example: "1990-05-15"
address:
$ref: '#/components/schemas/Address'
phone:
type: string
pattern: '^\+?[1-9]\d{1,14}$'
description: User's phone number
example: "+1-555-0123"
preferences:
$ref: '#/components/schemas/UserPreferences'
parentGuardian:
$ref: '#/components/schemas/ParentGuardian'
Address:
type: object
properties:
street:
type: string
maxLength: 100
example: "123 Main St"
city:
type: string
maxLength: 50
example: "Springfield"
state:
type: string
maxLength: 2
pattern: '^[A-Z]{2}$'
example: "IL"
zipCode:
type: string
pattern: '^\d{5}(-\d{4})?$'
example: "62701"
required:
- street
- city
- state
- zipCode
UserPreferences:
type: object
properties:
emailNotifications:
type: boolean
default: true
description: Receive email notifications
smsNotifications:
type: boolean
default: false
description: Receive SMS notifications
newsletter:
type: boolean
default: true
description: Subscribe to library newsletter
language:
type: string
enum: [en, es, fr, de]
default: "en"
description: Preferred language
bookCategories:
type: array
items:
type: string
description: Preferred book categories
example: ["fiction", "mystery", "science"]
ParentGuardian:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 100
example: "Sarah Johnson"
email:
type: string
format: email
example: "parent@email.com"
phone:
type: string
pattern: '^\+?[1-9]\d{1,14}$'
example: "+1-555-0124"
relationship:
type: string
enum: [parent, guardian, grandparent]
default: "parent"
required:
- name
- email
- phone
CreateUserRequest:
type: object
properties:
email:
type: string
format: email
example: "jane.smith@email.com"
password:
type: string
minLength: 8
pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'
description: |
Password must contain:
- At least 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one digit
- At least one special character (@$!%*?&)
firstName:
type: string
minLength: 1
maxLength: 50
lastName:
type: string
minLength: 1
maxLength: 50
dateOfBirth:
type: string
format: date
membershipType:
type: string
enum: [adult, minor, senior, student]
default: "adult"
address:
$ref: '#/components/schemas/Address'
phone:
type: string
pattern: '^\+?[1-9]\d{1,14}$'
parentGuardian:
$ref: '#/components/schemas/ParentGuardian'
required:
- email
- password
- firstName
- lastName
- dateOfBirth
UpdateUserRequest:
type: object
properties:
firstName:
type: string
minLength: 1
maxLength: 50
lastName:
type: string
minLength: 1
maxLength: 50
address:
$ref: '#/components/schemas/Address'
phone:
type: string
pattern: '^\+?[1-9]\d{1,14}$'
preferences:
$ref: '#/components/schemas/UserPreferences'
LoginRequest:
type: object
properties:
email:
type: string
format: email
password:
type: string
minLength: 1
required:
- email
- password
LoginResponse:
type: object
properties:
user:
$ref: '#/components/schemas/User'
token:
$ref: '#/components/schemas/TokenInfo'
required:
- user
- token
TokenInfo:
type: object
properties:
accessToken:
type: string
description: JWT access token
tokenType:
type: string
enum: [Bearer]
default: "Bearer"
expiresIn:
type: integer
description: Token expiration time in seconds
example: 3600
refreshToken:
type: string
description: Refresh token for obtaining new access tokens
required:
- accessToken
- tokenType
- expiresIn
- refreshToken
UserResponse:
type: object
properties:
data:
$ref: '#/components/schemas/User'
required:
- data
UserListResponse:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/PaginationInfo'
required:
- data
- pagination
PaginationInfo:
type: object
properties:
page:
type: integer
minimum: 1
description: Current page number
limit:
type: integer
minimum: 1
description: Items per page
total:
type: integer
minimum: 0
description: Total number of items
totalPages:
type: integer
minimum: 0
description: Total number of pages
required:
- page
- limit
- total
- totalPages
ErrorResponse:
type: object
properties:
error:
type: object
properties:
code:
type: string
description: Error code identifier
message:
type: string
description: Human-readable error message
details:
type: object
description: Additional error details
additionalProperties: true
required:
- code
- message
required:
- error
responses:
ValidationError:
description: Request validation failed
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "VALIDATION_ERROR"
message: "Request validation failed"
details:
- field: "email"
message: "Email format is invalid"
- field: "password"
message: "Password must be at least 8 characters"
UnauthorizedError:
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "UNAUTHORIZED"
message: "Authentication token is required"
ForbiddenError:
description: Insufficient privileges
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "FORBIDDEN"
message: "Insufficient privileges for this operation"
NotFoundError:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "NOT_FOUND"
message: "User not found"
InternalServerError:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: "INTERNAL_ERROR"
message: "An unexpected error occurred"
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
JWT token authentication. Include the token in the Authorization header:
`Authorization: Bearer <token>`
tags:
- name: Users
description: User management operations
- name: Authentication
description: Authentication and authorization operations