APIs
Designing APIs
An API is a set of rules that defines interactions between software applications so they can communicate and share functionalities.
- Integration: facilitates communication between various software entities, enabling seamless interaction and data sharing
- Automation: supports task automation, allowing systems to interact programmatically without human intervention
- Efficiency: optimizes data transfer by enabling extraction of specific data between applciations.
It's important because it is the binding agent for diverse software components, enabling the creation of sophisticated, feature-rich applications.
- modularity: promotes development of clear, independent, and manageable software components, enhancing maintainability.
- innovation: foster creation of new business models and extension of services by allowing integration of services and for them to build on top of each other.
- scalability and performance: facilitates efficient scalability of individual components, ensuring high performance and availability.
Types of APIs:
- web APIs: enables communication over the web using HTTP/HTTPS
- library-based APIs: libraries or SDKs, provides routines and tools for software development
- operating system APIs: allow interactions with underlying OS services, enabling tasks like file handling and memory allocation.
- database APIs: enable communication between application and database management systems, allowing CRUD operations on database records
Web APIs
Web APIs allow different software systems to communicate with each other over the web using HTTP/HTTPS.
- Open APIs: accessible to developers and public (e.g., Twitter API)
- Internal APIs: used within a company for internal services
- Composite APIs: access multiple endpoints in one call; useful for microservices
- Partner APIs: exposed to specific partners or collaborators.
REST stands for representational state transfer. It's the most common web API and its a architectural style for designing networked applications. It uses HTTP requests to perform CRUD operations on resources.
REST Principles build around REST principles to provide a stateless, scalable, and cacheable way to interact with web resources.
- statelessness: each request contains all information needed by the server
- client-server architecture: client manages UI/UX; server manages resources, operating independently
- uniform interface: ensures consistent and standardized interactions
- cacheable: responses can be marked as cacheable to improve performance
- layered system: enables scalability through unknown intermediary servers.
RESTful APIs are named around resources. They use URIs to identify resources
- nouns represent resources like
/posts,/users - resource names should be plural
- use sub-resources for relations
/posts/1/comments
Example in Express.js
app.get('/posts', (req, res) => {
// logic to retrieve and return posts
})
app.get('/posts/:postId/comments', (req, res) => {
// logic to retrieve and return comments for a specific post
});
CRUD operations in RESTful APIs rely on standard HTTP methods:
- create:
POSTto create a new resource - read:
GETto retrieve a resource - update:
PUTorPATCHto update a resource - delete:
DELETEto remove a resource.
RESTful APIs use the standard HTTP status codes to convey the outcome of an API request:
2xx: success(e..g, 200 OK, 201 Created) indicates a successful request3xx: redirection(e.g., 301 Moved Permanently, 304 Not Modified) requires the client to take additional action to complete the request4xx: Client Errors(e..g, 400 Bad Request, 404 Not Found) signifies errors from the client side5xx: Server Errors(e.g., 500 Internal Server Error) represents errors from the server side.
Error handling:
- provide clear error message
- use standard HTTP status code
- include error details when necessary
- Example in Express.js:
app.get('/posts/:postId', (req, res) => {
// Logic to retrieve a specific post
if (!postExists) {
res.status(404).send({
status: 404
error: 'Post not found',
message: 'The requested post does not exist.'
});
}
});
GraphQL
GraphQL is a query language for APIs and a runtime for executing those queries.
- created by facebook to address the need for more efficient and powerful data fetching in modern applications.
- declarative data fetching: clients specify what they need exactly. This prevents over/under fetching
- single endpoint: all requests are directed to a single endpoint, improving maintainability and organization of API interactions.
Key concepts:
- schema: defines types, queries, mutations, acts as a contract between client and server
- resolver: functions that determine how data for a specific field is fetched and returned to the client
- querying: allows client to specify the need for data, reducing over/under fetched data
- mutations: operations that create, modify, or delete data defined specifically in the schema
Compare GraphQL vs RESTful APIs:
- data retrieval: GraphQL allows client to grab exactly what they need but REST retrieves all data associated with that resource
- Number of requests: GraphQL allows you to retrieve everything you need with a single request since there's one endpoint while REST may require multiple requests to different endpoints.
- Schema and type: GraphQL is strongly typed and defines the capabilities of the API using a schema, ensuring more reliable interactions.
- Use GraphQL when you have diverse, nested, and complex data requirements exist. The frontend team needs flexibility in data retrieval.
- Use REST when the API has simple, flat, static data structures. A standard, cacheable, and stateless API is suitable.
Things to keep in mind when designing GraphQL APIs:
- define a clear and concise schema: acts as a contract between client and server, representing the API's capabilities
- optimize for the client so that they can fetch exactly what they need
- use descriptive names so that they can clearly convey their purpose and functionality
- versioning: use schema evolution instead of version numbers to manage changes and ensure backwards compatibility.
Example of an GraphQL implementation in Express.js
const { graphqlHTTP } = require('express-graphql')
const { buildSchema } = require('graphql')
const schema = buildSchema('
type Query {
post(id: ID!): Post
}
type Post {
title: String
author: User
comments: [Comment]
}
// other type definitions...
')
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
}))
then the queries are used to read or fetch values:
fetch('/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query: '{
post(id: "1") {
title
author {
name
}
comments {
body
}
}
}'
})
}).then(response => repsonse.json())
You can also perform a mutation to create, update, or delete data. Your GraphQL API would have to support mutations.
RPC
Remote Procedure Call (RPC) is a protocol that one program can use to request a service from a program located on another computer in a network.
- Procedure-oriented: allows calling functions on a remote server
- Synchronous communication: waits for the server to complete the task before resuming.
- Tight coupling: requires knowledge of available procedures on the server.
History and purpose:
- developed in 1980 for transparent communication between software components
- purpose: enable invocation of procedures, functions, or methods on a remote server as if they were local
- objective: facilitate the creation of distributed, client-server based applications.
How it works:
- It uses the client-server model: the client sends a request to the server, and the server returns a response.
- Stub procedures: simplifies the communication process by abstracting network details
- Interface Definition Language (IDL): defines the available procedures and data types.
Types of RPC:
-
Google has gRPC, which improves on the original RPC. It also provides a rich set of tooling around it so that you can build services with their library, which generates a lot of boiler plate for you. Has its own interface description language called Protocol Buffers that is language-agnostic. It's efficient and works with HTTP/2 for transport. Popular choice for micro services, passing data between two servers.
-
tRPC is popular for Typescript application. Uses Typescript. An advantage of tRPC is that you know the schema (or type) of the data you will receive and send. It's type safe. Improves the developer experience.
Example
//server side
const grpc = requre('grpc')
const booksProto = grpc.load('books.proto')
const server = new grpc.Server()
const books = [{id: 1, title: 'Book one', author: 'Author one'}]
server.addService(booksProto.books.BookService.service, {
list:(_, callback) => callback(null, books),
});
server.bind('127.0.0.1:50051', grpc.ServerCredentials.createInsecure());
console.log('server is running at http://127.0.0.1:50051')
server.start();
// Client Side
const grpc = require('grpc');
const booksProto = grpc.load('books.proto');
const client = new grpc.Client('localhost:50051', grpc.credentials.createInsecure());
client.list({}, (error, books) => {
if (!error) console.log('Book List', books);
else console.error(error);
});
Designing RPC APIs
- Define clear procedures that are available and their inputs and outputs using interface definition language.
- Minimize coupling: avoid overly complex procedure definitions and try to minimize the dependency between the client and the server
- Error handling: define clear error messages and handle errors gracefully to aid in debugging and resolution
RPC vs GraphQL:
- data retrieval: GraphQL allows clients to specify data structure, while RPC invokes specific procedures.
- flexibility: GraphQL has more flexibility in fetching data, while RPC is rigid and procedure-oriented.
- coupling: RPC tends to have tighter coupling due to the need to know available procedures (you'll have to look at docs), while GraphQL has looser coupling.
RPC vs REST
- communication: RPC is procedure-oriented and invokes specific functions, while REST is resource-based and manipulates resources using HTTP methods.
- statelessness: REST is stateless, with each request containing all necessary information, while RPC can maintain state between requests (acts more like functions, so you could take results and do something with it)
- coupling: RPC has tight coupling (since you need to know the available services) while REST offers looser coupling.
When to use?
- procedure-oriented application: when the interaction is more about invoking functions or procedures rather than manipulating resources.
- strict contracts: strict contract between client and server is beneficial, as the client needs to know the available protocols. If you're using something like tRPC it has the benefits of knowing the state.
- efficient and performance: less overhead than HTTP and alternative transport protocols can optimize performance. Because RPC has its own encoding it can be up to 5x faster than HTTP.
- network constraints: in environments with network constraints when a lightweight protocol is essential.
You are given data and you need to run some computation on it. This isn't resource based so RPC is better.
Other APIs
There are various other API design methodologies.
- Simple Object Access Protocol (SOAP): a protocol that uses XML for message formatting in communication over the network
- Notable for its strict structured communication, but
- Open Data Protocol (OData): a protocol that allows the creation and consumption of queryable and interoperable RESTful APIPs.
- JSON-RPC and XML-RPC: remote procedure call (RPC) protocols encoding in JSON and XML respectively.
SOAP is a protocol for exchanging structured information in the implementation of web services.
- protocol, not architecture. (just like REST)
- XML-based utilizes XML for message format/data schema and relies on othe application layer protocols like HTTP and SMTP for message negotiation and transmission
- strict standards and specification: has extensive set of rules for structuring messages.
- not as prevalent as REST, GraphQL, and RPC in modern web development.
- used by old school, enterprise level application. Used to be common in financial institutes like banks or government applications used by many divisions across the nation. Takes a lot of time to develop.
Use SOAP when:
- reliability and security is critical, i.e., data integrity and confidentiality. (e.g., enterprise solutions, financial services, telecommunication services)
- interoperability is essential: API needs to interact with various platforms and languages
- stateful operations are needed: client and server needs to understand previous interactions and each other's state
- ACID compliance is required, especially in database interactions.
Linked from Software System Design