ABCsteps lesson path

Create Your Own API

Build an Express.js server to receive and return data so the app can do more than display a static screen. Build one artifact, keep one review trail, and make the work easy to inspect later.

Lesson
13
Time
45 min
Access
public lesson

Learning objective

Create simple endpoints with clear input and output behavior.

Lab outcome

Build an API route and call it from the frontend.

Module milestone

Build a small full-stack leaderboard with persistent data.

Lesson proof workflow

Read, build, then review the evidence.

  1. Node.js hero workflow iconStep 1ReadStart with Express endpoints before touching tools.
  2. JavaScript hero workflow iconStep 2BuildBuild toward: Build an API route and call it from the frontend.
  3. JSON hero workflow iconStep 3ReviewReview the evidence using Client server calls.

Toolchain

Your own API turns a static interface into a system.

These are the practical surfaces used in this lesson. Learn the habit first, then connect it to the wider engineering ecosystem.

Node.js iconNode.jsAPI server

Create endpoints with clear request and response behavior.

JavaScript iconJavaScriptApp logic

Connect UI behavior to server responses.

JSON iconJSONResponse data

Return predictable payloads the client can use.

Proof of work

Leave one inspectable trail from this lesson.

The useful output is not a passive note. It is a small artifact another person can inspect: a working file, a command result, a commit, a screenshot, a README note, or a demo link.

Lesson lab: Build an API route and call it from the frontend.

Tool and platform logos are ecosystem references only: no affiliation, endorsement, interview access, hiring promise, salary promise, or placement guarantee.

Node.js proof icon

Build

Produce the artifact

Complete the lab and keep the result visible: Build an API route and call it from the frontend.

JavaScript proof icon

Record

Save review evidence

Capture what changed, what broke, and how Express endpoints became clearer through the work.

JSON proof icon

Explain

Write the vocabulary

Use your own words for Input output design and Client server calls; this is what makes the lesson inspectable later.

Skills companies recognize

Translate the lesson into inspectable work language.

This lesson turns one small lab into the language a learner can use in a README, demo note, or technical conversation. The point is not to collect logos; the point is to explain work clearly enough that another engineer can inspect it.

Where this skill appears

Backend and full-stack teams care less about the framework name and more about clear endpoint behavior.

Backend engineeringAPI platformFull-stack product teams

Ecosystem references

AWS skill ecosystem logoGoogle Cloud skill ecosystem logoMicrosoft skill ecosystem logoGitHub skill ecosystem logoCloudflare skill ecosystem logoOpenAI skill ecosystem logoGoogle skill ecosystem logo

Platform and company logos are ecosystem references only: no affiliation, endorsement, interview access, hiring preference, salary outcome, or placement guarantee.

Node.js skill proof icon

README line

Name the artifact

Lab proof: Build an API route and call it from the frontend. Connect it to Express endpoints so the result reads like work, not a passive note.

JavaScript skill proof icon

Review line

Explain the stack

Use Node.js, JavaScript, JSON to explain Input output design and what changed between the first attempt and the inspected result.

JSON skill proof icon

Conversation line

Answer with evidence

If a team asks about Client server calls, use this proof line: Show the endpoint, sample input, sample output, error case, and the frontend call that uses it.

Proof translation

Node.js proof translation icon

Skill signal

Express endpoints is the market word. The lesson makes it visible through a small working artifact.

JavaScript proof translation icon

Proof artifact

The inspectable artifact is: Build an API route and call it from the frontend.

JSON proof translation icon

Interview answer

Use Input output design and Client server calls to explain what changed, what failed, and how you verified it.

Paid guidance

Read publicly. Upgrade when guidance will help you finish.

This lesson remains part of the public written syllabus. Paid help is online-only and human-led: video walkthroughs as they roll out, live class context, WhatsApp Q&A, and project review around the same work.

No account wall, automated checkout, or placement promise is introduced here. Enrollment stays human-led by WhatsApp or call, and the useful proof remains the learner's own artifact.

Node.js paid guidance icon

Public

Written lesson stays open

Read the prepare and review material for lesson 13 on the public site before buying anything.

JavaScript paid guidance icon

