You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中使用环境变量隐藏API密钥及现有代码修改方案问询

Next.js 隐藏OpenWeather API密钥解决方案

步骤1:配置环境变量

在项目根目录创建.env.local文件,添加如下内容:

OPENWEATHER_API_KEY=你实际的OpenWeather API密钥
  • 不要给这个变量加NEXT_PUBLIC前缀,加了前缀会暴露到客户端代码,密钥就泄露了
  • 确保.env.local已经添加到.gitignore文件中,避免被提交到代码仓库

步骤2:创建服务端API路由

Next.js的API路由运行在服务端,不会泄露服务端环境变量,用来代理第三方接口请求刚好合适。
在pages/api目录下创建weather.js文件,代码如下:

export default async function handler(req, res) {
  const { q, units = 'metric' } = req.query;
  const API_KEY = process.env.OPENWEATHER_API_KEY;

  try {
    // 第一步查询城市坐标
    const firstRes = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${q}&units=${units}&appid=${API_KEY}`
    );
    if (!firstRes.ok) throw new Error('城市查询失败');
    const firstData = await firstRes.json();

    const { lon, lat } = firstData.coord;
    // 第二步查询天气详情
    const weatherRes = await fetch(
      `https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&units=${units}&exclude=&appid=${API_KEY}`
    );
    if (!weatherRes.ok) throw new Error('天气查询失败');
    const weatherData = await weatherRes.json();

    // 合并需要的数据返回给前端
    res.status(200).json({
      weather: weatherData,
      city: `${firstData.name}, ${firstData.sys.country}`,
      searchInput: firstData.name
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
}

步骤3:修改原有的Context代码

把原来客户端直接调用第三方接口的逻辑,改成调用你刚写的本地API路由,不需要再传密钥:

import { createContext, useState, useEffect } from 'react';

const WeatherContext = createContext({
  searchLocation: (input) => {},
  btnHandler: (input) => {},
  weather: '',
  isLoading: true,
});

export function WeatherContextProvider(props) {
  const [weather, setWeather] = useState({});
  const [city, setCity] = useState('');
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);
  const [unit, setUnit] = useState('metric');
  const [searchInput, setSearchInput] = useState('');
  

  const btnHandler = () => {
    setUnit(prev => prev === 'metric' ? 'imperial' : 'metric');
  };

  const searchLocation = async (input = searchInput, units = unit) => {
    const queryInput = loading ? 'London' : input;
    try {
      const response = await fetch(
        `/api/weather?q=${encodeURIComponent(queryInput)}&units=${units}`
      );
      const data = await response.json();
      if (!response.ok) {
        throw new Error(data.error || '请求失败');
      }
      setWeather(data.weather);
      setCity(data.city);
      setSearchInput(data.searchInput);
      setLoading(false);
      setError(false);
    } catch (error) {
      setError(error.message);
      console.log(error);
    }
  };

  useEffect(() => {
    searchLocation();
  }, [unit, searchInput]);

  const context = {
    searchLocation: searchLocation,
    city: city,
    weather: weather,
    isLoading: loading,
    error: error,
    btnHandler: btnHandler,
    unit: unit,
    searchInput: searchInput,
  };

  return (
    <WeatherContext.Provider value={context}>
      {props.children}
    </WeatherContext.Provider>
  );
}

export default WeatherContext;

补充说明

  • 如果你用的是Next.js 13+ App Router,只需要把API路由创建在app/api/weather/route.js路径下,导出对应的GET方法即可,其余逻辑完全一致
  • 可以根据需要给API路由加请求频率限制,避免接口被滥用

内容的提问来源于stack exchange,提问作者illyria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 00:39:04