API Development Mastery
Backend API Fundamentals
REST, HTTP, status codes, URI designData Layer & Persistence
Database integration, CRUD, transactions, RedisOpenAPI Specification
Contract-first design, OpenAPI 3.0/3.1Documentation & DX
Swagger UI, Redoc, developer portalsAuthentication & Authorization
OAuth 2.0, JWT, RBAC, ABACSecurity Hardening
OWASP Top 10, input validation, CORSAWS API Gateway
REST/HTTP APIs, Lambda integration, WAFAzure API Management
Policies, products, developer portalGCP Apigee
API proxies, monetization, analyticsArchitecture Patterns
Gateway, BFF, microservices, DDDVersioning & Governance
SemVer, deprecation, lifecycleMonitoring & Analytics
Observability, tracing, SLIs/SLOsPerformance & Rate Limiting
Caching, throttling, load testingGraphQL & gRPC
Alternative API styles, Protocol BuffersTesting & Contracts
Contract testing, Pact, Postman/NewmanCI/CD & Automation
Spectral, GitHub Actions, TerraformAPI Product Management
API as Product, monetization, ecosystemsGraphQL Fundamentals
What is GraphQL?
GraphQL is a query language and runtime for APIs that allows clients to request exactly the data they need. Developed by Facebook, it solves over-fetching and under-fetching problems common in REST.
GraphQL Core Concepts
| Concept | Description |
|---|---|
| Schema | Type definitions describing available data |
| Query | Read operations - fetch data |
| Mutation | Write operations - create/update/delete |
| Subscription | Real-time updates via WebSocket |
| Resolver | Functions that return data for fields |
Schemas & Types
# schema.graphql - Type definitions
type Query {
tasks(status: TaskStatus, limit: Int = 20, cursor: String): TaskConnection!
task(id: ID!): Task
me: User!
}
type Mutation {
createTask(input: CreateTaskInput!): Task!
updateTask(id: ID!, input: UpdateTaskInput!): Task!
deleteTask(id: ID!): Boolean!
}
type Subscription {
taskUpdated(userId: ID!): Task!
}
type Task {
id: ID!
title: String!
description: String
status: TaskStatus!
priority: Priority!
dueDate: DateTime
createdAt: DateTime!
updatedAt: DateTime!
owner: User!
tags: [Tag!]!
}
type User {
id: ID!
email: String!
name: String!
tasks(status: TaskStatus): [Task!]!
}
type TaskConnection {
edges: [TaskEdge!]!
pageInfo: PageInfo!
}
type TaskEdge {
node: Task!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
enum TaskStatus {
PENDING
IN_PROGRESS
COMPLETED
CANCELLED
}
enum Priority {
LOW
MEDIUM
HIGH
URGENT
}
input CreateTaskInput {
title: String!
description: String
priority: Priority = MEDIUM
dueDate: DateTime
tagIds: [ID!]
}
input UpdateTaskInput {
title: String
description: String
status: TaskStatus
priority: Priority
dueDate: DateTime
}
scalar DateTime
Queries & Mutations
// Apollo Server setup
const { ApolloServer, gql } = require('apollo-server-express');
const { makeExecutableSchema } = require('@graphql-tools/schema');
// Resolvers
const resolvers = {
Query: {
tasks: async (_, { status, limit, cursor }, { dataSources, user }) => {
return dataSources.taskAPI.getTasks({ userId: user.id, status, limit, cursor });
},
task: async (_, { id }, { dataSources }) => {
return dataSources.taskAPI.getTask(id);
},
me: (_, __, { user }) => user
},
Mutation: {
createTask: async (_, { input }, { dataSources, user }) => {
return dataSources.taskAPI.createTask({ ...input, userId: user.id });
},
updateTask: async (_, { id, input }, { dataSources, user }) => {
// Authorization check
const task = await dataSources.taskAPI.getTask(id);
if (task.userId !== user.id) {
throw new ForbiddenError('Not authorized');
}
return dataSources.taskAPI.updateTask(id, input);
},
deleteTask: async (_, { id }, { dataSources, user }) => {
const task = await dataSources.taskAPI.getTask(id);
if (task.userId !== user.id) {
throw new ForbiddenError('Not authorized');
}
return dataSources.taskAPI.deleteTask(id);
}
},
// Field resolver for nested data
Task: {
owner: (task, _, { dataSources }) => {
return dataSources.userAPI.getUser(task.userId);
},
tags: (task, _, { dataSources }) => {
return dataSources.tagAPI.getTagsByTask(task.id);
}
}
};
// Create server
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
user: req.user,
dataSources: {
taskAPI: new TaskAPI(),
userAPI: new UserAPI(),
tagAPI: new TagAPI()
}
})
});
Client Query Examples
# Query: Get tasks with owner info
query GetMyTasks($status: TaskStatus, $limit: Int) {
tasks(status: $status, limit: $limit) {
edges {
node {
id
title
status
priority
dueDate
owner {
name
email
}
tags {
name
color
}
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
# Mutation: Create task
mutation CreateTask($input: CreateTaskInput!) {
createTask(input: $input) {
id
title
status
createdAt
}
}
gRPC Fundamentals
What is gRPC?
gRPC is a high-performance RPC framework using Protocol Buffers for serialization. It's ideal for microservice communication with features like streaming and strong typing.
- Performance: Binary serialization (10x smaller than JSON)
- Streaming: Client, server, and bidirectional streams
- Code Generation: Typed clients in 10+ languages
- HTTP/2: Multiplexing, header compression
Protocol Buffers
// task.proto - Protocol Buffer definitions
syntax = "proto3";
package taskapi.v1;
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
service TaskService {
// Unary RPCs
rpc GetTask(GetTaskRequest) returns (Task);
rpc CreateTask(CreateTaskRequest) returns (Task);
rpc UpdateTask(UpdateTaskRequest) returns (Task);
rpc DeleteTask(DeleteTaskRequest) returns (google.protobuf.Empty);
// Server streaming
rpc ListTasks(ListTasksRequest) returns (stream Task);
// Bidirectional streaming
rpc SyncTasks(stream TaskUpdate) returns (stream Task);
}
message Task {
string id = 1;
string title = 2;
string description = 3;
TaskStatus status = 4;
Priority priority = 5;
google.protobuf.Timestamp due_date = 6;
google.protobuf.Timestamp created_at = 7;
google.protobuf.Timestamp updated_at = 8;
string user_id = 9;
}
enum TaskStatus {
TASK_STATUS_UNSPECIFIED = 0;
TASK_STATUS_PENDING = 1;
TASK_STATUS_IN_PROGRESS = 2;
TASK_STATUS_COMPLETED = 3;
TASK_STATUS_CANCELLED = 4;
}
enum Priority {
PRIORITY_UNSPECIFIED = 0;
PRIORITY_LOW = 1;
PRIORITY_MEDIUM = 2;
PRIORITY_HIGH = 3;
PRIORITY_URGENT = 4;
}
message GetTaskRequest {
string id = 1;
}
message CreateTaskRequest {
string title = 1;
string description = 2;
Priority priority = 3;
google.protobuf.Timestamp due_date = 4;
}
message UpdateTaskRequest {
string id = 1;
optional string title = 2;
optional string description = 3;
optional TaskStatus status = 4;
optional Priority priority = 5;
}
message DeleteTaskRequest {
string id = 1;
}
message ListTasksRequest {
optional TaskStatus status = 1;
int32 limit = 2;
}
message TaskUpdate {
string id = 1;
Task task = 2;
}
gRPC Server Implementation
// grpc-server.js - Node.js gRPC server
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const packageDefinition = protoLoader.loadSync('task.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const taskProto = grpc.loadPackageDefinition(packageDefinition).taskapi.v1;
// Service implementation
const taskService = {
getTask: async (call, callback) => {
try {
const task = await db.tasks.findById(call.request.id);
callback(null, task);
} catch (error) {
callback({ code: grpc.status.NOT_FOUND, message: 'Task not found' });
}
},
createTask: async (call, callback) => {
const task = await db.tasks.create(call.request);
callback(null, task);
},
// Server streaming - sends multiple responses
listTasks: async (call) => {
const { status, limit } = call.request;
const cursor = db.tasks.find({ status }).limit(limit).cursor();
for await (const task of cursor) {
call.write(task);
}
call.end();
},
// Bidirectional streaming
syncTasks: (call) => {
call.on('data', async (update) => {
const task = await db.tasks.upsert(update.id, update.task);
call.write(task);
});
call.on('end', () => call.end());
}
};
// Start server
const server = new grpc.Server();
server.addService(taskProto.TaskService.service, taskService);
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
console.log('gRPC server running on port 50051');
});
REST vs GraphQL vs gRPC
When to Use Each
| Aspect | REST | GraphQL | gRPC |
|---|---|---|---|
| Best For | Public APIs, CRUD | Complex frontends, BFF | Microservices, real-time |
| Data Format | JSON | JSON | Binary (Protobuf) |
| Performance | Good | Good (can optimize) | Excellent |
| Browser Support | Native | Native | Requires grpc-web |
| Learning Curve | Low | Medium | Medium-High |
Practice Exercises
Exercise 1: GraphQL API
- Create GraphQL schema for tasks
- Implement Query and Mutation resolvers
- Test with Apollo Studio
Exercise 2: gRPC Service
- Define Protocol Buffer schema
- Generate server and client code
- Implement server streaming RPC
Exercise 3: API Gateway with Multiple Backends
- GraphQL gateway federating multiple services
- REST and gRPC backends
- Protocol translation layer