Recorded

Recorded and live guidance clarify the work

Paid guidance can add founder-led video walkthroughs as they roll out and live online class context; the teaching explains the work, but does not replace the written lesson.

JSON paid guidance icon

Human

Questions use real context

When stuck, useful guidance starts from the route, error, screenshot, repo fragment, and the lab artifact: Build an API route and call it from the frontend.

Phase 1 · Briefing

Lesson briefing

Before You Study (5 mins)

Lesson focus: Until now, you've called other people's APIs (Lesson 09: weather, quotes, LLMs). Today you flip the relationship and become the API provider. Your backend will expose a real REST API for a Snake-game leaderboard — GET to read scores, POST to add one, DELETE to clear them — with proper request validation, error handling, and the conventions every professional API follows. By the end, your frontend talks to your backend over an interface you designed and own.

What you should have ready:

  • Lesson 12's backend/ directory with Express, CORS, and the /api/health route working
  • nodemon running so the server hot-reloads as you edit
  • A REST client for testing — your terminal curl is fine, Insomnia or Bruno are free GUIs
  • About 60 minutes
  • Patience for debugging — half of API work is fixing the 400-status response that just came back

The Concept

A REST API is a way of designing HTTP endpoints around a small, consistent vocabulary. The word "REST" stands for Representational State Transfer — but the practical meaning today is: resources have URLs, and HTTP verbs do operations on those resources. That's the entire idea.

The vocabulary you saw in Lesson 09 from the consumer side, now applied as a designer:

VerbPathOperation
GET /scoresRead all scores
GET /scores/42Read score with ID 42
POST /scoresCreate a new score
PUT /scores/42Replace score 42 entirely
PATCH /scores/42Partially update score 42
DELETE /scores/42Remove score 42

The same path with different verbs does different things. This is RESTful design's core elegance: a small, predictable surface that any developer can navigate without docs.

A real API has more than just routes. It has a request-handling pipeline that is the same shape on every server you'll ever build:

text
HTTP request arrives
      │
      ▼
1. Parse body / query params (express.json())
      │
      ▼
2. Validate input (is name a non-empty string? is score a number?)
      │
      ▼
3. Authenticate (who is the caller? do they have a valid token?)   [later lessons]
      │
      ▼
4. Authorize (are they allowed to do THIS action?)                 [later lessons]
      │
      ▼
5. Execute business logic (compute, query DB, call other services)
      │
      ▼
6. Format response (set status code, return JSON)
      │
      ▼
HTTP response sent

This pipeline is implemented in Express through middleware — small functions that run in order, each one either passing control to the next (next()) or short-circuiting with a response. Authentication, validation, logging, error handling — they're all middleware. Once this clicks, every Express server feels like the same shape.

A few conventions every well-designed API follows:

  • Use the right verb. GET doesn't change state. POST creates. DELETE removes. Mixing them up confuses everyone, including future-you.
  • Return the right status code. 200 OK for read, 201 Created for write, 204 No Content for delete, 400 Bad Request for validation failures, 404 Not Found for missing resources, 500 Internal Server Error for crashes. The status code is the API speaking to you in its own language; respect it.
  • Return JSON, always. Even errors ({ "error": "name is required" }). Never plain text or HTML.
  • Validate everything. A POST /scores with score: "🐍" instead of a number should fail at validation, not at the database.
  • Be idempotent where you can. A PUT /scores/42 with the same body should produce the same end state on every call. POST is the exception.

Today we'll build a toy in-memory leaderboard — scores stored in a JavaScript array, lost on server restart. Lesson 14 replaces the array with a real SQLite database. The route handlers and the API design stay identical; only the storage changes. This is a deliberate decoupling: API surface is the contract, storage is the implementation.

The companion article for the consumer side of this lesson is What is an API and How Do APIs Actually Work — which now reads differently because you're about to design your own.

Quick Concepts

