If you're building a full-stack application using frontend frameworks and a backend like Ktor, chances are you're working with APIs and endpoints every day. But what exactly is an API? And how is it different from an endpoint?
Many developers use these terms interchangeably β but there's a meaningful difference. In this blog, weβll demystify them using real working code from a Kotlin Ktor backend and a TypeScript frontend.
π What is an API?
API stands for Application Programming Interface.
In simple terms, an API is a set of rules that allows different software systems to communicate. In a web app, this usually means a frontend talks to a backend using HTTP requests.
β The API is defined in the backend.
β
The API includes all the routes (endpoints) your backend exposes β like /login
, /register
, /getUser
, etc.
π What is an Endpoint?
An endpoint is a specific route or URL within your API that performs a particular function. Think of your API as a restaurant menu, and each endpoint is one dish you can order.
Example:
-
/login
is one endpoint. -
/register
is another endpoint.
Every endpoint is part of an API, but the API is the whole collection of those endpoints.
π§ Quick Comparison
Concept | Defined In | Description | Example |
---|---|---|---|
API | Backend | Full set of functions the backend exposes | Auth API |
Endpoint | Backend | A specific function (route) within the API | POST /login
|
API Call | Frontend | A function that sends requests to an endpoint | axios.post(...) |
π¦ Real Backend Code (Ktor API Endpoint)
This is how we define the POST /login
endpoint in Kotlin using Ktor:
routing {
post("/login") {
try {
val loginRequest = call.receive<LoginRequest>()
val (loginResponse, errorMessage) = authService.login(loginRequest)
if (loginResponse != null) {
logMessage("Login successful for user: ${loginResponse.profile.email}", LogType.INFO)
call.respond(loginResponse)
} else {
logMessage("Login failed for user: ${loginRequest.username}, reason: $errorMessage")
call.respond(HttpStatusCode.BadRequest, mapOf("errorMessage" to (errorMessage ?: "Invalid login")))
}
} catch (e: Exception) {
logMessage("Error processing login request: ${e.message}")
call.respond(HttpStatusCode.InternalServerError, mapOf("errorMessage" to "Internal server error"))
}
}
}
Here:
- We define an API endpoint:
POST /login
- It's part of the larger Authentication API
π Real Frontend Code (API Call Using Axios)
This is how the frontend consumes the /login
endpoint:
export const loginUser = async (
username: string,
password: string
): Promise<LoginResponse | LoginErrorResponse> => {
try {
const response = await axios.post(`${loginUrl}login`, { username, password });
const data = response.data;
// Save tokens securely
if (data.tokens?.accessToken && data.tokens?.refreshToken) {
cookiesService.setCookie('accessToken', data.tokens.accessToken);
cookiesService.setCookie('refreshToken', data.tokens.refreshToken);
}
// Save user profile details
if (data.profile) {
Object.keys(data.profile).forEach(key => {
if (key !== 'profileImage' && data.profile[key]) {
cookiesService.setCookie(key, String(data.profile[key]));
}
});
// Save login session info
const deviceId = getDeviceId();
const deviceName = getBrowserInfo();
const loginTime = Date.now();
const userId = data.profile.userId;
try {
const location = await getLocation();
await saveLoginSession({
userId,
deviceId,
deviceName,
deviceLocation: location ? `${location.city}, ${location.country}` : null,
userAgent: navigator.userAgent,
loginTime,
});
} catch (locationError) {
console.warn('Failed to get location:', locationError);
await saveLoginSession({
userId,
deviceId,
deviceName,
deviceLocation: null,
userAgent: navigator.userAgent,
loginTime,
});
}
}
return data;
} catch (error) {
if (axios.isAxiosError(error)) {
return {
errorMessage: error.response?.data?.errorMessage || 'Invalid credentials'
};
}
return { errorMessage: 'An unexpected error occurred' };
}
};
Here:
- The function
loginUser
is calling the API endpoint/login
- It is not the API itself β it is a consumer of the API
π Conclusion
- β The API lives in the backend.
- β The frontend just calls the API via endpoints.
- β
An endpoint is a specific route in the API like
/login
or/register
. - β API calls from the frontend (like using Axios) are clients β not definitions.
Understanding this distinction helps you architect cleaner, more scalable full-stack apps. Keep building! π»π
βοΈ Author
Post by a full-stack dev passionate about Kotlin, Ktor, TypeScript, and clean architecture. Follow me for more dev insights!
Top comments (0)