Skip to Content
AssignmentsAssignment 1

Assignment 1: Basic Backend Development

Release Date: January 18, 2026

Due Date: February 1, 2026, 11:59 PM EST

Weight: 12.5% of final grade

Overview

In this assignment, you will build the backend foundation of the Paper Management System by implementing a RESTful API using Express with SQLite as the database. You will create endpoints to manage paper metadata and implement proper error handling.

Learning Objectives

After completing this assignment, you will be able to:

  • Build RESTful APIs using Express
  • Design and implement database operations using SQLite
  • Handle HTTP requests and responses
  • Implement basic and clearly defined input validation
  • Reason about backend control flow and error handling

Requirements

Before you start coding

After reading the assignment requirements below, pause and think about what you expect will be the hardest part of this assignment. This will help you answer Q1 in reasoning.md, which you will submit together with your code.

See the AI Usage Policy for details.

Database Schema

Your implementation should create a SQLite database named paper_management.db in the root directory with the following schema:

CREATE TABLE papers ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, authors TEXT NOT NULL, published_in TEXT NOT NULL, year INTEGER NOT NULL CHECK (year > 1900), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP );
  • The table creation should be implemented in database.js
  • Timestamps are automatically managed by SQLite

API Endpoints

Implement the following API endpoints:

POST /api/papers

Create a new paper.

  • Required fields:
    • title: non-empty string
    • authors: non-empty string (comma-separated list of authors)
    • published_in: non-empty string
    • year: integer greater than 1900
  • Automatic fields:
    • id: automatically generated primary key
    • created_at: automatically set to current timestamp by database
    • updated_at: automatically set to current timestamp by database
  • Input Validation:
    • Ensure all required fields are present, not empty, not whitespace-only
    • Validate year is a valid integer greater than 1900
    • If multiple validation errors exist, return all of them together
  • Responses:
    • 201 Created: Returns the created paper with ID and timestamps
    • 400 Bad Request: Validation errors

Request Body:

{ "title": "Required, string", "authors": "Required, string", "published_in": "Required, string", "year": "Required, integer > 1900" }

An Request Example:

{ "title": "Example Paper Title", "authors": "John Doe, Jane Smith", "published_in": "ICSE 2025", "year": 2025 }

Success Response:

  • Status: 201 Created
  • Returns the created paper with all fields including automatic ones
{ "id": "Auto-generated, integer", "title": "string", "authors": "string", "published_in": "string", "year": "integer", "created_at": "timestamp", "updated_at": "timestamp" }

A Success Response Example:

{ "id": 1, "title": "Example Paper Title", "authors": "John Doe, Jane Smith", "published_in": "ICSE 2025", "year": 2025, "created_at": "2026-01-19T10:30:00.000Z", "updated_at": "2026-01-19T10:30:00.000Z" }
Note

Timestamps in responses must be in ISO 8601 format. More details of timestamp format can be found in Implementation Notes

Error Response:

  1. Missing Required Fields (400 Bad Request):

    Return all validation errors in the messages array to help API users fix all issues at once. The following is an example when all the four required fields are missing:

    { "error": "Validation Error", "messages": [ "Title is required", "Authors are required", "Published venue is required", "Published year is required" ] }
  2. Invalid Year (400 Bad Request):

    { "error": "Validation Error", "messages": ["Valid year after 1900 is required"] }

GET /api/papers

Retrieve a list of papers with optional filtering and pagination.

Supported Query Parameters:

  • year: integer - Filter papers by exact year match

  • published_in: string - Filter by case-insensitive partial match

    For example:

    • GET /api/papers?published_in=icse
      → Matches papers published in venues such as "ICSE 2024", "ICSE 2025", "icse workshop"

    • GET /api/papers?published_in=NeurIPS
      → Matches "NeurIPS 2023", "neurips 2024", "NeurIPS (Datasets & Benchmarks)"

  • limit: integer - Maximum number of results (default: 10, max: 100)

  • offset: integer - Number of results to skip (default: 0)

Note on limit and offset (Pagination)

