How to set up Netlify Functions on Netlify

intermediate 20 min read Updated 2026-03-13
Quick Answer

Create a netlify/functions directory with JavaScript handler files, configure netlify.toml with your functions path, and deploy via Git or CLI. Functions automatically deploy with your site and are accessible at /.netlify/functions/<function-name>. Test locally using netlify dev before deploying to production.

Prerequisites

  • Netlify account created at app.netlify.com
  • Node.js (version 16+) and npm installed
  • Netlify CLI installed globally via npm
  • Git repository (GitHub, GitLab, or Bitbucket)
  • Basic familiarity with JavaScript and command line

Step-by-Step Instructions

1

Install Netlify CLI and Initialize Your Project

Install the Netlify CLI globally on your machine using npm install -g netlify-cli. Create a new project directory and navigate into it with mkdir my-netlify-functions && cd my-netlify-functions. Initialize npm in your project root with npm init -y to create a package.json file with default values. This sets up the foundation for your serverless functions project.
Verify your Netlify CLI installation with netlify --version to ensure it's properly installed.
2

Create the Functions Directory Structure

Create the default functions directory at the project root using mkdir netlify/functions. Netlify automatically detects this directory during builds without requiring additional configuration. If you prefer a custom directory name (e.g., functions instead of netlify/functions), you'll need to configure it in netlify.toml later. The directory structure should look like:
my-netlify-functions/
├── netlify/
│   └── functions/
├── package.json
└── netlify.toml
Use the default netlify/functions directory for zero-config deployment; custom paths require explicit configuration.
3

Write Your First Serverless Function

Create a new file in the functions directory named hello.mjs (use .mjs for ES modules, which is the required syntax). Add the following handler code:
export default async (request, context) => {
  return new Response(`Hello, ${context?.identity?.context?.user?.email ?? 'world'}!`, {
    headers: { 'Content-Type': 'text/plain' }
  });
};
The handler function receives two arguments: request (a web platform Request object containing HTTP details) and context (Netlify metadata including user identity). Your function must export a default handler and return a Response object.
Use .mjs for ES modules, .cjs for CommonJS, or .js if your package.json has type: module configured.
4

Configure netlify.toml for Custom Settings (Optional)

Create a netlify.toml file in your project root to customize your build and function settings. Add the following configuration:
[build]
  functions = "netlify/functions"
  command = "npm run build"
  publish = "dist"

[[redirects]]
  from = "/api/*"
  to = "/.netlify/functions/:splat"
  status = 200
The [build] section specifies your functions directory (change to functions if using a custom path), build command, and publish directory. The [[redirects]] section (optional) routes API requests to your functions. If using the default netlify/functions directory, this file is optional but recommended for clarity.
Set NODE_VERSION = "18" in netlify.toml if your build requires a specific Node.js version.
5

Set Up Local Development Environment

Run netlify dev (or ntl dev for short) in your project root to start the local development server. This command automatically detects your netlify.toml and package.json configuration. The server will start on http://localhost:8888 by default. Your functions will be accessible at http://localhost:8888/.netlify/functions/hello. Open this URL in your browser to test your function—it should return "Hello, world!" or your email if you're logged in.
Use netlify dev instead of your app's dev server (e.g., npm run dev on port 3000) to avoid CORS issues when testing functions locally.
6

Add Environment Variables for Local Development

Create a .env file in your project root to store sensitive configuration values like API keys. Add your variables in the format KEY=value, for example:
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_secret
These variables are automatically loaded into process.env when you run netlify dev. Access them in your functions using process.env.SPOTIFY_CLIENT_ID. Add .env to your .gitignore file to prevent committing secrets to version control.
For production, set environment variables in the Netlify dashboard under Site Settings > Build & Deploy > Environment instead of using .env files.
7

Connect Your Git Repository to Netlify

