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 stringauthors: non-empty string (comma-separated list of authors)published_in: non-empty stringyear: integer greater than 1900
- Automatic fields:
id: automatically generated primary keycreated_at: automatically set to current timestamp by databaseupdated_at: automatically set to current timestamp by database
- Input Validation:
- Ensure all required fields are present, not empty, not whitespace-only
- Validate
yearis 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 timestamps400 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"
}Timestamps in responses must be in ISO 8601 format. More details of timestamp format can be found in Implementation Notes
Error Response:
-
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" ] } -
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 matchFor 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:
limitcontrols how many results are returnedoffsetcontrols how many results are skipped before returning data
For example:
GET /api/papers?limit=10&offset=0→ return the first 10 papersGET /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. yearmust be exact matchpublished_inmust be case-insensitive partial matchlimitdefaults to 10 if not providedoffsetdefaults 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:
-
Validate ID format:
- Must be a positive integer
-
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:
-
Invalid ID Format (400 Bad Request):
{ "error": "Validation Error", "message": "Invalid ID format" } -
Paper Not Found (404 Not Found):
{ "error": "Paper not found" }
PUT /api/papers/:id
Updates an existing paper by ID.
Input Validation:
idmust be a positive integer and exist- All request body validation rules from POST apply
Validation Process:
- Validate
id - Validate request body
- 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_attimestamp is automatically updatedcreated_attimestamp remains unchanged
{
"id": "integer",
"title": "string",
"authors": "string",
"published_in": "string",
"year": "integer",
"created_at": "timestamp",
"updated_at": "timestamp"
}Error Response (400 Bad Request):
-
Invalid ID Format (400 Bad Request):
{ "error": "Validation Error", "message": "Invalid ID format" } -
Missing Required Fields (400 Bad Request):
{ "error": "Validation Error", "messages": [ "Title is required", "Authors are required", "Published venue is required", "Published year is required" ] } -
Invalid Year (400 Bad Request):
{ "error": "Validation Error", "messages": ["Valid year after 1900 is required"] } -
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:
idmust be a positive integeridmust exist in the database
Success Response:
- Status: 204 No Content
- No response body
Error Responses:
-
Invalid ID Format (400 Bad Request):
{ "error": "Validation Error", "message": "Invalid ID format" } -
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
Zsuffix (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
.000Zfor 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.
- SQLite provides timestamps with second-level precision. When converting them to ISO 8601, it is acceptable to append
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.
" ")
- Empty strings (
- Invalid
yearvalues 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)
- Non-numeric strings (e.g.
URL / Query Parameter Validation
-
Invalid
idvalues include:- Non-numeric strings (e.g.
"abc") - Partially numeric strings (e.g.
"1a") - Decimal numbers
- Values less than or equal to 0
- Non-numeric strings (e.g.
-
Invalid
limitvalues include:- Non-numeric values
- Decimal numbers
- Values less than or equal to 0
- Values greater than 100
-
Invalid
offsetvalues 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
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
-
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.gzAlternatively, 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.jsonYou need to implement the following:
- Database operations in
database.js - Route handlers in
routes.js - Middleware functions in
middleware.js
The
server.jsfile is fully configured and should not be modified. - Database operations in
-
After extracting the archive, install dependencies:
npm installNote on Installation Warnings
During
npm install, you may see warning messages such asnpm warn deprecatedornpm auditnotices. 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.
-
Start the development server:
npm run devThis starts the server using
nodemon, which automatically restarts the application when you make changes to the code.
Development Tips
-
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
-
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
-
Start your application locally
npm start -
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.
-
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 -
Run the provided test script
npm test -- a1.sample.test.js -
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.jsso that Jest recognizes them as test files. -
Run all test files (all files whose filenames end with
.test.js)npm test -
Run tests in watch mode
npm test -- --watchWatch mode automatically reruns tests when files change, which is especially useful during development.
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-1folder, create a file calledreasoning.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.mdis 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
1234567890with your student number. - The archive must contain exactly one top-level folder named
assignment-1with the required files inside. - Do not include
node_modules/. The auto-grader will runnpm installto install all dependencies specified inpackage.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.
-
Download
verify_a1_submission.pyto the same directory as your.tar.gzfile:curl -o verify_a1_submission.py https://www.eecg.utoronto.ca/~cying/courses/ece1724-web/assignments/assignment-1/verify_a1_submission.pyAlternatively, download the script here.
-
Ensure you have Python installed. If not, install it from python.org .
-
From the same directory as your
1234567890-a1.tar.gz, run:python verify_a1_submission.py 1234567890-a1.tar.gz- Replace
1234567890with your student number.
- Replace
Submit to Quercus
Submit your .tar.gz file to Quercus .
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)
- Table creation with correct schema (5)
API Endpoints (95 points)
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_atis in ISO 8601 format (1),updated_atis in ISO 8601 format (1)
- Error handling and validation (11)
- Status code is 400 (1)
- Missing required fields (3)
titleis not provided (1)titleis an empty string""(1)titlecontains 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)
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_atis in ISO 8601 format (1),updated_atis in ISO 8601 format (1)
- Filtering and pagination (8)
yearfilter (2)published_incase-insensitive partial match (2)limitandoffset(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)
- Invalid
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_atis in ISO 8601 format (1),updated_atis in ISO 8601 format (1)
- Error handling (5)
- Invalid
id(3)-1(1)abc(1)1a(1)
- Not found error (2)
- Invalid
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_atis different fromcreated_at(1)updated_atis 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)
- Invalid
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)
- Invalid
Resources
- Course Materials:
- Express Documentation
- SQLite Documentation
Questions?
-
Discussion Board:
- Post questions on course discussion board
- Search existing discussions first
- Use clear titles and provide relevant code snippets
-
Office Hours:
- Time: Wednesdays, 4:00 PM — 5:00 PM
- Location: Room 7206, Bahen Centre for Information Technology
-
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