limit and offset are commonly used for pagination in APIs:

  • limit controls how many results are returned
  • offset controls how many results are skipped before returning data

For example:

  • GET /api/papers?limit=10&offset=0 → return the first 10 papers
  • GET /api/papers?limit=10&offset=2 → skip the first 2 papers and return the next 10

Input Validation:

  • year:
    • Must be an integer greater than 1900
  • limit:
    • Must be a positive integer
    • Maximum value: 100
    • Default: 10
  • offset:
    • Must be a non-negative integer
    • Default: 0

Filter Behavior:

  • Multiple filters are combined using AND logic.
  • If no filters are provided, all papers are returned (subject to pagination).
  • If no papers match the specified filters, an empty array ([]) is returned.
  • year must be exact match
  • published_in must be case-insensitive partial match
  • limit defaults to 10 if not provided
  • offset defaults to 0 if not provided

Success Response:

  • Status: 200 OK
  • Returns an array of paper objects
[ { "id": "Auto-generated, integer", "title": "string", "authors": "string", "published_in": "string", "year": "integer", "created_at": "timestamp", "updated_at": "timestamp" } // ... more papers ]

Error Response:

Invalid Query Parameters (400 Bad Request):

{ "error": "Validation Error", "message": "Invalid query parameter format" }

Note: Simplified Error Handling

Error handling for invalid query parameters is simplfied in this assignment. Whether there is one invalid parameter or multiple invalid parameters, the response will always be:

{ "error": "Validation Error", "message": "Invalid query parameter format" }

For example, both these requests:

  • GET /api/papers?year=1000 (single invalid parameter)
  • GET /api/papers?year=1000&offset=-1 (multiple invalid parameters)

will receive the same error response shown above.

GET /api/papers/:id

Retrieves a specific paper by ID.

URL Parameters:

  • id: integer - The ID of the paper to retrieve

Validation Process:

  1. Validate ID format:

    • Must be a positive integer
  2. Check paper existence:

    • Must exist in the database

Success Response:

  • Status: 200 OK
  • Returns a single paper object
{ "id": "integer", "title": "string", "authors": "string", "published_in": "string", "year": "integer", "created_at": "timestamp", "updated_at": "timestamp" }

Error Responses:

  1. Invalid ID Format (400 Bad Request):

    { "error": "Validation Error", "message": "Invalid ID format" }
  2. Paper Not Found (404 Not Found):

    { "error": "Paper not found" }

PUT /api/papers/:id

Updates an existing paper by ID.

Input Validation:

  • id must be a positive integer and exist
  • All request body validation rules from POST apply

Validation Process:

  1. Validate id
  2. Validate request body
  3. Check existence

Request Body:

{ "title": "Required, string", "authors": "Required, string", "published_in": "Required, string", "year": "Required, integer > 1900" }

A Request Example:

{ "title": "Updated Paper Title", "authors": "John Doe, Jane Smith", "published_in": "IEEE TSE", "year": 2025 }

Success Response:

  • Status: 200 OK
  • Returns the updated paper with all fields
  • updated_at timestamp is automatically updated
  • created_at timestamp remains unchanged
{ "id": "integer", "title": "string", "authors": "string", "published_in": "string", "year": "integer", "created_at": "timestamp", "updated_at": "timestamp" }

Error Response (400 Bad Request):

  1. Invalid ID Format (400 Bad Request):

    { "error": "Validation Error", "message": "Invalid ID format" }
  2. Missing Required Fields (400 Bad Request):

    { "error": "Validation Error", "messages": [ "Title is required", "Authors are required", "Published venue is required", "Published year is required" ] }
  3. Invalid Year (400 Bad Request):

    { "error": "Validation Error", "messages": ["Valid year after 1900 is required"] }
  4. Paper Not Found (404 Not Found):

    { "error": "Paper not found" }

Note: Validation Processing Logic

When handling PUT requests with multiple potential errors (e.g. an invalid ID and an invalid request body), always validate the ID first before checking the request body. This is because if the ID is invalid, there’s no need to validate the request body since the resource cannot be identified.

For example: A PUT /api/papers/0 request with the request body

{ "authors": "John Doe", "published_in": "ICSE 2025", "year": 2025 // missing title }

The response should be:

{ "error": "Validation Error", "message": "Invalid ID format" }

Since the ID validation fails first, the request body is not checked.

DELETE /api/papers/:id

Deletes a paper by ID.

Input Validation:

  • id must be a positive integer
  • id must exist in the database

Success Response:

  • Status: 204 No Content
  • No response body

Error Responses:

  1. Invalid ID Format (400 Bad Request):

    { "error": "Validation Error", "message": "Invalid ID format" }
  2. Paper Not Found (404 Not Found):

    { "error": "Paper not found" }

Implementation Notes

Response Requirements

  • All responses must follow the JSON formats specified in this handout.
  • All timestamps in API responses must be in ISO 8601 format and use the UTC timezone, indicated by the Z suffix (e.g. “2026-01-19T10:30:00.000Z”).
    • SQLite provides timestamps with second-level precision. When converting them to ISO 8601, it is acceptable to append .000Z for the milliseconds portion. Millisecond precision is not required, and you do not need to generate or simulate millisecond-level timestamps.
    • For this assignment, we only verify that timestamps are valid ISO 8601 UTC strings. Accurate timezone conversion and handling are out of scope for Assignment 1.

Validation Scope

This assignment introduces input validation at a conceptual level. You are not expected to handle every edge case that would exist in a real-world backend system.

You are required to implement only the validation rules explicitly listed in this handout. The autograder will test only the cases described in Requirements section and as In Scope below. Any behavior not mentioned should be considered out of scope and will not be tested.

In Scope

Request Body Validation

  • All required fields must be present.
  • Required fields must not be
    • Empty strings ("")
    • Strings containing only whitespace (e.g. " ")
  • Invalid year values include:
    • Non-numeric strings (e.g. "abc")
    • Partially numeric strings (e.g. "2000a")
    • Numbers less than or equal to 1900
    • Decimal numbers (e.g. 1901.5)

URL / Query Parameter Validation

  • Invalid id values include:

    • Non-numeric strings (e.g. "abc")
    • Partially numeric strings (e.g. "1a")
    • Decimal numbers
    • Values less than or equal to 0
  • Invalid limit values include:

    • Non-numeric values
    • Decimal numbers
    • Values less than or equal to 0
    • Values greater than 100
  • Invalid offset values include:

    • Non-numeric values
    • Decimal numbers
    • Negative values

Out of Scope

You are not required to handle the following:

  • Range queries (e.g. year=2019-2026)
  • SQL injection defenses beyond basic validation
  • Trimming, normalizing, or sanitizing strings unless explicitly required
  • Any validation logic not explicitly listed
Note

In real-world backend systems, validation is far more complex. In this assignment, we deliberately limit the scope so you can focus on foundational backend concepts. That said, you should still be mindful of potential edge cases and develop the habit of thinking about how they would be handled in production systems.

Getting Started

  1. Download the starter code archive:

    curl -o starter-code.tar.gz https://www.eecg.utoronto.ca/~cying/courses/ece1724-web/assignments/assignment-1/starter-code.tar.gz

    Alternatively, download the archive here.

    Extract the archive, you should see the following project structure:

    assignment-1/ ├── src/ │ ├── server.js # Express application setup (DO NOT MODIFY) │ ├── database.js # Database operations (TODO) │ ├── routes.js # API routes (TODO) │ └── middleware.js # Custom middleware (TODO) └── package.json

    You need to implement the following:

    • Database operations in database.js
    • Route handlers in routes.js
    • Middleware functions in middleware.js

    The server.js file is fully configured and should not be modified.

  2. After extracting the archive, install dependencies:

    npm install

    Note on Installation Warnings

    During npm install, you may see warning messages such as npm warn deprecated or npm audit notices. These come from indirect development dependencies used by the Node.js toolchain and do not affect the correctness, functionality, or grading of this assignment.

    You may safely ignore these warnings.

  3. Start the development server:

    npm run dev

    This starts the server using nodemon, which automatically restarts the application when you make changes to the code.

Development Tips

  1. Systematic Approach:

    • Start with database schema implementation
    • Implement endpoints one at a time
    • Test each endpoint thoroughly before moving to next
    • Follow the provided TODOs and hints
  2. Common Pitfalls to Avoid:

    • Not following error response formats exactly
    • Missing input validation
    • Incorrect HTTP status codes
    • Not handling all error cases

Testing Your Implementation

Manual Testing

  1. Start your application locally

    npm start
  2. Test your API endpoints

    Use tools such as cURL, Thunder Client , or Postman  to manually test your API endpoints.

Sample Test Cases

To verify that your implementation meets the assignment requirements, use the provided test script, a1.sample.test.js. This provided script focuses on core functionality and corresponds to approximately 80% of the autograder score.

The autograder will run similar but more comprehensive tests that check all explicitly stated requirements in this handout, and it will not test anything beyond what is specified.

  1. Download the test script

    From a terminal, navigate into your project directory and run:

    curl -o a1.sample.test.js https://www.eecg.utoronto.ca/~cying/courses/ece1724-web/assignments/assignment-1/a1.sample.test.js
  2. Run the provided test script

    npm test -- a1.sample.test.js
  3. Write your own tests

    The provided sample test does not cover all requirements listed in this handout, including:

    • Some error scenarios specified in the validation requirements
    • Filtering and pagination behavior

    You are therefore encouraged to write additional test cases to thoroughly validate all required functionality described in this handout.

    You may add your own tests directly to a1.sample.test.js, or create separate test files with filenames ending in .test.js so that Jest  recognizes them as test files.

  4. Run all test files (all files whose filenames end with .test.js)

    npm test
  5. Run tests in watch mode

    npm test -- --watch

    Watch mode automatically reruns tests when files change, which is especially useful during development.

Tip

The auto-grader will check for exact error messages. Ensure that your error responses match the formats and wording specified in this handout exactly.

Submission Instructions

Please follow the steps below to submit a .tar.gz archive to Quercus.

Prepare reasoning.md

In addition to the required code files, you must submit a short reflection file named reasoning.md.

  • Create a folder called assignment-1.
  • Inside the assignment-1 folder, create a file called reasoning.md.
  • Answer the three required questions described in the AI Usage Policy page.

Important Notes

  • Keep your responses brief and specific, with a total length of no more than 300 words
  • This file is intended to capture your genuine thinking process. It is not a long writing assignment required polishing.
  • reasoning.md is worth 5% of this assignment grade. Its grading rubric is on the AI Usage Policy page.

Generate the Archive

Make your assignment-1 directory have exactly the following structure:

      • database.js
      • middleware.js
      • routes.js
      • server.js
    • package.json
    • reasoning.md

From the parent directory that contains the assignment-1 folder, create the submission archive:

tar zcvf 1234567890-a1.tar.gz --exclude='.DS_Store' assignment-1
  • Replace 1234567890 with your student number.
  • The archive must contain exactly one top-level folder named assignment-1 with the required files inside.
  • Do not include node_modules/. The auto-grader will run npm install to install all dependencies specified in package.json.

Any submission that fails to meet the specified format or structure and requires TA intervention for the autograder to work will receive a 20-point deduction.

Verify Your Submission

To avoid a 20-point deduction, use the provided Python script to verify your submission.

  1. Download verify_a1_submission.py to the same directory as your .tar.gz file:

    curl -o verify_a1_submission.py https://www.eecg.utoronto.ca/~cying/courses/ece1724-web/assignments/assignment-1/verify_a1_submission.py

    Alternatively, download the script here.

  2. Ensure you have Python installed. If not, install it from python.org .

  3. From the same directory as your 1234567890-a1.tar.gz, run:

    python verify_a1_submission.py 1234567890-a1.tar.gz
    • Replace 1234567890 with your student number.

Submit to Quercus

Submit your .tar.gz file to Quercus .

Note

You are allowed to submit unlimited times. Only your latest submission before the deadline will be graded. Quercus automatically appends a suffix to the file name after the first submission — this will not affect grading.

Grading Scheme (100 points)

The total 100 points correspond to 95% of this assignment’s grade. The remaining 5% comes from your reasoning.md submission.

Grades were released on February 6. If you would like to request a remark, please do so by February 20, following the instructions in the syllabus.

Database Operations (5 points)

  1. Table creation with correct schema (5)

API Endpoints (95 points)

  1. POST /api/papers (21)
  • Successful creation (10)
    • Status code is 201 (1)
    • Correct title (1), authors (1), published_in (1), year (1), id (1)
    • Has created_at (1), updated_at (1)
    • created_at is in ISO 8601 format (1), updated_at is in ISO 8601 format (1)
  • Error handling and validation (11)
    • Status code is 400 (1)
    • Missing required fields (3)
      • title is not provided (1)
      • title is an empty string "" (1)
      • title contains only whitespace " " (1)
    • Invalid year (3)
      • 1900 (1)
      • "a" (1)
      • 1901.1 (1)
    • Multiple validation errors (4)
      • Test with an empty request body. Returns all four required error messages (1 point per message)
  1. GET /api/papers (31)
  • Basic retrieval (11)
    • Status code is 200 (1)
    • Response is a list (1)
    • Correct id (1), title (1), authors (1), published_in (1), year (1)
    • Has created_at (1), updated_at (1)
    • created_at is in ISO 8601 format (1), updated_at is in ISO 8601 format (1)
  • Filtering and pagination (8)
    • year filter (2)
    • published_in case-insensitive partial match (2)
    • limit and offset (2)
    • Default limit (2)
  • Error handling (12)
    • Invalid year (3)
      • 1900 (1)
      • 1901a (1)
      • 1901.1 (1)
    • Invalid limit (4)
      • 0 (1)
      • abc (1)
      • 90.5 (1)
      • 101 (1)
    • Invalid offset (3)
      • -1 (1)
      • 1a (1)
      • 1.5 (1)
    • Multiple invalid query parameters (2)
  1. GET /api/papers/:id (15)
  • Successful retrieval (10)
    • Status code is 200 (1)
    • Correct id (1), title (1), authors (1), published_in (1), year (1)
    • Has created_at (1), updated_at (1)
    • created_at is in ISO 8601 format (1), updated_at is in ISO 8601 format (1)
  • Error handling (5)
    • Invalid id (3)
      • -1 (1)
      • abc (1)
      • 1a (1)
    • Not found error (2)
  1. PUT /api/papers/:id (19)
  • Successful update (8)
    • Status code is 200 (1)
    • Correct id (1), title (1), authors (1), published_in (1), year (1)
    • updated_at is different from created_at (1)
    • updated_at is ISO 8601 format (1)
  • Error handling and validation (11)
    • Invalid id (3)
      • 0 (1)
      • 1.5 (1)
      • abc (1)
    • Missing fields (3)
      • Not provided (1)
      • Empty string "" (1)
      • String of only spaces " " (1)
    • Invalid year (1)
      • 1900 (1)
    • Validate ID first before request body (2)
    • Not found error (2)
  1. DELETE /api/papers/:id (9)
  • Successful deletion (5)
    • Status code is 204 (1)
    • No response body (1)
    • Paper is not in database anymore (3)
  • Error handling (4)
    • Invalid id (2)
      • abc (1)
      • 1a (1)
    • Not found error (2)

Resources

Questions?

  1. Discussion Board:

    • Post questions on course discussion board 
    • Search existing discussions first
    • Use clear titles and provide relevant code snippets
  2. Office Hours:

    • Time: Wednesdays, 4:00 PM — 5:00 PM
    • Location: Room 7206, Bahen Centre for Information Technology
  3. Tips for Getting Help:

    • Start early to allow time for questions
    • Be specific about your problem
    • Share what you’ve tried
    • Include relevant error messages
Last updated on