0Pricing
Vue Academy · Lesson

Fastify Backend for Vue SPAs

Fastify routes, CORS, serving static Vue build, rate limiting, request validation.

Fastify Backend for Vue SPAs is a free Vue Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Vue Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Fastify for a Vue SPA

Fastify is a fast, low-overhead Node.js web framework with a strong plugin ecosystem and built-in schema validation. It makes an excellent API backend for a Vue single-page application, and can also serve the built static files.

Creating the Server with a Logger

Instantiate Fastify with logger: true to get structured request logging via Pino out of the box. This is invaluable for debugging API calls from the Vue app.

import Fastify from 'fastify'

const app = Fastify({
  logger: true
})

await app.listen({ port: 3000, host: '0.0.0.0' })

CORS for the Vue Dev Server

During development the Vue app runs on a different origin (e.g. localhost:5173). Register @fastify/cors and allow that origin so the browser permits cross-origin API calls.

import cors from '@fastify/cors'

await app.register(cors, {
  origin: 'http://localhost:5173',
  credentials: true // allow cookies
})

Serving the Built Vue dist

In production you serve the compiled Vue app. Register @fastify/static pointing at the dist directory so Fastify delivers index.html and assets.

import fastifyStatic from '@fastify/static'
import { join } from 'node:path'

await app.register(fastifyStatic, {
  root: join(process.cwd(), 'dist'),
  prefix: '/'
})

SPA Fallback Route

A Vue SPA uses client-side routing, so unknown paths must return index.html (not 404). Add a wildcard fallback that serves the app shell so deep links work on refresh.

app.setNotFoundHandler((req, reply) => {
  // let the Vue router handle non-API paths
  if (req.url.startsWith('/api')) {
    return reply.code(404).send({ error: 'Not found' })
  }
  return reply.sendFile('index.html')
})

Rate Limiting

Protect the API from abuse with @fastify/rate-limit. Configure a maximum number of requests per time window per client.

import rateLimit from '@fastify/rate-limit'

await app.register(rateLimit, {
  max: 100,            // requests
  timeWindow: '1 minute'
})

Schema Validation on Routes

Fastify validates and serializes using JSON Schema. Define a body schema and Fastify rejects invalid payloads with a 400 before your handler runs — no manual validation needed.

app.post('/api/users', {
  schema: {
    body: {
      type: 'object',
      required: ['email', 'name'],
      properties: {
        email: { type: 'string', format: 'email' },
        name: { type: 'string', minLength: 1 }
      }
    }
  }
}, async (req) => {
  return createUser(req.body)
})

Response Schemas for Speed

Adding a response schema lets Fastify serialize output with a fast compiled serializer and strips fields not in the schema — improving both performance and security.

app.get('/api/users/:id', {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          id: { type: 'number' },
          name: { type: 'string' }
          // password is omitted, never serialized
        }
      }
    }
  }
}, getUserHandler)

Organizing Routes as Plugins

Fastify encourages encapsulation: group related routes into plugins registered under a prefix. This keeps the API modular as it grows.

async function userRoutes(app) {
  app.get('/', listUsers)
  app.post('/', createUser)
}

await app.register(userRoutes, { prefix: '/api/users' })

Plugin Registration Order

Order matters: register cross-cutting plugins (cors, rate-limit) before routes, and the static/SPA fallback last, so API routes are matched before the catch-all serves index.html.

await app.register(cors, corsOpts)
await app.register(rateLimit, rlOpts)
await app.register(userRoutes, { prefix: '/api/users' })
await app.register(fastifyStatic, staticOpts) // last

Dev vs Prod Strategy

In dev, run Vite's dev server and Fastify separately, relying on CORS. In prod, build the Vue app and let Fastify serve dist from the same origin — no CORS needed and one deployable process.

Quick Check

Test your understanding of the Fastify backend.

Recap

You learned building a Fastify backend for a Vue SPA:

  • logger: true gives structured request logging
  • @fastify/cors allows the Vue dev server origin
  • @fastify/static serves the built dist; a wildcard fallback enables SPA routing
  • @fastify/rate-limit guards against abuse
  • JSON Schema validates bodies and serializes responses safely and fast

Frequently asked questions

Is the “Fastify Backend for Vue SPAs” lesson free?

Yes — the full text of “Fastify Backend for Vue SPAs” is free to read here on the web, and the Vue Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Vue Academy course, upgrade to CoddyKit PRO.

What will I learn in “Fastify Backend for Vue SPAs”?

Fastify routes, CORS, serving static Vue build, rate limiting, request validation. You practise Vue Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Vue Academy?

No prior experience is required. Vue Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Fastify Backend for Vue SPAs” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Vue Academy lesson?

Yes. Every Vue Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Fastify Backend for Vue SPAs
  2. JWT Authentication: Login and Refresh
  3. Authenticated API Calls with Axios Interceptors
  4. Deploying Full-Stack Vue to Production
← Back to Vue Academy