Log in to your Netlify dashboard at app.netlify.com and click "Add new site" > "Import an existing project". Select your Git provider (GitHub, GitLab, or Bitbucket) and authorize Netlify to access your repositories. Choose the repository containing your functions project. Netlify will automatically detect your netlify.toml configuration and build settings. Review the build command and publish directory, then click "Deploy site". Netlify will now automatically build and deploy your site whenever you push changes to your repository.
Netlify automatically detects netlify.toml settings; ensure your build command matches your project setup (e.g., npm run build for most frameworks).
8

Deploy Functions and Test in Production

After connecting your Git repository, Netlify automatically builds and deploys your functions with every push to your main branch. Monitor the deployment progress in the Netlify dashboard under "Deploys". Once deployment completes, your functions are live at https://your-site-name.netlify.app/.netlify/functions/hello. Test your production function by visiting this URL in your browser or using curl:
curl https://your-site-name.netlify.app/.netlify/functions/hello
Check the deploy logs in the dashboard if your function doesn't work as expected.
View detailed deploy logs by clicking on a deployment in the Netlify dashboard to troubleshoot build or bundling errors.
9

Create Additional Functions and API Routes

Add more functions by creating additional files in your netlify/functions directory. Each file becomes a separate endpoint. For example, create netlify/functions/users.mjs to handle user data at /.netlify/functions/users. You can handle different HTTP methods (GET, POST, PUT, DELETE) within a single function by checking request.method:
export default async (request, context) => {
  if (request.method === 'GET') {
    return new Response('Get users');
  } else if (request.method === 'POST') {
    return new Response('Create user');
  }
};
Use the optional netlify.toml redirects to create cleaner API routes (e.g., /api/users instead of /.netlify/functions/users).
Function names must use only alphanumeric characters, hyphens, and underscores; avoid spaces and special characters.
10

Access Data Files and External APIs

Netlify Functions can access files within their own folder or subdirectories. If you need to use a JSON data file, place it in your functions directory or a subdirectory. For example, store db.json in netlify/functions/data/db.json and import it in your function:
import db from './data/db.json' assert { type: 'json' };

export default async (request, context) => {
  return new Response(JSON.stringify(db), {
    headers: { 'Content-Type': 'application/json' }
  });
};
Functions cannot access files outside their folder (e.g., ../src/data.json will fail during deployment). For external APIs, use fetch within your function to call third-party services like Spotify or Firebase.
JSON files in the functions directory are included during deployment; files outside the functions folder are not accessible to functions.

Common Issues & Troubleshooting

CORS errors when fetching functions from your frontend

Ensure you're testing with netlify dev on port 8888, not your app's dev server (e.g., port 3000). CORS issues occur when the frontend and functions run on different ports. In production, both are served from the same domain, so CORS is not an issue. Use netlify dev to test the production-like environment locally.

Function returns 404 or 'not found' error

Verify the function file exists in netlify/functions/ with the correct name and extension (.mjs, .cjs, or .js). Check that the filename matches your URL endpoint (e.g., hello.mjs creates /.netlify/functions/hello). Ensure netlify.toml correctly specifies the functions directory. Run netlify dev and check the terminal output for any build errors. If using a custom directory, confirm it's set in netlify.toml under [build] functions = "your-directory".

Environment variables are undefined in production but work locally

Environment variables set in .env only work locally with netlify dev. For production, set variables in the Netlify dashboard under Site Settings > Build & Deploy > Environment variables. Add each variable as a key-value pair. Redeploy your site after adding variables so they're available during the build. Verify variable names match exactly (they're case-sensitive).

Deploy fails with 'Could not resolve' or bundling errors

Check the deploy logs in the Netlify dashboard for specific error messages. Common causes include: importing files outside the functions directory (move them inside), using top-level await in CommonJS files (use .mjs or add type: module to package.json), or missing dependencies (add them to package.json). Ensure your build command in netlify.toml is correct and matches your project setup.

Function works locally but not after deployment

Compare your local environment (netlify dev) with production by checking: (1) Node.js version—set NODE_VERSION in netlify.toml if needed, (2) environment variables—confirm they're set in the dashboard, (3) file paths—ensure relative paths work in the deployed functions directory, (4) dependencies—verify all imports are in package.json. Check the deploy logs for build errors and redeploy after fixes.