skip to Main Content

I want to make an API call from my backend server running express.js. I expect when I visit http://localhost:3004/api/weather?city=[cityName], I should see the json response from openweathermap.org.
Instead, I get the message "unknown endpoint" from my middleware. Thats the terminal output from the server:

Time: 2024-09-08T08:02:14.510Z
Method: GET
Path:   /api/weather
Query:  { city: 'zagreb' }
Headers: {
  host: 'localhost:3004',
  connection: 'keep-alive',
  pragma: 'no-cache',
  'cache-control': 'no-cache',
  'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"',
  'sec-ch-ua-mobile': '?0',
  'sec-ch-ua-platform': '"macOS"',
  'upgrade-insecure-requests': '1',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
  'sec-fetch-site': 'none',
  'sec-fetch-mode': 'navigate',
  'sec-fetch-user': '?1',
  'sec-fetch-dest': 'document',
  'accept-encoding': 'gzip, deflate, br, zstd',
  'accept-language': 'en,de-DE;q=0.9,de;q=0.8,fr-FR;q=0.7,fr;q=0.6,en-US;q=0.5,hr;q=0.4,nl;q=0.3',
  cookie: 'Webstorm-8aead92d=3b86ad4a-0728-412a-9b16-792fba3736a2'
}
Body:   {}
IP:     ::1

Here is my code for the Route:

require('dotenv').config()
const weatherRouter = require('express').Router()
const axios = require('axios')

const apiKey = process.env.API_KEY

weatherRouter.get('/api/weather', async (req, res, next) => {
    const { city } = req.query
    if (!city) {
        return res.status(400).json({ error: 'City must be provided' });
    }
    try {
        console.log(`Fetching weather data for ${city}`);
        const response = await axios.get(`http://api.openweathermap.org/geo/1.0/direct?q=${city}&limit=5&appid=${apiKey}`);
        res.json(response.data);
    } catch (error) {
        res.status(500).json({ error: 'Failed to fetch weather data' });
    }
    next()
})

module.exports = weatherRouter

The final goal is, that I make the request from a form from the frontend running react.js. This currently does not work either.
That is the code I wrote on the frontend to link it to with be backend:

import axios from 'axios'
const baseUrl = '/api/weather'

const getWeatherData = async city => {
    const response = await axios.get(`${baseUrl}?city=${city}`)
    return response.data
}

export default { getWeatherData }

Can anyone tell me what I am doing wrong?
Here is the code from app.js where I mount the routes:

const config = require('./utils/config')
const express = require('express')
require('express-async-errors')
const app = express()
const cors = require('cors')
const citiesRouter = require('./controllers/cities')
const weathersRouter = require('./controllers/weathers')
/* const usersRouter = require('./controllers/users')
const loginRouter = require('./controllers/login') */
const middleware = require('./utils/middleware')
const logger = require('./utils/logger')
const mongoose = require('mongoose')

mongoose.set('strictQuery', false)

logger.info('connecting to', config.MONGODB_URI)

mongoose.connect(config.MONGODB_URI)
  .then(() => {
    logger.info('connected to MongoDB')
  })
  .catch((error) => {
    logger.error('error connection to MongoDB:', error.message)
  })

app.use(cors())
app.use(express.static('dist'))
app.use(express.json())
app.use(middleware.requestLogger)
app.use(middleware.tokenExtractor)

app.use('/api/cities', citiesRouter)
app.use('/api/weather', weathersRouter)

app.use(middleware.unknownEndpoint)
app.use(middleware.errorHandler)

module.exports = app

Here is the code from vite.config.js:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  proxy: {
    "/api": {
      target: "http://localhost:3004",
      changeOrigin: true,
    },
  },
})

Browser console and network tab

Here is a screenshot when making a get request from the frontend:
enter image description here

Here is a screenshot after removing the proxy in vite.config.js and setting baseURL to http://localhost:3004/api/weather:
enter image description here

2

Answers


  1. Updated:

    weatherRouter.get('/')
    

    Yes, correct I missed that from your post.
    Fix your baseURL in case you use vite proxy and also better use env var for production.

    const axiosClient = axios.create({
      baseURL: import.meta.env.VITE_API_BASE_URL || '/api'
    });
    
    
    const getWeatherData = async (city) => {
      const response = await axiosClient.get('/api/weather', {
        params: {
          city,
        },
      });
      return response.data;
    };
    

    /api 2 times because /api = http://localhost:3004 from vite proxy and also your endpoint url starts with /api. Without proxy baseUrl should be http://localhost:3004 in case you’ll decide to remove it.

    And also remove next() in your request handler on BE. You already send response from request handler for all cases but because of next() call some of your middlewares that goes next after app.use('/api/weather', weathersRouter) try to send response again. That’s why you are getting Cannot set headers after they are sent to the client.

    Login or Signup to reply.
  2. In the router, change route from:

    weatherRouter.get('/api/weather', async (req, res, next) => {
    

    to

    weatherRouter.get('/', async (req, res, next) => {
    

    as you’re already using that route when mounting the router here:

    app.use('/api/weather', weathersRouter)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search