Vercel Logo

Server Routes

In Next.js, an API route is a file called route.ts that exports named functions: GET, POST, PUT, DELETE. The HTTP method is the function name. The file lives inside app/api/, right next to your pages.

Nuxt encodes the HTTP method in the filename: index.get.ts handles GET requests, while index.post.ts handles POST. Server code lives in server/api/, separate from the Vue app. Nitro compiles those routes into the server output used by platforms such as Vercel.

You'll expose the 17 hot springs in the JSON file through an API route.

Outcome

Create a server route that returns all hot springs from the JSON data file.

Fast Track

  1. Create server/api/springs/index.get.ts with a basic event handler
  2. Import and return the springs JSON data
  3. Test the endpoint at /api/springs in the browser

Hands-on exercise 2.1

Build the first server route for the app: a GET endpoint that returns all hot springs.

Requirements:

  1. Create server/api/springs/index.get.ts
  2. Import the springs data from server/data/springs.json
  3. Return the full array of springs
  4. Verify the endpoint returns JSON at /api/springs

Implementation hints:

  • defineEventHandler is the Nuxt equivalent of exporting a GET function in Next.js. It's auto-imported in the server/ directory
  • You can import JSON files directly with import springs from "~/server/data/springs.json"
  • The ~/ alias resolves to the project root in server code, just like in app code
  • Return a value and Nitro automatically serializes it as JSON with the correct content type

In Next.js, a basic API route looks like this:

// app/api/springs/route.ts: Next.js version
import { NextResponse } from "next/server";
import springs from "@/data/springs.json";
 
export async function GET() {
  return NextResponse.json(springs);
}

Here's the Nuxt version:

server/api/springs/index.get.ts
import type { Spring } from "~/types/spring";
import springs from "~/server/data/springs.json";
 
export default defineEventHandler((event) => {
  return springs as Spring[];
});

Return a value and Nitro serializes it, so this handler does not need a NextResponse.json() wrapper. The event parameter provides access to the request, query parameters, headers, and body when a route needs them.

The filename does real work here. index.get.ts tells Nitro three things: this handles requests to the root of the /api/springs path (index), it only responds to GET requests (.get), and it's a TypeScript file (.ts). If someone sends a POST to this endpoint, Nitro returns a 405 automatically.

Next.js can export several methods from one route.ts file. Nuxt uses one file per method, so the filename identifies the route's HTTP method.

Server auto-imports

The server/ directory has auto-imports separate from the app/ directory. Nitro utilities such as defineEventHandler, getQuery, getRouterParam, and createError are available without imports. Vue utilities such as ref and computed are unavailable in server routes.

Don't mix up the aliases

In server/ files, ~/ resolves to the project root. In app/ files, ~/ resolves to the app/ directory. This means ~/types/spring works in both places, but for different reasons. If you try to import a Vue component from a server route, it will fail.

Try It

Start the dev server and visit http://localhost:3000/api/springs in your browser. You should see a JSON array of 17 hot springs:

[
  {
    "id": "breitenbush-hot-springs",
    "name": "Breitenbush Hot Springs",
    "description": "Tucked into the Willamette National Forest...",
    "location": {
      "region": "Pacific Northwest",
      "country": "US",
      "lat": 44.7896,
      "lng": -121.9815
    },
    "temperature": { "min": 98, "max": 112 },
    "type": "developed",
    "features": ["clothing-optional", "forest-setting", ...],
    "elevation": 2200,
    "imageUrl": "/images/springs/breitenbush-hot-springs.jpg"
  },
  ...
]

You can also test with curl:

curl http://localhost:3000/api/springs | head -c 200

If you see the JSON data, the route is working. If you get a 404, make sure the file is at server/api/springs/index.get.ts, not app/api/springs/index.get.ts. The server directory is a common source of misplaced files when you're coming from Next.js.

Commit

git add -A && git commit -m "feat(api): add GET /api/springs server route"

Done-When

  • server/api/springs/index.get.ts exists and exports a default event handler
  • Visiting /api/springs returns a JSON array of 17 hot springs
  • You can explain how filename conventions (.get.ts, .post.ts) replace exported function names in Next.js

Solution

server/api/springs/index.get.ts
import type { Spring } from "~/types/spring";
import springs from "~/server/data/springs.json";
 
export default defineEventHandler((event) => {
  return springs as Spring[];
});

Was this helpful?

supported.