What Are APIs, and How Do You Build Your First One?
- #setup
- #tutorial
An API is a defined way for one piece of software to ask another for something, without either side needing to know how the other is built internally. When your weather app shows today’s forecast, it isn’t scraping a webpage. It’s sending a request to a weather service’s API and getting structured data back.
Where you’re already using one
Most of what you do on the internet routes through an API somewhere. Your frontend calling /api/posts to load blog content from your own database counts, even though you built both sides yourself. So does Stripe processing a payment, Twilio sending a text, or GitHub opening a pull request on your behalf, none of which involve a human clicking through a UI. Fetching stock prices, weather, or map data from a service you don’t own is the same pattern from the other side: you send a request, get JSON back, and never touch their database directly.
The most common shape is REST: send an HTTP request (GET, POST, PUT, DELETE) to a URL, get a response back, usually JSON. GET /users/42 fetches user 42. POST /users creates one. GraphQL and gRPC solve the same problem differently, but REST is what you’ll run into first and most often, so it’s the one worth actually learning before the others.
Read one before you build one
Calling an existing API is the fastest way to see the request/response shape in practice, before you have to design your own. Most public APIs publish documentation listing their endpoints and require an API key, a token identifying you, usually passed in a header:
curl https://api.example.com/v1/forecast?city=Chicago \
-H "Authorization: Bearer YOUR_API_KEY"
The response comes back as JSON, which you parse in whatever language you’re using. Request in, structured data out. That’s the whole loop, and once it clicks, building your own is mostly a matter of flipping which side you’re on.
Three ways to build your own
FastAPI, if you know Python or don’t mind learning a little. Define a route as a function, and it generates interactive docs automatically:
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello/{name}")
def greet(name: str):
return {"message": f"Hello, {name}!"}
uvicorn main:app --reload gets you a working endpoint and a browsable docs page at /docs, which is genuinely useful for testing without writing a separate client.
Express, the long-standing default for JavaScript. More manual, no auto-generated docs, but minimal and unopinionated:
const express = require('express');
const app = express();
app.get('/hello/:name', (req, res) => {
res.json({ message: `Hello, ${req.params.name}!` });
});
app.listen(3000);
Next.js API Routes, if you’re already building a Next.js frontend and don’t want a second service to deploy. A file at app/api/hello/route.ts becomes an endpoint automatically:
export async function GET(request: Request) {
return Response.json({ message: 'Hello!' });
}
No CORS to fight, no separate deploy target.
Keeping a key from leaking
The API key in that curl example is exactly the kind of thing that ends up hardcoded in a client-side file and pushed to a public GitHub repo by accident. If you’re calling a third-party API from your own backend, keep the key server-side and never ship it to the browser, since anything sent to a browser is visible to anyone who opens dev tools. If you’re building your own API, the equivalent mistake is skipping auth entirely because “it’s just a side project,” then finding your database quietly racking up reads from a scraper that found the unprotected endpoint. Even a simple API key check on write endpoints heads off most of that.
Pick based on what you’re already doing
Already in Next.js? Use API Routes, it’s zero extra setup. My own default leans JS/TS end to end for exactly that reason: one language across frontend and backend means less context-switching and one set of tooling to keep track of. Python is where I go instead, but specifically for a scripting job or a small standalone API that isn’t sharing code with a frontend anyway, and FastAPI is the one I’d reach for there, mostly for the free auto-generated docs. If you’re deep in Node already and want something more unopinionated than either, Express is still a completely reasonable default. None of these choices are permanent. A route’s shape and its JSON are portable enough that swapping frameworks later means rewriting the boring parts, not the design.
Call an existing API first, then rebuild that same shape yourself with one of the three above. You’ll understand both sides of the request in an afternoon. For the parts that don’t make sense yet, MDN’s HTTP guide covers how requests, responses, and status codes actually work under the hood.