TermSimple Meaning
RESTResources have URLs; HTTP verbs do operations on them
EndpointA specific URL + verb combination handled by your server
Route handlerThe function that runs when a request hits an endpoint
MiddlewareA function in the request-processing pipeline (parse, auth, log, ...)
req.bodyThe parsed JSON body of a POST/PUT/PATCH request
req.paramsURL path parameters — /scores/:id makes req.params.id available
req.queryQuery string parameters — /scores?limit=10 makes req.query.limit available
ValidationChecking inputs are well-formed before running business logic

What We Will Build

By the end of this lesson, you will have done these specific things:

  1. Set up an in-memory store in server.js — a simple JavaScript array we can read and write to:
    javascript
    let scores = []           // [{ id, name, score, createdAt }]
    let nextId = 1
    
  2. Built GET /api/scores — returns the array, sorted by score descending, optionally paginated via ?limit=10:
    javascript
    app.get('/api/scores', (req, res) => {
      const limit = Math.min(Number(req.query.limit) || 10, 100)
      const sorted = [...scores].sort((a, b) => b.score - a.score).slice(0, limit)
      res.json({ scores: sorted, count: sorted.length, total: scores.length })
    })
    
  3. Built POST /api/scores — accepts { name, score }, validates them, creates a new entry, returns it with a 201 Created:
    javascript
    app.post('/api/scores', (req, res) => {
      const { name, score } = req.body
      if (typeof name !== 'string' || name.trim().length < 1 || name.length > 30) {
        return res.status(400).json({ error: 'name must be a 1-30 char string' })
      }
      if (typeof score !== 'number' || score < 0 || !Number.isFinite(score)) {
        return res.status(400).json({ error: 'score must be a non-negative number' })
      }
      const entry = { id: nextId++, name: name.trim(), score, createdAt: new Date().toISOString() }
      scores.push(entry)
      res.status(201).json(entry)
    })
    
  4. Built GET /api/scores/:id — returns one specific score by ID; 404 Not Found if it doesn't exist.
  5. Built DELETE /api/scores/:id — removes a score; 204 No Content on success.
  6. Tested every endpoint with curl — the discipline of testing your own API from the terminal builds the muscle every senior backend engineer has:
    bash
    curl -s http://localhost:3001/api/scores | jq .
    curl -s -X POST http://localhost:3001/api/scores \
      -H 'Content-Type: application/json' \
      -d '{"name":"Divyanshu","score":1500}' | jq .
    
  7. Triggered every error path on purpose — sent a missing name (expect 400), sent an invalid score (expect 400), fetched a non-existent ID (expect 404), so you've seen the failure modes of your own API.
  8. Wired the frontend — your Snake game now calls POST /api/scores on game-over and GET /api/scores on game-start. The leaderboard appears on screen.
  9. Added one global error-handler middleware at the bottom of your server so any uncaught error returns a clean 500 Internal Server Error JSON instead of an HTML stack trace:
    javascript
    app.use((err, req, res, next) => {
      console.error(err)
      res.status(500).json({ error: 'Internal server error' })
    })
    

Step 9 is what separates a real API from a hello-world demo. In production, something will throw; the question is what the user sees when it does.

Think About

Before studying, consider:

  1. If you accept any payload POST /api/scores sends — including { "score": 999999999 } from a malicious client — your leaderboard fills with garbage in 60 seconds. Where does the rule "scores must be plausible" belong? (Hint: not just on the frontend. Backend validation is non-negotiable.)
  2. The leaderboard you build today loses all data on server restart. That's fine for development. What changes the moment you put this in production with real users? (Hint: the storage layer becomes the most important part of the system. Lesson 14 builds it.)

By the End

After this lesson, you'll:

  • Have a working CRUD API for scores (Create, Read one, Read many, Delete)
  • Know the right HTTP verb for each operation
  • Know the right status code for each outcome (200 / 201 / 204 / 400 / 404 / 500)
  • Validate every input before touching state
  • Have a global error-handling middleware
  • Test your own API from curl and from your frontend
  • Be ready for Lesson 14 — replacing the in-memory array with a real database

You are the chef now. The world places orders. 👨‍🍳

Next lesson · 14

Databases: Store Data Permanently

Use SQLite to understand tables, records, and persistence without adding unnecessary infrastructure.

Node.js next lesson iconJSON next lesson iconGit next lesson icon