API Route 404 Fix
API Route 404 Fix
From the screenshot, it looks like your request is reaching the server but the server doesn’t
recognize the route you’re calling ( /api/v1 ). In other words, there’s likely no handler defined
for that path in your code. Here are a few things to check:
[Link]('/api/v1', someRouter);
If you see that, then inside someRouter , make sure you have a route for "/" or
whatever sub-path you intend to call. For example:
js
1/33
3. Confirm the HTTP method
Make sure your server code is actually handling a POST on that route if you’re
sending a POST request. If your server only has a GET route, a POST request will
also result in a 404 (or method not allowed, depending on your setup).
If this is deployed on Railway, ensure your environment variables (if any) and your
start command are set correctly. Sometimes the local environment route works but
the deployed one doesn’t if the path is not exported or if there’s a different prefix.
This can help confirm that your app is running and that the request is truly hitting a
non-existent route.
Check your server logs on Railway to see if you get any errors about missing routes
or other deployment issues.
In short, you’ll want to make sure the path in your request matches exactly the path you’ve
defined in your code—both in terms of URL and HTTP method. Once you confirm they
match, the 404 should go away. If you’re still stuck, try sharing your route definitions
(anonymizing any sensitive info) so we can pinpoint the mismatch.
2/33
From the screenshot of your [Link] , the key detail is that you only have a GET route defined
at /api/v1 :
ts
You’re not defining any POST handler at /api/v1 . That means if you try to POST to
/api/v1 , Express won’t find a matching route—hence the 404.
ts
Otherwise, if you want to create resources like products, orders, etc., you already have:
ts
[Link]('/api/v1/products', productRouter);
[Link]('/api/v1/users', userRouter);
[Link]('/api/v1/orders', orderRouter);
[Link]('/api/v1/discounts', discountRouter);
3/33
Summary
404 means the path you’re hitting doesn’t match any defined route/method.
If you need a POST route at /api/v1 , define it explicitly. Otherwise, use the resource-
based endpoints (e.g. POST /api/v1/products ) you set up in your routers.
Based on your discountRoutes screenshot, it looks like the POST route is defined at:
arduino
[Link]('/', createDiscount);
bash
POST /api/v1/discounts
So if you’re trying to create a discount, make sure you send your request to that exact
endpoint. For example, in Postman (or wherever you’re testing):
1. Method: POST
2. URL: [Link]
If you send a POST request to /api/v1 instead, you’ll get a 404 because there’s no POST
route defined at /api/v1 in your code.
4/33
what about get and put
From the screenshot, your discountRouter has these routes (based on typical REST patterns
and naming conventions):
5. DELETE a discount
ts
[Link]('/api/v1/discounts', discountRouter);
That means all of these routes are prefixed with /api/v1/discounts . So for:
… and so on.
5/33
Just make sure you replace 123 with the actual discount ID you want to retrieve or update.
arduino
ts
[Link]('/api/v1/discounts', discountRouter);
How to fix
Make sure you’re calling exactly:
bash
GET [Link]
ts
6/33
[Link]('/', getDiscounts); // for GET /api/v1/discounts
[Link]('/:id', getDiscount); // for GET /api/v1/discounts/:id
// ...
bash
GET /api/v1/discounts
2/2
7/33
const discount = await [Link]({
code,
description,
discountPercentage,
discountAmount,
startDate,
endDate,
maxUses,
});
[Link](201).json(discount);
} catch (error) {
next(error);
}
};
8/33
try {
const { code } = [Link];
const updateData = [Link];
const discount = await [Link]({
code }, updateData, {
new: true,
});
if (!discount) {
[Link](404).json({ message: "Discount code not
found" });
return;
}
[Link](discount);
} catch (error) {
next(error);
}
};
9/33
req: Request,
res: Response,
next: NextFunction
) => {
try {
const { code } = [Link];
const discount = await [Link]({ code });
if (!discount) {
[Link](404).json({ message: "Discount code not
found" });
return;
}
10/33
next(error);
}
};
Below is a quick rundown of how each controller function works and the typical routes you’d
assign them to in your [Link] (or similar) file. This will help you confirm how to
call them from your client (e.g., Postman, front-end app) and how they interact with your
Discount model in MongoDB.
{
code,
description,
discountPercentage,
discountAmount,
startDate,
endDate,
maxUses
}
Checks if a discount with the same code already exists. If yes, returns 400 with
"Discount code already exists" .
Otherwise, creates a new Discount document in MongoDB and returns 201 with the
newly created discount.
Typical Route
ts
// [Link]
[Link]('/', createDiscount);
11/33
How to call (Postman example)
bash
POST /api/v1/discounts
Content-Type: application/json
{
"code": "SUMMER2025",
"description": "Summer sale 2025",
"discountPercentage": 10,
"startDate": "2025-06-01",
"endDate": "2025-07-01",
"maxUses": 100
}
Typical Route
ts
[Link]('/:code', getDiscount);
bash
GET /api/v1/discounts/SUMMER2025
12/33
3. Update a discount by code – updateDiscount
Code summary
Takes all fields from [Link] ( updateData ) and updates the discount in MongoDB
using findOneAndUpdate .
Typical Route
ts
[Link]('/:code', updateDiscount);
bash
PUT /api/v1/discounts/SUMMER2025
Content-Type: application/json
{
"description": "Updated summer sale",
"discountPercentage": 15
}
13/33
Typical Route
ts
[Link]('/:code', deleteDiscount);
bash
DELETE /api/v1/discounts/SUMMER2025
If valid, increments currentUses by 1 and returns a success message along with the
discount data.
Typical Route
ts
[Link]('/apply', applyDiscount);
bash
POST /api/v1/discounts/apply
Content-Type: application/json
14/33
"code": "SUMMER2025"
}
ts
// POST /api/v1/discounts
[Link]('/', createDiscount);
// GET /api/v1/discounts/:code
[Link]('/:code', getDiscount);
// PUT /api/v1/discounts/:code
[Link]('/:code', updateDiscount);
// DELETE /api/v1/discounts/:code
[Link]('/:code', deleteDiscount);
// POST /api/v1/discounts/apply
[Link]('/apply', applyDiscount);
15/33
ts
// ...
[Link]('/api/v1/discounts', discountRouter);
With this setup, you can call the endpoints from Postman or your front-end exactly as shown
in the examples above (make sure you match the method, path, and body format).
2. Check the HTTP Method: For updating, use PUT ; for creating, use POST ; etc.
That’s it! With this structure, you should be able to create, retrieve, update, delete, and
apply discount codes successfully.
16/33
Create a Discount Code
Method Endpoint Description Request Body Response
17/33
Method Endpoint Description Request Body Response
Note:
18/33
1. Replace SUMMER2025 and other example values with the actual data you plan to use.
2. If your Express server is case-sensitive for routes, ensure you use the exact lowercase or
uppercase paths you defined.
3. Ensure you send the request body as valid JSON ( Content-Type: application/json ).
how to do that ?
reates a new Discount document in MongoDB and returns
201 with the newly created discount.
Here’s a quick rundown of how you can create a new Discount document in MongoDB using
Mongoose and return an HTTP 201 status code along with the newly created discount:
3. (Optional) Check for duplicates to ensure a discount with the same code doesn’t
already exist.
5. Send a 201 response with the newly created discount document if everything goes well.
6. Handle errors in a try/catch block and pass them to your error handler via
next(error) .
js
19/33
// Check if the discount code already exists
const existing = await [Link]({ code });
if (existing) {
return [Link](400).json({ message: "Discount code already exists" });
}
Key points:
Ensure your request body ( [Link] ) includes all necessary fields (e.g., code ,
discountPercentage , etc.).
Make sure your Mongoose model ( Discount ) matches the fields you’re sending.
20/33
) => {
try {
const {
code,
description,
discountPercentage,
discountAmount,
startDate,
endDate,
maxUses,
} = [Link];
[Link](201).json(discount);
} catch (error) {
next(error);
}
};
21/33
try {
const { code } = [Link];
const discount = await [Link]({ code });
if (!discount) {
[Link](404).json({ message: "Discount code not
found" });
return;
}
[Link](discount);
} catch (error) {
next(error);
}
};
22/33
next: NextFunction
) => {
try {
const { code } = [Link];
const discount = await [Link]({
code });
if (!discount) {
[Link](404).json({ message: "Discount code not
found" });
return;
}
[Link]({ message: "Discount code deleted successfully"
});
} catch (error) {
next(error);
}
};
23/33
expired or inactive" });
return;
}
Below is a step-by-step explanation of how the createDiscount function (from your code
snippet) creates a new Discount document in MongoDB and returns a 201 (Created) status
with the newly created discount.
// [Link]
import { createDiscount } from '../controllers/discountController';
24/33
[Link]('/', createDiscount);
So when the client sends a POST request to /api/v1/discounts (assuming your app
uses [Link]('/api/v1/discounts', discountRouter); ), this function runs.
ts
const {
code,
description,
discountPercentage,
discountAmount,
startDate,
endDate,
maxUses,
} = [Link];
The function reads these fields from the request body ( [Link] ).
Make sure your client (e.g., Postman, frontend app) sends JSON data in the body, with
Content-Type: application/json .
ts
25/33
Before creating a new discount, it checks if a discount with the same code already exists
in the database.
If it does exist, it responds with a 400 (Bad Request) error and a short message.
ts
[Link](201).json(discount);
26/33
Status Code: 201 (Created), which indicates a new resource was created on the
server.
JSON Body: the newly created discount document (including _id and any other
fields stored in MongoDB).
6. Error Handling
ts
} catch (error) {
next(error);
}
If any unforeseen errors occur (e.g., database connectivity issues), the function catches
them and passes them to your Express error handler via next(error) .
ts
[Link]('/', createDiscount);
2. Client Request:
Method: POST
URL: /api/v1/discounts
Body (JSON):
json
27/33
{
"code": "SUMMER2025",
"description": "Summer Sale",
"discountPercentage": 10,
"discountAmount": 0,
"startDate": "2025-06-01T[Link].000Z",
"endDate": "2025-07-01T[Link].000Z",
"maxUses": 100
}
3. Server Action: The createDiscount controller runs, checks for duplicates, creates a new
record if none exists, and returns 201 with the newly created discount document.
That’s it! You now have a fully functional createDiscount endpoint that stores new discount
codes in MongoDB and returns them with a 201 status.
It looks like your code should accept a POST request to /api/v1/discounts , but Railway is
still responding with a 404 (“not found”). That typically means one of two things:
1. The deployed code on Railway isn’t the same as what you have locally.
Maybe you haven’t pushed your latest changes (the ones that add
[Link]('/api/v1/discounts', discountRouter) ) to the repository that Railway
deploys from.
Or there’s a build step issue where your [Link] changes aren’t actually
compiled/deployed.
28/33
Double-check that your [Link] really has:
ts
Make sure it’s '/api/v1/discounts' exactly (all lowercase) both in code and in
Postman.
If you’re using TypeScript, confirm that you’re importing and exporting everything
correctly, and that your build output is up to date.
Troubleshooting Steps
1. Confirm the route usage in [Link] :
See if there’s an error message about failing to load the discountRouter , or if the
server is restarting/crashing.
You should see a line like Server running on port ... and no major errors at
startup.
If you’re using TypeScript, ensure your [Link] is correct and that the
compiled .js files include your updated routes.
ts
29/33
[Link]('/test', (req, res) => {
[Link]({ message: 'Discounts test route works!' });
});
If that also gives 404, it confirms the route never got registered in production.
5. Case Sensitivity:
Commit and push your latest code to GitHub (or wherever Railway pulls from).
Once your deployed code matches your local code, the POST /api/v1/discounts route
should work as intended.
If the route “never got registered in production,” it typically means your deployed code on
Railway doesn’t actually contain the changes that add [Link]('/api/v1/discounts',
discountRouter) . Here’s how you can fix that:
30/33
1. Confirm Your Code Is Pushed to the Correct Branch
Make sure you’ve committed and pushed all local changes to the branch that Railway is
deploying from (often main or master ).
If you’re using GitHub, check the repo on GitHub to ensure the latest commit has the
discountRouter code and the [Link]('/api/v1/discounts', discountRouter); line
in [Link] (or wherever you define routes).
Alternatively, push another commit (e.g., update your README) to trigger a new build.
Look for any error messages indicating that your discountRouter file can’t be
imported or that [Link] fails to compile.
After deployment, watch the “Logs” or “Runtime Logs” to see if your server starts without
errors. You should see something like Server running on port ... .
If there’s a crash, you’ll see an error. Fix that error, commit, and redeploy.
31/33
curl [Link]
If you get a 404, it still means your code with the route is not present or not registered. If
you get a JSON response (like {"message":"Discounts test route works!"} ), the route
is recognized.
Make sure the route path is exactly '/api/v1/discounts' (all lowercase) both in code
and in your Postman request. If you wrote '/api/v1/Discounts' anywhere, that could
cause a mismatch.
json
"scripts": {
"start": "node dist/[Link]"
}
or if using TypeScript:
json
"scripts": {
"start": "tsc && node dist/[Link]"
}
32/33
Railway must run the compiled code (if using TypeScript). If you haven’t compiled it or if
your build output is missing the new routes, the old code will run.
Summary
1. Push your latest changes to GitHub (or whichever repo Railway is watching).
3. Watch logs to confirm the new code is actually built and started.
4. Test the endpoint again in Postman with the correct URL ( POST /api/v1/discounts ).
Once your deployed code matches your local code, your discountRouter route should
register, and you won’t see 404 errors anymore.
From the code snippet alone, there’s no authentication or role check to restrict who can
create a discount. In other words, anyone who can successfully send a POST request to
/api/v1/discounts with the required data can create a discount coupon.
If you want to limit discount creation to certain users (e.g., only admins), you need to:
2. Check the user’s role (e.g., isAdmin ) before allowing the createDiscount controller to
run.
3. Return an appropriate response ( 403 Forbidden or 401 Unauthorized ) if the user isn’t
allowed to create discounts.
Right now, your createDiscount function does not perform any such checks, so by default
it’s open to all.
33/33