Skip to content

03 API Structure

JP Barbosa edited this page Apr 15, 2023 · 1 revision

API Structure

Environment Variables

code .env.example
PORT=3333

VITE_API_URL=http://localhost:3333

NEO4J_URL=bolt://localhost:7687
NEO4J_DATABASE=neo4j
NEO4J_USERNAME=
NEO4J_PASSWORD=
echo ".env" >> .gitignore
cp .env.example .env

AppResponse

code ./packages/shared/src/types/ApiError.ts
import { AxiosError } from 'axios';

export type ApiError = {
  error: string;
};

export type AxiosCustomError = AxiosError<ApiError>;
code ./packages/shared/src/types/AppResponse.ts
import { Response } from 'express';
import { ApiError } from './ApiError';

type ResponseBody<T> = ApiError | T;

export type AppResponse<T> = Response<ResponseBody<T>>;
code ./packages/shared/src/types/index.ts
...
export * from './ApiError';
export * from './AppResponse';

Config

code ./packages/api/src/global.d.ts
declare global {
  namespace Express {
    interface Request {
      neo4jSession?: import('neo4j-driver').Session;
    }
  }
}

export {};
code ./packages/api/src/config/index.ts
import dotenv from 'dotenv';

dotenv.config();

const { NEO4J_URL, NEO4J_USERNAME, NEO4J_PASSWORD, NEO4J_DATABASE } =
  process.env;

export const config = {
  neo4j: {
    url: NEO4J_URL,
    username: NEO4J_USERNAME,
    password: NEO4J_PASSWORD,
    database: NEO4J_DATABASE,
  },
};

Error Handler Middleware

code ./packages/api/src/middleware/errorHandler.ts
import { NextFunction, Request } from 'express';
import { Neo4jError } from 'neo4j-driver';
import { AppResponse } from '@neo4j-crud/shared';

export const errorHandler = (
  err: Neo4jError,
  req: Request,
  res: AppResponse<any>,
  next: NextFunction
) => {
  console.error(err);
  res.status(500).send({ error: err.code });
};

Neo4j Session Middleware

code ./packages/api/src/middleware/neo4jSession.ts
import { Request, Response, NextFunction } from 'express';
import neo4j from 'neo4j-driver';
import { config } from '../config';

export const neo4jSession = (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  const { url, username, password, database } = config.neo4j;
  const auth = neo4j.auth.basic(username, password);
  const driver = neo4j.driver(url, auth, {
    disableLosslessIntegers: true,
  });
  const session = driver.session({
    database,
  });
  req.neo4jSession = session;
  res.on('finish', () => {
    session.close();
  });
  next();
};

Middleware Index

code ./packages/api/src/middleware/index.ts
export * from './errorHandler';
export * from './neo4jSession';

Movies Controller

code ./packages/api/src/controllers/movies.ts
import { NextFunction } from 'express';
import { AppResponse, Movie } from '@neo4j-crud/shared';
import * as graph from '@neo4j-crud/graph';

export const moviesController = {
  getAll: async (req, res: AppResponse<Movie[]>, next: NextFunction) => {
    try {
      const movies = await graph.movies(req.neo4jSession).getAll();
      res.send(movies);
    } catch (err) {
      next(err);
    }
  },

  getById: async (req, res: AppResponse<string>, next: NextFunction) => {
    res.send('To be implemented');
  },

  create: async (req, res: AppResponse<string>, next: NextFunction) => {
    res.send('To be implemented');
  },

  update: async (req, res: AppResponse<string>, next: NextFunction) => {
    res.send('To be implemented');
  },

  remove: async (req, res: AppResponse<string>, next: NextFunction) => {
    res.send('To be implemented');
  },
};
code ./packages/api/src/controllers/index.ts
export * from './movies';

Movies Routes

code ./packages/api/src/routes/movies.ts
import { Router } from 'express';
import { moviesController } from '../controllers';

const router = Router();

router.get('/', moviesController.getAll);
router.get('/:id', moviesController.getById);
router.post('/', moviesController.create);
router.put('/:id', moviesController.update);
router.delete('/:id', moviesController.remove);

export default router;
code ./packages/api/src/routes/index.ts
import { Router } from 'express';
import moviesRouter from './movies';

const routers = Router();

routers.use('/movies', moviesRouter);

export default routers;

Main

code ./packages/api/src/main.ts
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import routers from './routes';
import { errorHandler, neo4jSession } from './middleware';

const app = express();

app.use(bodyParser.json());
app.use(cors());
app.use(neo4jSession);
app.use(routers);
app.use(errorHandler);

const port = process.env.PORT || 3333;
const server = app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}`);
});
server.on('error', console.error);

Start NX

nx run-many --target=serve --all

Test

open http://localhost:3333/movies
[
  {
    "tagline": "In the heart of the nation's capital, in a courthouse of the U.S. government...",
    "id": 198,
    "title": "A Few Good Men",
    "released": 1992,
  },
  ...
]

Commit

git add .
git commit -m "API Structure"

Next step: Graph Structure