HackIndia Logo
HackathonsNewsSkillsGrowth
HackIndia LogoContact: +91 9910125804

General

  • Companies
  • Colleges
  • Board of Advisors
  • Brand Ambassadors
  • FAQ
  • Contact
  • Report a Bug

Hackathons

  • Upcoming Hackathons
  • Judges
  • Mentors
  • Partners
  • Participating Colleges
  • Testimonials

Resources

  • About Us
  • Branding
  • Latest News
  • Growth
  • Skills
  • Builders
  • Ecosystem
  • Forum
  • Ideas
  • Prompt Optimizer
  • Games

© 2026 HackIndia. All rights reserved.

Privacy PolicyTerms of ServiceCode of ConductSitemap

Build 2.139 · 05 Nov 2026, 10:27 UTC

    HackIndia
    HackathonsNewsInternshipsSkillsGrowthPro Perks
    ← Build a Recruiter-Ready Express API

    All the reading

    Express

    8 lessons, about 33 minutes. The check's questions come from these.

    Contents
    Creating first routes
    1. 1. Ship Your First Express Route
    2. 2. Design Routes That Make Sense
    Reading requests cleanly
    1. 3. Use Middleware Without Hanging Requests
    2. 4. Read Bodies, Queries, and Params
    Using statuses and config
    1. 5. Return Status Codes With Meaning
    2. 6. Protect Config and Secrets
    Handling errors and release
    1. 7. Handle Errors in One Place
    2. 8. Publish Your API Repo

    Creating first routes

    1. Ship Your First Express Route

    4 min

    In 30 seconds
    • Create a small Node.js 24 LTS project with Express 5.
    • Add one GET route that returns JSON.
    • Run your API locally and check it in the browser.

    What you build

    You will create the smallest useful backend for this track: one Express app with one route.

    The route will answer GET / with JSON. This is the first proof that your API runs on your laptop and can later be shared in a public repo.

    Key point

    Remember this

    An Express API is a Node app that listens for HTTP requests and sends responses.

    A route connects one HTTP method and one path to one handler function. For example, GET / runs when someone opens the root URL.

    Your to-do

    Create the project

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    node -v
    npm init -y
    npm install express
    mkdir src
    Example

    First route

    Put this in src/app.js.

    This uses app.get('/', handler). The get part is the HTTP method. The '/' part is the path. Query strings are not part of the route path; you will handle them in a later lesson.

    import express from 'express';
    
    const app = express();
    const port = 3000;
    
    app.get('/', (req, res) => {
      res.json({
        message: 'Recruiter-ready API is running',
        owner: 'Your Name',
        status: 'ok'
      });
    });
    
    app.listen(port, () => {
      console.log(`API listening on http://localhost:${port}`);
    });

    2. Design Routes That Make Sense

    4 min

    In 30 seconds
    • Name routes with nouns, not actions.
    • Choose HTTP methods by what the client wants to do.
    • Sketch your API before adding more code.
    Key point

    One route idea

    A good route describes a resource. The HTTP method describes the action.

    Use /projects with GET to list projects. Use /projects with POST to create one. Avoid routes like /getProjects or /createProject.

    Routes are contracts

    Your API routes are the first thing a recruiter will scan. They should be easy to guess without reading every line of code.

    Think like a frontend developer using your API. If the app has students, projects, teams, or applications, those words usually become route names.

    Your to-do

    Pick route names

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Reading requests cleanly

    3. Use Middleware Without Hanging Requests

    4 min

    In 30 seconds
    • Add middleware that logs, checks, or prepares a request before a route.
    • Use next() when middleware does not send a response.
    • Keep middleware order clear so your API never hangs.
    Key point

    Remember this

    Middleware sits between the incoming request and your route handler. It can run code, change req or res, send a response, or pass control forward.

    If it does not send a response, it must call next(). Otherwise the browser, Postman, or curl keeps waiting.

    Where it runs

    Express has application-level middleware and router-level middleware. You use app.use() for the whole app, or router.use() for one group of routes.

    In a recruiter-ready repo, middleware is useful for logging, simple checks, auth later, and parsing request bodies. This lesson focuses on flow, not body parsing.

    Example

    A safe logger

    This logger prints the method and path, then lets the request continue. Put it before your routes.

    Using statuses and config

    5. Return Status Codes With Meaning

    4 min

    In 30 seconds
    • Choose 200, 201, 400, 404, and 500 instead of sending every response as 200.
    • Use Express 5 style: res.status(code).json(data), not old response shortcuts.
    • Make your API easier for recruiters to test with clear success and error replies.
    Key point

    One rule

    Your status code should tell the client what happened before they read the JSON.

    Use 2xx for success, 4xx when the client sent a bad request, and 5xx when the server failed.

    Why it matters

    Recruiters will run your API with curl, Postman, or Thunder Client. They should see useful status codes, not only pretty messages.

    A frontend also depends on this. If your API returns 200 for an error, the frontend may treat a failed request as a success.

    Your to-do

    Common choices

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    Handling errors and release

    7. Handle Errors in One Place

    5 min

    In 30 seconds
    • Create one error handler for your whole Express API.
    • Let async route failures reach the handler in Express 5.
    • Return safe, useful error JSON with the right status code.
    Key point

    One exit for errors

    Do not format errors inside every route. Send failed requests to one error-handling middleware, then shape the response there.

    In Express, an error handler has four arguments: err, req, res, next. Put it after your routes.

    Why this matters

    A recruiter will test your API with wrong IDs, missing fields, bad JSON, and broken requests. If each route returns errors differently, your project feels unfinished.

    A central handler makes your API predictable. Your success responses stay in routes. Your failure responses stay in one place.

    Example

    Route to handler

    In Express 5, async route handlers that reject or throw are passed to error middleware. You do not need the old Express 4 wrapper pattern for every async route.

    10 questions, 7 to pass.

    Sign in to take the test
    Your to-do

    Make imports work

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 3 done

    {
      "type": "module",
      "scripts": {
        "dev": "node src/app.js"
      },
      "dependencies": {
        "express": "^5.2.1"
      }
    }
    Tip

    Test like a user

    Run npm run dev. Then open http://localhost:3000 in your browser.

    You should see JSON, not an HTML page. This is normal for an API. Later, your frontend, Postman, curl, or a recruiter’s local machine can call the same route.

    npm run dev
    Common mistake

    Avoid old syntax

    Older tutorials may show Express 4 patterns. Do not copy response shortcuts like res.json(obj, status) or res.send(body, status).

    In Express 5, set the status first when you need one: res.status(201).json(data). For today, the default 200 response is fine.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Example

    Clean route sketch

    This is a simple project API shape. It uses route methods from HTTP methods such as GET, POST, PATCH, and DELETE.

    Express route paths do not include query strings. /projects?tech=node is still matched by /projects.

    import express from 'express';
    
    const app = express();
    
    app.get('/projects', (req, res) => {
      res.json({ message: 'List projects' });
    });
    
    app.post('/projects', (req, res) => {
      res.status(201).json({ message: 'Create project' });
    });
    
    app.get('/projects/:id', (req, res) => {
      res.json({ message: `Get project ${req.params.id}` });
    });
    
    app.patch('/projects/:id', (req, res) => {
      res.json({ message: `Update project ${req.params.id}` });
    });
    
    app.delete('/projects/:id', (req, res) => {
      res.status(204).send();
    });
    Common mistake

    Avoid action URLs

    Beginners often put the verb inside the path. That makes the API noisy and hard to extend.

    Do not write /getAllProjects, /addProject, /deleteProject/:id. The method already says the action.

    Tip

    Use AI carefully

    Ask ChatGPT, Gemini, or Claude to review your route plan before coding. Paste your app idea and ask for REST-style routes using nouns and HTTP methods.

    Check the output. Remove action names in paths. Keep query strings separate from route paths. Do not accept old Express 4 wildcard examples like /* for Express 5.

    Prompt:
    I am building an Express 5 API for a college hackathon project tracker.
    Suggest a simple route table using resource nouns, HTTP methods, and short descriptions.
    Do not use action words like getProject in the URL.
    Keep filters as query strings.
    Example

    Route table first

    Before writing code, make a small route table in your README or notes. This keeps your API consistent when you add middleware, body handling, and errors later.

    GET    /projects           List projects
    POST   /projects           Create a project
    GET    /projects/:id       Get one project
    PATCH  /projects/:id       Update one project
    DELETE /projects/:id       Delete one project
    GET    /projects?tech=node Filter projects by tech
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Notice the next() call. Without it, GET /health never reaches the route.

    import express from 'express';
    
    const app = express();
    
    function requestLogger(req, res, next) {
      console.log(`${req.method} ${req.path}`);
      next();
    }
    
    app.use(requestLogger);
    
    app.get('/health', (req, res) => {
      res.json({ status: 'ok' });
    });
    
    app.listen(3000, () => {
      console.log('API running on port 3000');
    });
    Your to-do

    Add one middleware

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Common mistake

    The hanging request

    The most common bug is writing middleware that neither responds nor calls next(). Your server is still running, but the request is stuck.

    This often happens when you add an if check and forget the else path.

    function brokenCheck(req, res, next) {
      if (req.headers['x-student-id']) {
        next();
      }
      // No response and no next() when the header is missing.
    }
    
    function fixedCheck(req, res, next) {
      if (!req.headers['x-student-id']) {
        return res.status(400).json({ error: 'x-student-id header is required' });
      }
    
      next();
    }
    Tip

    Use return

    When middleware sends a response, use return. It stops the function there and avoids accidentally calling next() later.

    This keeps your code clear during hackathon debugging, especially when you add checks quickly.

    function requireCollege(req, res, next) {
      if (!req.headers['x-college']) {
        return res.status(400).json({ error: 'x-college header is required' });
      }
    
      req.college = req.headers['x-college'];
      next();
    }
    Example

    Router-level middleware

    Use router-level middleware when only one group of routes needs a check. For example, project routes may require a team id, but health routes should stay open.

    This helps your project structure look intentional.

    import express from 'express';
    
    const app = express();
    const projectsRouter = express.Router();
    
    function requireTeamId(req, res, next) {
      if (!req.headers['x-team-id']) {
        return res.status(400).json({ error: 'x-team-id header is required' });
      }
    
      req.teamId = req.headers['x-team-id'];
      next();
    }
    
    projectsRouter.use(requireTeamId);
    
    projectsRouter.get('/', (req, res) => {
      res.json({ teamId: req.teamId, projects: [] });
    });
    
    app.get('/health', (req, res) => {
      res.json({ status: 'ok' });
    });
    
    app.use('/projects', projectsRouter);
    
    app.listen(3000);
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    4. Read Bodies, Queries, and Params

    4 min

    In 30 seconds
    • Read route params from req.params for IDs in the path.
    • Read filters and search text from req.query after the ? in a URL.
    • Read JSON form data from req.body only after adding a body parser.
    Key point

    Three input places

    Express gives you request data from three different places. Use req.params for path values, req.query for URL filters, and req.body for data sent in the request body.

    Do not use old req.param(name). Express 5 removed it. Being explicit makes your API easier to review in a recruiter repo.

    Params are path values

    A param is part of the route path. Use it for required identity values, like a student ID, project ID, or internship application ID.

    Example: GET /projects/42 should read 42 from req.params.projectId. Query strings do not decide which route matches.

    Queries are filters

    A query string starts after ?. Use it for optional choices like search, page, branch, year, or sort.

    Example: GET /projects?branch=cse&year=3 should read branch and year from req.query. Treat query values as user input, not trusted data.

    Common mistake

    Body is not automatic

    In Express 5, req.body can be undefined if no body parser has run. Do not assume it is an empty object.

    For JSON APIs, add express.json() before the routes that need JSON. You can apply it app-wide for a small learning API, or only on routes that accept bodies.

    Example

    One projects route

    This example reads all three places. It creates a project from the JSON body, uses the path param for the owner, and reads an optional query flag.

    Keep validation simple here. Later lessons can improve errors and status codes.

    import express from 'express';
    
    const app = express();
    
    app.use(express.json());
    
    app.get('/projects/:projectId', (req, res) => {
      const { projectId } = req.params;
      const { includeTeam } = req.query;
    
      res.json({
        projectId,
        includeTeam: includeTeam === 'true'
      });
    });
    
    app.post('/students/:studentId/projects', (req, res) => {
      const { studentId } = req.params;
      const { title, repoUrl } = req.body ?? {};
      const { source } = req.query;
    
      res.json({
        message: 'Project received',
        studentId,
        source: source ?? 'direct',
        project: {
          title,
          repoUrl
        }
      });
    });
    
    app.listen(3000, () => {
      console.log('API running on http://localhost:3000');
    });
    Your to-do

    Test with curl

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    curl "http://localhost:3000/projects/42?includeTeam=true"
    
    curl -X POST "http://localhost:3000/students/s101/projects?source=hackindia" \
      -H "Content-Type: application/json" \
      -d '{"title":"UPI Expense Tracker","repoUrl":"https://github.com/you/upi-expense-tracker"}'
    Tip

    Ask AI to inspect

    Paste one route into ChatGPT, Gemini, or Claude and ask: “List every value this route reads from params, query, and body. Point out any missing parser or unsafe assumption.”

    Check the answer. AI may suggest old Express patterns from older tutorials. Reject req.param(name), res.json(obj, status), and code that assumes req.body always exists.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    0 of 5 done

    Example

    Use status then json

    In Express 5, write the status first, then send JSON. Older tutorials may show removed shortcuts like res.json(obj, status). Do not use those.

    app.post('/students', express.json(), (req, res) => {
      const { name, college } = req.body;
    
      if (!name || !college) {
        return res.status(400).json({
          error: 'name and college are required'
        });
      }
    
      const student = {
        id: 'stu_101',
        name,
        college
      };
    
      return res.status(201).json({
        message: 'student created',
        student
      });
    });
    Example

    Not found example

    Use 404 when the URL is valid but the resource is missing. For example, the route exists, but that project ID is not in your data.

    app.get('/projects/:id', (req, res) => {
      const project = projects.find((item) => item.id === req.params.id);
    
      if (!project) {
        return res.status(404).json({
          error: 'project not found'
        });
      }
    
      return res.status(200).json({ project });
    });
    Common mistake

    Avoid fake success

    Do not return 200 with { error: 'missing name' }. That makes clients guess what happened.

    Also avoid using 500 for validation mistakes. If the user forgot a field, the server did not crash. The client sent a bad request, so use 400.

    Tip

    Ask AI carefully

    If you use ChatGPT, Gemini, or Claude to draft route handlers, ask it to include meaningful HTTP status codes. Then check every response yourself.

    Look for old Express patterns. In Express 5, use res.status(status).json(obj) and res.status(status).send(body).

    Prompt:
    Write an Express 5 route for POST /registrations.
    Use express.json(), validate name and email, return 400 for missing fields, 201 for created, and JSON responses.
    Do not use old res.json(obj, status) syntax.
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    6. Protect Config and Secrets

    4 min

    In 30 seconds
    • Load config from environment variables, not hard-coded strings.
    • Run your API with a local .env file using Node’s --env-file.
    • Keep .env out of Git and commit a safe .env.example instead.
    Key point

    One rule

    Secrets do not belong in code. Your API should read config from process.env, and each machine should provide its own values.

    This protects things like database URLs, JWT secrets, API keys, ports, and production settings.

    What counts as config

    Config is anything that may change between your laptop, a teammate’s laptop, and deployment. For this Express API, PORT and NODE_ENV are enough to practise the pattern.

    A secret is config that must not be public. Example: payment keys, email passwords, database passwords, and token signing secrets.

    Example

    Create env files

    Use a real .env file locally. Then create .env.example with fake safe values so a recruiter knows what to set before running your repo.

    .env
    PORT=3000
    NODE_ENV=development
    JWT_SECRET=replace-this-locally
    
    .env.example
    PORT=3000
    NODE_ENV=development
    JWT_SECRET=change-me
    Example

    Read process.env

    Keep config in one small file. This makes it easy to check required values when the app starts, instead of failing later during a request.

    // src/config.js
    export const config = {
      port: process.env.PORT || 3000,
      nodeEnv: process.env.NODE_ENV || 'development',
      jwtSecret: process.env.JWT_SECRET
    };
    
    export function checkConfig() {
      const required = ['JWT_SECRET'];
    
      for (const key of required) {
        if (!process.env[key]) {
          throw new Error(`Missing required environment variable: ${key}`);
        }
      }
    }
    Example

    Start with env

    Node.js can load variables from a file before your app starts. Use --env-file for your local run script.

    OS environment variables win over values from the env file. That is useful on deployment platforms.

    // package.json
    {
      "scripts": {
        "dev": "node --env-file=.env src/server.js",
        "start": "node src/server.js"
      }
    }
    
    // src/server.js
    import express from 'express';
    import { config, checkConfig } from './config.js';
    
    checkConfig();
    
    const app = express();
    
    app.get('/health', (req, res) => {
      res.status(200).json({ status: 'ok', env: config.nodeEnv });
    });
    
    app.listen(config.port, () => {
      console.log(`API running on port ${config.port}`);
    });
    Your to-do

    Keep secrets out

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    .gitignore
    node_modules
    .env
    Common mistake

    Common mistake

    Do not write const apiKey = 'real-key-here' in your route file. Even if the repo is private today, it can become public during a hackathon submission.

    Also do not depend on only your local .env. A recruiter should see .env.example and know exactly which variables are needed.

    Tip

    Ask AI safely

    You can ask ChatGPT or Gemini to draft a config.js file. Do not paste real secrets into the chat.

    Check that the answer uses process.env, validates required variables, and does not log secret values to the console.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Use next(error) when you create an error yourself, such as when a resource is not found.

    import express from 'express';
    
    const app = express();
    app.use(express.json());
    
    const students = [
      { id: '1', name: 'Asha', college: 'VIT' }
    ];
    
    app.get('/students/:id', async (req, res, next) => {
      const student = students.find((item) => item.id === req.params.id);
    
      if (!student) {
        const error = new Error('Student not found');
        error.status = 404;
        return next(error);
      }
    
      res.json({ data: student });
    });
    
    app.post('/students', async (req, res) => {
      if (!req.body?.name) {
        const error = new Error('Name is required');
        error.status = 400;
        throw error;
      }
    
      res.status(201).json({ data: req.body });
    });
    
    app.use((err, req, res, next) => {
      if (res.headersSent) {
        return next(err);
      }
    
      const status = err.status || err.statusCode || 500;
    
      res.status(status).json({
        error: {
          message: status === 500 ? 'Internal server error' : err.message,
          status
        }
      });
    });
    
    app.listen(process.env.PORT || 3000);
    Your to-do

    Add the handler

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Common mistake

    Do not leak stacks

    Stack traces help you debug, but they should not be shown to users. They can reveal file names, folders, and internal code details.

    For your project, return a safe message for 500 errors. Log the full error in your terminal while developing if you need it.

    app.use((err, req, res, next) => {
      console.error(err);
    
      const status = err.status || err.statusCode || 500;
      const message = status === 500 ? 'Internal server error' : err.message;
    
      res.status(status).json({ error: { message, status } });
    });
    Tip

    Bad JSON is covered

    If express.json() cannot parse the request body, it creates a parse error. These errors commonly carry a status like 400.

    That means your central handler can also format malformed JSON errors. Test it by sending broken JSON in Postman, Thunder Client, or curl.

    curl -X POST http://localhost:3000/students \
      -H "Content-Type: application/json" \
      -d '{ "name": '
    Common mistake

    Order matters

    If your error handler is above your routes, it will not catch route errors. Express runs middleware in order.

    Keep normal middleware first, then routes, then the final error handler.

    app.use(express.json());
    
    app.use('/students', studentsRouter);
    app.use('/projects', projectsRouter);
    
    app.use((err, req, res, next) => {
      // final error response here
    });
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    8. Publish Your API Repo

    4 min

    In 30 seconds
    • Prepare your API repo so a recruiter can run it without guessing.
    • Add clear scripts, env examples, and a short README with test requests.
    • Submit a public HackIndia project link that proves your Express API skill.
    Key point

    One job

    Your repo must answer one recruiter question fast: can I run this API and see the required backend skills?

    Do not make them inspect every file. Put the run steps, env setup, routes, and example requests in the README.

    What recruiters check

    They will usually scan your folder structure, package scripts, README, and routes before reading deeper code. Your project should show Express 5 routes, middleware, body and query handling, status codes, env variables, and one centralized error handler.

    Use Node.js 24 LTS for this track. Node.js 26 is the current non-LTS line, but production-oriented beginner projects should use an LTS release.

    Your to-do

    Clean the repo

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Example

    Useful files

    A simple structure is enough. Make the important parts easy to find.

    your-api/
      src/
        app.js
        server.js
        routes/
          jobs.routes.js
        middleware/
          requestLogger.js
          errorHandler.js
        controllers/
          jobs.controller.js
      .env.example
      .gitignore
      package.json
      README.md
    Example

    README starter

    Write your README for someone who has your GitHub link and five minutes. Include exact commands and a few sample requests.

    # Campus Jobs API
    
    Express 5 API built with Node.js 24 LTS.
    
    ## Features
    - Routes for jobs
    - Router-level middleware
    - Request body, query, and params handling
    - Meaningful HTTP status codes
    - Environment variables
    - Centralized error handling
    
    ## Run locally
    1. Install dependencies
       npm install
    
    2. Create env file
       cp .env.example .env
    
    3. Start the API
       npm run dev
    
    ## Environment variables
    PORT=3000
    NODE_ENV=development
    
    ## Example requests
    GET /api/jobs?location=Pune
    GET /api/jobs/1
    POST /api/jobs
    Body: { "title": "Backend Intern", "company": "Demo Startup" }
    
    ## Tech
    Node.js 24 LTS, Express 5
    Tip

    Use AI carefully

    Ask an AI tool to review your README like a recruiter. Prompt it with your folder tree and ask: “Can you run this API from these instructions? What is missing?”

    Check every suggestion. AI may invent scripts, routes, files, or commands that do not exist in your repo.

    Common mistake

    Common publish mistakes

    Do not commit your real .env file. Commit .env.example instead. Also avoid README claims that your code does not prove.

    Do not use old Express 4 response examples like res.json(obj, status). In Express 5, use res.status(status).json(obj).

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done