Lesson 57-Fastify Project Architecture

Fastify Project Architecture Design

Project Initialization

Create and initialize a new Fastify project:

mkdir my-fastify-app
cd my-fastify-app
npm init -y
npm install fastify

Directory Structure

A typical Fastify project directory structure:

my-fastify-app/
├── src/
│   ├── routes/
│   │   └── index.js
│   ├── controllers/
│   │   └── index.js
│   ├── services/
│   │   └── userService.js
│   ├── middlewares/
│   │   └── auth.js
│   ├── plugins/
│   │   └── logger.js
│   ├── models/
│   │   └── user.js
│   └── index.js
├── tests/
│   └── index.test.js
├── .env
└── package.json
  • src/routes/: Route definitions.
  • src/controllers/: Business logic controllers.
  • src/services/: Service layer for business logic.
  • src/middlewares/: Middleware functions.
  • src/plugins/: Fastify plugins.
  • src/models/: Data models.
  • tests/: Test files.
  • .env: Environment variable configuration.
  • package.json: Project dependencies.

Main Application Entry (src/index.js)

const fastify = require('fastify')({ logger: true });

// Load plugins
fastify.register(require('./plugins/logger'));

// Register routes
fastify.register(require('./routes/index'));

// Register middleware
fastify.register(require('./middlewares/auth'));

// Start server
const start = async () => {
    try {
        await fastify.listen({ port: process.env.PORT || 3000 });
    } catch (err) {
        fastify.log.error(err);
        process.exit(1);
    }
};
start();

Routes (src/routes/index.js)

module.exports = async function (fastify, opts) {
    // Import controller
    const controller = require('../controllers/index');

    // Define routes
    fastify.get('/', controller.home);
    fastify.post('/users', controller.createUser);
};

Controller (src/controllers/index.js)

const userService = require('../services/userService');

// Controller methods
exports.home = async (request, reply) => {
    return { hello: 'world' };
};

exports.createUser = async (request, reply) => {
    const user = await userService.createUser(request.body);
    return user;
};

Service Layer (src/services/userService.js)

const userModel = require('../models/user');

// Service layer methods
exports.createUser = async (userData) => {
    const user = new userModel(userData);
    return await user.save();
};

Data Model (src/models/user.js)

Assuming MongoDB:

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    name: String,
    email: String,
    password: String
});

module.exports = mongoose.model('User', userSchema);

Middleware (src/middlewares/auth.js)

module.exports = async function (fastify, opts) {
    fastify.addHook('onRequest', async (request, reply) => {
        // Middleware logic
        // ...
    });
};

Plugin (src/plugins/logger.js)

module.exports = async function (fastify, opts) {
    fastify.addHook('onRequest', async (request, reply) => {
        fastify.log.info(`Request received: ${request.url}`);
    });
};

Environment Variables (.env)

PORT=3000
MONGO_URI=mongodb://localhost:27017/mydb

Tests (tests/index.test.js)

Using Mocha and Chai:

const chai = require('chai');
const chaiHttp = require('chai-http');
const fastify = require('../src/index');
const should = chai.should();

chai.use(chaiHttp);

describe('Users', () => {
    it('should POST a user', async () => {
        const user = {
            name: 'John Doe',
            email: 'john.doe@example.com',
            password: 'password123'
        };

        const res = await chai.request(fastify.server)
            .post('/users')
            .send(user);

        res.should.have.status(200);
        res.body.should.be.an('object');
        res.body.should.have.property('name').eql(user.name);
    });
});
Share your love