DEV Community

Michael Odhiambo
Michael Odhiambo

Posted on

πŸ€– API vs Endpoint - What's the Difference? (With Real Full-Stack Code)

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"))
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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' };
  }
};
Enter fullscreen mode Exit fullscreen mode

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)