### Install Vesper and SQLite Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/getting-started.md Install the Vesper framework and the sqlite3 driver using npm. These are essential for setting up a new project. ```bash npm i vesper --save ``` ```bash npm i sqlite3 --save ``` -------------------------------- ### Install Vesper Globally (JavaScript) Source: https://github.com/vesper-framework/vesper/blob/master/README.md Install Vesper globally using npm to use the init command for creating a new JavaScript project. ```bash npm i vesper -g vesper init --name my-project --javascript ``` -------------------------------- ### Bootstrap Function Source: https://context7.com/vesper-framework/vesper/llms.txt Initializes and starts a Vesper application with provided configuration options. ```APIDOC ## Bootstrap Function ### Description The `bootstrap` function initializes and starts a Vesper application with the provided configuration options including port, controllers, entities, schemas, and middleware. ### Method `bootstrap(options)` ### Parameters - **options** (object) - Required - Configuration options for the Vesper application. - **port** (number) - Required - The port number to run the server on. - **controllers** (array) - Required - An array of controller classes. - **entities** (array) - Required - An array of TypeORM entity classes. - **schemas** (array) - Required - An array of paths to GraphQL schema files. - **cors** (boolean | object) - Optional - Enable CORS. - **graphQLRoute** (string) - Optional - The route for the GraphQL endpoint (defaults to '/graphql'). - **playground** (string) - Optional - The route for the GraphQL Playground (defaults to '/playground'). - **setupContainer** (function) - Optional - A function to set up request-scoped services in the dependency injection container. - **authorizationChecker** (function) - Optional - A function to check user authorization. ### Request Example ```typescript import { bootstrap } from "vesper"; import { PostController } from "./controller/PostController"; import { Post } from "./entity/Post"; bootstrap({ port: 3000, controllers: [PostController], entities: [Post], schemas: [__dirname + "/schema/**/*.graphql"], cors: true, graphQLRoute: "/graphql", playground: "/playground", setupContainer: async (container, action) => { // Setup request-scoped services const user = await authenticateUser(action.request); container.set(CurrentUser, user); }, authorizationChecker: (roles, action) => { const user = action.context.container.get(CurrentUser); return roles.some(role => user.roles.includes(role)); } }).then(() => { console.log("Server running on http://localhost:3000"); console.log("Playground available at http://localhost:3000/playground"); }).catch(error => { console.error(error.stack); }); ``` ### Response This function returns a Promise that resolves when the server has successfully started. ``` -------------------------------- ### Install Vesper Globally (TypeScript) Source: https://github.com/vesper-framework/vesper/blob/master/README.md Install Vesper globally using npm to use the init command for creating a new TypeScript project. ```bash npm i vesper -g vesper init --name my-project --typescript ``` -------------------------------- ### Compile and Run Application Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/getting-started.md Command to compile TypeScript and start the Node.js server. ```bash tsc && node ./src/index.js ``` -------------------------------- ### GraphQL API Queries and Mutations Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/getting-started.md Example GraphQL operations to interact with the Post API. ```graphql query { posts { id title } } ``` ```graphql query { post(id: 1) { id title } } ``` ```graphql mutation { postSave(title: "First post", text: "about first post") { id title text } } ``` ```graphql mutation { postSave(id: 1, title: "First post", text: "about first post") { id title text } } ``` ```graphql mutation { postDelete(id: 1) } ``` -------------------------------- ### Implement inefficient PostController with category loading Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/resolvers.md An example of an inefficient approach that loads categories for every post individually. ```javascript import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; import {Category} from "../entity/Category"; export class PostController { constructor(container) { this.entityManager = container.get(EntityManager); } async posts() { const posts = await this.entityManager.find(Post); await Promise.all(posts.map(async post => { const categories = await this.entityManager .createQueryBuilder(Category, "category") .innerJoin("category.posts", "post", "post.id = :postId", { postId: post.id }) .getMany(); post.categoryNames = categories.map(category => category.name); })); return posts; } } ``` -------------------------------- ### Define GraphQL Schema Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/controllers-and-args.md Example schema definitions for Query and Mutation types. ```graphql type Query { posts: [Post] post(id: Int): Post } type Mutation { postSave(id: Int, title: String, text: String): Post postDelete(id: Int): Boolean } ``` -------------------------------- ### Registering Resolvers in Bootstrap Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/resolvers.md Example of how to register your custom resolvers within the Vesper bootstrap configuration. ```typescript bootstrap({ resolvers: [ PostResolver ], // ... }); ``` -------------------------------- ### GraphQL Schema Definition for Controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/project-structure.md Example of GraphQL schema definitions for controllers, specifically 'PhotoController' and 'AlbumController'. These schemas should be placed in the 'schema/controller' directory. ```graphql # schema/controller/PhotoController.graphql type PhotoController { # ... } # schema/controller/AlbumController.graphql type AlbumController { # ... } ``` -------------------------------- ### GraphQL Queries for Relations Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/advanced-tutorial.md Example GraphQL queries to fetch posts with categories or categories with posts. ```graphql query { posts { id title text categories { id name } } } ``` ```graphql query { categories { id name posts { id title text } } } ``` -------------------------------- ### GraphQL Schema Definition for Model Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/project-structure.md Example of a GraphQL schema definition for a 'Photo' model and an 'Album' model. These files should be located in the 'schema/model' directory. ```graphql # schema/model/Photo.graphql type Photo { # ... } # schema/model/Album.graphql type Album { # ... } ``` -------------------------------- ### JavaScript Controller Class Example Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/project-structure.md Illustrates a JavaScript class for a controller, designed for use within the 'controller' directory. A single controller should correspond to a single model. ```javascript // controller/PhotoController.js export class PhotoController { // ... } // controller/AlbumController.js export class AlbumController { // ... } ``` -------------------------------- ### JavaScript Model Class Example Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/project-structure.md Defines a JavaScript class for a model, intended to be placed in the 'model' or 'entity' directory. Each model should reside in its own file. ```javascript // model/Photo.js export class Photo { // ... } // model/Album.js export class Album { // ... } ``` -------------------------------- ### Define GraphQL Post model Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/resolvers.md The schema definition for the Post model used in the examples. ```graphql type Post { id: Int title: String text: String categoryNames: [String] } ``` -------------------------------- ### Inefficient Post Controller with Category Loading Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/resolvers.md An example of a controller that inefficiently loads category names for each post, leading to N+1 query problems. ```typescript import {Controller, Query} from "vesper"; import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; import {Category} from "../entity/Category"; @Controller() export class PostController { constructor(private entityManager: EntityManager) { } @Query() async posts() { const posts = await this.entityManager.find(Post); await Promise.all(posts.map(async post => { const categories = await this.entityManager .createQueryBuilder(Category, "category") .innerJoin("category.posts", "post", "post.id = :postId", { postId: post.id }) .getMany(); post.categoryNames = categories.map(category => category.name); })); return posts; } } ``` -------------------------------- ### Inject TextGenerator Service into PostController Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/advanced-tutorial.md Example of injecting the TextGenerator service into a PostController using Vesper's service container. ```javascript import {TextGenerator} from "../service/TextGenerator"; export class PostController { constructor(container) { this.textGenerator = container.get(TextGenerator); } // ... } ``` -------------------------------- ### Implement a repository service Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/services-and-repositories.md Create a repository class to centralize database queries and entity management. ```javascript import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; export class PostRepository { constructor(container) { this.entityManager = container.get(EntityManager); } find(args) { let findOptions = {}; if (args.limit) findOptions.take = args.limit; if (args.offset) findOptions.skip = args.offset; if (args.sortBy === "last") findOptions.order = { "id": "DESC" }; if (args.sortBy === "name") findOptions.order = { "name": "ASC" }; return this.entityManager.find(Post, findOptions); } findById(id) { return this.entityManager.findOne(Post, id); } save(args) { return this.entityManager.save({ id: args.id, title: args.title, text: args.text }); } async remove(id) { await this.entityManager.remove(Post, { id: id }); return true; } } ``` -------------------------------- ### Bootstrap Vesper Application Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/getting-started.md Initializes the Vesper framework with controllers, entities, and schema paths. ```javascript import {bootstrap} from "vesper"; import {PostController} from "./controller/PostController"; import {Post} from "./entity/Post"; bootstrap({ port: 3000, controllers: [ { controller: PostController, action: "posts", type: "query" }, { controller: PostController, action: "post", type: "query" }, { controller: PostController, action: "postSave", type: "mutation" }, { controller: PostController, action: "postDelete", type: "mutation" }, ], entities: [ Post ], schemas: [ __dirname + "/schema/**/*.graphql" ] }).then(() => { console.log("Your app is up and running on http://localhost:3000. " + "You can use playground in development mode on http://localhost:3000/playground"); }).catch(error => { console.error(error.stack ? error.stack : error); }); ``` -------------------------------- ### Bootstrap a Vesper Application Source: https://context7.com/vesper-framework/vesper/llms.txt Initializes the Vesper server with configuration for controllers, entities, schemas, and dependency injection containers. ```typescript import { bootstrap } from "vesper"; import { PostController } from "./controller/PostController"; import { Post } from "./entity/Post"; bootstrap({ port: 3000, controllers: [PostController], entities: [Post], schemas: [__dirname + "/schema/**/*.graphql"], cors: true, graphQLRoute: "/graphql", playground: "/playground", setupContainer: async (container, action) => { // Setup request-scoped services const user = await authenticateUser(action.request); container.set(CurrentUser, user); }, authorizationChecker: (roles, action) => { const user = action.context.container.get(CurrentUser); return roles.some(role => user.roles.includes(role)); } }).then(() => { console.log("Server running on http://localhost:3000"); console.log("Playground available at http://localhost:3000/playground"); }).catch(error => { console.error(error.stack); }); ``` -------------------------------- ### Small Project Directory Structure Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/project-structure.md Recommended layout for small applications, centralizing source code components within a single src directory. ```text ├── src // your app source code │ ├── args // controller and resolver arguments │ ├── controller // controllers for your root queries │ ├── entity // database models called entities │ ├── manager // "manager" services - place for complicated business model logic │ ├── model // model classes │ ├── repository // "repository" services - place for database queries │ ├── resolver // your model and entity resolvers │ ├── schema // graphql schema files stored in ".graphql" format │ │ ├── controller // schemas for root queries, mutations and subscriptions │ │ ├── input // schemas for input files │ │ └── model // schemas for models and entities │ ├── service // services - place for logic extracted out of other classes │ ├── validator // args and user input validation logic │ ├── util // utility functions │ └── index.ts // bootstrap file │ ├── test // unit, functional, e2e and other tests ├── config.json // application configuration used in source code ├── ormconfig.json // TypeORM connection configuration ├── package.json // project dependencies ├── tsconfig.json // TypeScript compiler configuration file └── tslint.json // TsLint file ``` -------------------------------- ### Implement basic PostController Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/resolvers.md Initial controller implementation that returns basic post data without category names. ```javascript import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; export class PostController { constructor(container) { this.entityManager = container.get(EntityManager); } posts() { return this.entityManager.find(Post); } } ``` -------------------------------- ### Bootstrap Vesper Application Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/getting-started.md Initializes the Vesper application with controllers, entities, and schema paths. ```typescript import {bootstrap} from "vesper"; import {PostController} from "./controller/PostController"; import {Post} from "./entity/Post"; bootstrap({ port: 3000, controllers: [ PostController ], entities: [ Post ], schemas: [ __dirname + "/schema/**/*.graphql" ] }).then(() => { console.log("Your app is up and running on http://localhost:3000. " + "You can use playground in development mode on http://localhost:3000/playground"); }).catch(error => { console.error(error.stack ? error.stack : error); }); ``` -------------------------------- ### Inject TextGenerator Service into Controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/advanced-tutorial.md Example of injecting the TextGenerator service into a Vesper Controller using constructor injection. ```typescript import {Controller} from "vesper"; import {TextGenerator} from "../service/TextGenerator"; @Controller() export class PostController { constructor(private textGenerator: TextGenerator) { } // ... } ``` -------------------------------- ### Implement PostController Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/controllers-and-args.md Basic controller implementation mapping methods to GraphQL operations. ```javascript export class PostController { posts() { // serves "posts: [Post]" requests // return posts } post({ id }) { // serves "post(id: Int): Post" requests // return post by id } postSave(args) { // serves "postSave(id: Int, title: String, text: String): Post" // save post and return it } postDelete({ id }) { // serves "postDelete(id: Int): Boolean" requests // delete post by id } } ``` -------------------------------- ### Create a simple service class Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/services-and-repositories.md Define a service class to encapsulate business logic, such as password encryption. ```javascript export class PasswordEncryptor { encrypt(password) { // ... do password encryption ... return password; } } ``` -------------------------------- ### GraphQL Query Requesting Computed Field Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/advanced-tutorial.md Example GraphQL query to fetch posts including the computed 'categoryNames' field. ```graphql query { posts { id title text categoryNames } } ``` -------------------------------- ### Implement a Repository Service Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/services-and-repositories.md Create a repository class to encapsulate database queries using TypeORM's EntityManager. ```typescript import {Service} from "typedi"; import {EntityManager, FindManyOptions} from "typeorm"; import {Post} from "../entity/Post"; import {PostsArgs} from "../args/PostsArgs"; import {PostSaveArgs} from "../args/PostSaveArgs"; @Service() export class PostRepository { constructor(private entityManager: EntityManager) { } find(args: PostsArgs) { let findOptions: FindManyOptions = {}; if (args.limit) findOptions.take = args.limit; if (args.offset) findOptions.skip = args.offset; if (args.sortBy === "last") findOptions.order = { "id": "DESC" }; if (args.sortBy === "name") findOptions.order = { "name": "ASC" }; return this.entityManager.find(Post, findOptions); } findById(id: number) { return this.entityManager.findOne(Post, id); } save(args: PostSaveArgs) { const post = new Post(); post.id = args.id; post.title = args.title; post.text = args.text; return this.entityManager.save(post); } remove(id: number) { return this.entityManager.remove(Post, { id: id }); } } ``` -------------------------------- ### Basic Post Controller in Vesper Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/resolvers.md A simple Vesper controller to fetch all posts using TypeORM's EntityManager. ```typescript import {Controller, Query} from "vesper"; import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; @Controller() export class PostController { constructor(private entityManager: EntityManager) { } @Query() posts() { return this.entityManager.find(Post); } } ``` -------------------------------- ### Create Basic Post Resolver in JavaScript Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/advanced-tutorial.md JavaScript resolver class for the Post model to fetch category names. Requires EntityManager from typeorm. ```javascript import {EntityManager} from "typeorm"; import {Category} from "../entity/Category"; export class PostResolver { constructor(container) { this.entityManager = container.get(EntityManager); } categoryNames(post) { return this.entityManager .createQueryBuilder(Category, "category") .innerJoin("category.posts", "post", "post.id = :postId", { postId: post.id }) .getMany() .then(categories => categories.map(category => category.name)); } } ``` -------------------------------- ### Large Project Directory Structure Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/project-structure.md Recommended layout for large applications, utilizing a modular approach to organize features into distinct directories. ```text ├── src // your app source code │ ├── module1 // module name (for example "user") │ │ ├── args // controller and resolver arguments │ │ ├── controller // controllers for your root queries │ │ ├── entity // database models called entities │ │ ├── manager // "manager" services - place for complicated business model logic │ │ ├── model // model classes │ │ ├── repository // "repository" services - place for database queries │ │ ├── resolver // your model and entity resolvers │ │ ├── schema // graphql schema files stored in ".graphql" format │ │ │ ├── controller // schemas for root queries, mutations and subscriptions │ │ │ ├── input // schemas for input files │ │ │ └── model // schemas for models and entities │ │ ├── service // services - place for logic extracted out of other classes │ │ ├── validator // args and user input validation logic │ │ ├── util // utility functions │ │ └── index.ts // module file │ │ │ ├── module2 // module name (for example "photo") │ │ ├── args // controller and resolver arguments │ │ ├── controller // controllers for your root queries │ │ ├── entity // database models called entities │ │ ├── manager // "manager" services - place for complicated business model logic │ │ ├── model // model classes │ │ ├── repository // "repository" services - place for database queries │ │ ├── resolver // your model and entity resolvers │ │ ├── schema // graphql schema files stored in ".graphql" format │ │ │ ├── controller // schemas for root queries, mutations and subscriptions │ │ │ ├── input // schemas for input files │ │ │ └── model // schemas for models and entities │ │ ├── service // services - place for logic extracted out of other classes │ │ ├── validator // args and user input validation logic │ │ ├── util // utility functions │ │ └── index.ts // module file │ │ │ ├── ... // other modules │ └── index.ts // bootstrap file │ ├── test // unit, functional, e2e and other tests ├── config.json // application configuration used in source code ├── ormconfig.json // TypeORM connection configuration ├── package.json // project dependencies ├── tsconfig.json // TypeScript compiler configuration file └── tslint.json // TsLint file ``` -------------------------------- ### Implement Entity Controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/models-and-entities.md Controller methods for loading, saving, and deleting entities using EntityManager. ```javascript import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; export class PostController { constructor(container) { this.entityManager = container.get(EntityManager); } posts() { return this.entityManager.find(Post); } post({ id }) { return this.entityManager.findOne(Post, id); } postSave(args) { const post = this.entityManager.create(Post, args); return this.entityManager.save(Post, post); } postDelete({ id }) { return this.entityManager .remove(Post, { id: id }) .then(() => true); } } ``` -------------------------------- ### Create a Vesper Controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/controllers-and-args.md Implementation of a controller class using @Controller, @Query, and @Mutation decorators. ```typescript import {Controller, Query, Mutation} from "vesper"; @Controller() export class PostController { @Query() posts() { // serves "posts: [Post]" requests // return posts } @Query() post({ id }) { // serves "post(id: Int): Post" requests // return post by id } @Mutation() postSave(args) { // serves "postSave(id: Int, title: String, text: String): Post" // save post and return it } @Mutation() postDelete({ id }) { // serves "postDelete(id: Int): Boolean" requests // delete post by id } } ``` -------------------------------- ### Controller Decorator Source: https://context7.com/vesper-framework/vesper/llms.txt Registers a class as a GraphQL controller for root queries, mutations, and subscriptions. ```APIDOC ## @Controller Decorator ### Description The `@Controller` decorator registers a class as a GraphQL controller that serves root queries, mutations, and subscriptions. Controllers use dependency injection for services like EntityManager. ### Usage ```typescript import { Controller, Query, Mutation } from "vesper"; import { EntityManager } from "typeorm"; import { Post } from "../entity/Post"; import { PostSaveArgs } from "../args/PostSaveArgs"; @Controller() export class PostController { constructor(private entityManager: EntityManager) {} @Query() posts() { return this.entityManager.find(Post); } @Query() post({ id }: { id: number }) { return this.entityManager.findOne(Post, id); } @Mutation() postSave(args: PostSaveArgs) { const post = this.entityManager.create(Post, args); return this.entityManager.save(Post, post); } @Mutation() async postDelete({ id }: { id: number }) { await this.entityManager.remove(Post, { id }); return true; } } ``` ``` -------------------------------- ### Configure TypeORM Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/getting-started.md Defines the database connection settings in ormconfig.json. ```json { "type": "sqlite", "database": "database.sqlite", "synchronize": true, "logging": false } ``` -------------------------------- ### Implement batch PostResolver Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/resolvers.md An optimized Resolver that processes an array of posts to reduce database queries. ```javascript import {EntityManager} from "typeorm"; import {Category} from "../entity/Category"; export class PostResolver { constructor(container) { this.entityManager = container.get(EntityManager); } categoryNames(posts) { const postIds = posts.map(post => post.id); return this.entityManager .createQueryBuilder(Category, "category") .innerJoinAndSelect("category.posts", "post", "post.id IN (:...postIds)", { postIds }) .getMany() .then(categories => { return posts.map(post => { return categories .filter(category => category.posts.some(categoryPost => categoryPost.id === post.id)) .map(category => category.name); }); }); } } ``` -------------------------------- ### Controller Class Definition Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/project-structure.md Defines a `PhotoController` class. Use a single controller for each model, placing it in the `controller` directory. ```javascript export class PhotoController { // ... } ``` -------------------------------- ### Vesper Project Directory Structure Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/project-structure.md Illustrates the recommended directory layout for a Vesper.js project, separating client, server, and shared code for better organization and code sharing. ```tree ├── client │ ├── src // your client-side app source code │ └── ... // other client-side files and directories │ ├── server │ ├── src // your server-side app source code │ │ ├── args // controller and resolver arguments │ │ ├── controller // controllers for your root queries │ │ ├── entity // database models called entities │ │ ├── manager // "manager" services - place for complicated business model logic │ │ ├── model // model classes │ │ ├── repository // "repository" services - place for database queries │ │ ├── resolver // your model and entity resolvers │ │ ├── schema // graphql schema files stored in ".graphql" format │ │ │ ├── controller // schemas for root queries, mutations and subscriptions │ │ │ ├── input // schemas for input files │ │ │ └── model // schemas for models and entities │ │ ├── service // services - place for logic extracted out of other classes │ │ ├── validator // args and user input validation logic │ │ ├── util // utility functions │ │ └── index.ts // bootstrap file │ │ │ ├── test // unit, functional, e2e and other tests │ ├── config.json // application configuration used in source code │ ├── ormconfig.json // TypeORM connection configuration │ ├── package.json // project dependencies │ ├── tsconfig.json // TypeScript compiler configuration file │ └── tslint.json // TsLint file │ └── shared // shared interfaces, classes and other code ├── args // args interfaces can be there ├── model // model interfaces can be there └── ... // other code you want to shared between client and server ``` -------------------------------- ### Define Current User Class Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/authorization.md Create a class to represent the currently authorized user. This class will hold user-specific information. ```javascript export class CurrentUser { constructor(id, name) { this.id = id; this.name = name; } } ``` -------------------------------- ### Bootstrap PubSub Instance in Vesper Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/subscribers.md Register a PubSub instance in the service container and provide a subscriptionAsyncIterator during Vesper bootstrap. This ensures a single instance is used across the application. ```javascript import {bootstrap} from "vesper"; import {PubSub} from "graphql-subscriptions"; const pubSub = new PubSub(); bootstrap({ // ... setupContainer: container => container.set(PubSub, pubSub), subscriptionAsyncIterator: triggers => pubSub.asyncIterator(triggers) }); ``` -------------------------------- ### Another Controller Class Definition Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/project-structure.md Defines an `AlbumController` class. Adhere to the convention of one controller per model, located in the `controller` directory. ```javascript export class AlbumController { // ... } ``` -------------------------------- ### Implement Controller Action with Arguments Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/advanced-tutorial.md Controller method using the defined arguments to query the database via TypeORM. ```typescript import {Controller, Query} from "vesper"; import {EntityManager, FindManyOptions} from "typeorm"; import {PostsArgs} from "../args/PostsArgs"; import {Post} from "../entity/Post"; @Controller() export class PostController { constructor(private entityManager: EntityManager) { } @Query() posts(args: PostsArgs) { let findOptions: FindManyOptions = {}; if (args.limit) findOptions.take = args.limit; if (args.offset) findOptions.skip = args.offset; if (args.sortBy === "last") findOptions.order = { "id": "DESC" }; if (args.sortBy === "name") findOptions.order = { "name": "ASC" }; return this.entityManager.find(Post, findOptions); } // ... } ``` -------------------------------- ### Resolver method signature Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/resolvers.md The standard signature for resolver methods, accepting args, context, and info. ```javascript categoryNames(posts, args, context, info) { // ... } ``` -------------------------------- ### Register Controllers in Bootstrap Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/controllers-and-args.md Registering controller actions within the bootstrap configuration. ```javascript bootstrap({ controllers: [ { controller: PostController, action: "posts", type: "query" }, { controller: PostController, action: "post", type: "query" }, { controller: PostController, action: "postSave", type: "mutation" }, { controller: PostController, action: "postDelete", type: "mutation" }, ], // ... }); ``` -------------------------------- ### Bootstrap Application Entities Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/advanced-tutorial.md Registering entities in the application bootstrap file. ```typescript bootstrap({ entities: [ Post, Category ] // ... }); ``` -------------------------------- ### Integrate Vesper with Express Middleware Source: https://context7.com/vesper-framework/vesper/llms.txt Configure Vesper by passing an existing Express app to bootstrap or by manually mounting the Vesper middleware. ```typescript import express from "express"; import { bootstrap, buildVesperSchema, vesper } from "vesper"; import * as bodyParser from "body-parser"; import helmet from "helmet"; import rateLimit from "express-rate-limit"; // Option 1: Pass existing Express app to bootstrap const app = express(); app.use(helmet()); app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 })); bootstrap({ port: 3000, expressApp: app, controllers: [PostController], entities: [Post], schemas: [__dirname + "/schema/**/*.graphql"] }); // Option 2: Build schema and use vesper middleware manually async function startServer() { const app = express(); // Custom middleware app.use(helmet()); app.use(bodyParser.json()); // Health check endpoint app.get("/health", (req, res) => res.json({ status: "ok" })); // Build Vesper schema const schema = await buildVesperSchema({ controllers: [PostController], entities: [Post], schemas: [__dirname + "/schema/**/*.graphql"] }); // Mount GraphQL endpoint app.use("/graphql", bodyParser.json(), vesper(schema)); app.listen(3000, () => { console.log("Custom Express server with Vesper running"); }); } startServer(); ``` -------------------------------- ### Create a Service in TypeScript Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/services-and-repositories.md Define a service class using the @Service decorator from TypeDI. ```typescript import {Service} from "typedi"; @Service() export class PasswordEncryptor { encrypt(password) { // ... do password encryption ... return password; } } ``` -------------------------------- ### Define a GraphQL Controller Source: https://context7.com/vesper-framework/vesper/llms.txt Registers a class as a GraphQL controller to handle root queries and mutations using dependency injection. ```typescript import { Controller, Query, Mutation } from "vesper"; import { EntityManager } from "typeorm"; import { Post } from "../entity/Post"; import { PostSaveArgs } from "../args/PostSaveArgs"; @Controller() export class PostController { constructor(private entityManager: EntityManager) {} @Query() posts() { return this.entityManager.find(Post); } @Query() post({ id }: { id: number }) { return this.entityManager.findOne(Post, id); } @Mutation() postSave(args: PostSaveArgs) { const post = this.entityManager.create(Post, args); return this.entityManager.save(Post, post); } @Mutation() async postDelete({ id }: { id: number }) { await this.entityManager.remove(Post, { id }); return true; } } ``` -------------------------------- ### Configure Service Container for Authorization Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/authorization.md Configure the Vesper service container in the bootstrap file to instantiate and inject the CurrentUser. This involves fetching user data based on request headers and creating a CurrentUser instance. ```javascript bootstrap({ setupContainer: async (container, action) => { // trivial implementation, used for demonstration purpose const request = action.request; // user request, you can get http headers from it const entityManager = getManager(); const user = entityManager.findOneOrFail(User, { token: request.headers.token }); const currentUser = new CurrentUser(user.id, user.firstName + " " + user.lastName); container.set(CurrentUser, currentUser); }, // ... }); ``` -------------------------------- ### Basic Post Resolver Implementation Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/resolvers.md A Vesper resolver to fetch category names for a single post. This method is called per post, which can be inefficient. ```typescript import {Resolver, ResolverInterface, Resolve} from "vesper"; import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; import {Category} from "../entity/Category"; @Resolver(Post) export class PostResolver implements ResolverInterface { constructor(private entityManager: EntityManager) { } @Resolve() categoryNames(post: Post) { return this.entityManager .createQueryBuilder(Category, "category") .innerJoin("category.posts", "post", "post.id = :postId", { postId: post.id }) .getMany() .then(categories => categories.map(category => category.name)); } } ``` -------------------------------- ### Register Controller in Bootstrap Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/controllers-and-args.md Registering controllers within the bootstrap configuration. ```typescript bootstrap({ controllers: [ PostController ], // ... }); ``` -------------------------------- ### Implement Subscription Controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/subscribers.md A basic controller for the 'messageSent' subscription. It filters messages based on the 'receiver' field matching the 'me' argument provided in the subscription query. ```typescript @Controller() export class MessageController { @Subscription() messageSent({ messageSent }, args: MessageSentArgs) { return messageSent.receiver === args.me; } } ``` -------------------------------- ### Inject Services into Controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/controllers-and-args.md Using the constructor to inject services from the container. ```javascript export class PostController { constructor(container) { this.entityManager = container.get(EntityManager); this.someService = container.get(SomeService); } } ``` -------------------------------- ### Implement Dependency Injection with TypeDI Source: https://context7.com/vesper-framework/vesper/llms.txt Use @Service() decorators to define injectable classes and inject them into controllers via constructor injection. ```typescript import { Service } from "typedi"; import { Controller, Mutation } from "vesper"; import { EntityManager } from "typeorm"; import { User } from "../entity/User"; // Service for password encryption @Service() export class PasswordEncryptor { async encrypt(password: string): Promise { // Use bcrypt or similar return hashedPassword; } async verify(password: string, hash: string): Promise { return isValid; } } // Repository service for database operations @Service() export class UserRepository { constructor(private entityManager: EntityManager) {} findByEmail(email: string) { return this.entityManager.findOne(User, { where: { email } }); } save(user: User) { return this.entityManager.save(user); } } // Controller using injected services @Controller() export class UserController { constructor( private userRepository: UserRepository, private passwordEncryptor: PasswordEncryptor ) {} @Mutation() async userRegister({ email, password, name }: { email: string; password: string; name: string; }) { const existingUser = await this.userRepository.findByEmail(email); if (existingUser) { throw new Error("Email already registered"); } const user = new User(); user.email = email; user.name = name; user.password = await this.passwordEncryptor.encrypt(password); return this.userRepository.save(user); } } ``` -------------------------------- ### Create a Basic Post Resolver in TypeScript Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/advanced-tutorial.md A Vesper resolver for the Post entity that computes the 'categoryNames' field by querying related categories. ```typescript import {Resolver, Resolve, ResolverInterface} from "vesper"; import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; import {Category} from "../entity/Category"; @Resolver(Post) export class PostResolver implements ResolverInterface { constructor(private entityManager: EntityManager) { } @Resolve() categoryNames(post: Post) { return this.entityManager .createQueryBuilder(Category, "category") .innerJoin("category.posts", "post", "post.id = :postId", { postId: post.id }) .getMany() .then(categories => categories.map(category => category.name)); } } ``` -------------------------------- ### GraphQL Schema for Another Controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/project-structure.md Defines the GraphQL schema for the `AlbumController`. Ensure controller schemas are placed in the `schema/controller` directory. ```graphql type AlbumController { # ... } ``` -------------------------------- ### Define a PhotoModule class Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/modules.md Create a module class implementing GraphModule to encapsulate photo-related components. ```typescript export class PhotoModule implements GraphModule { schemas = [ __dirname + "/schema/**/*.graphql" ]; controllers = [ PhotoController ]; entities = [ Photo ]; resolvers = [ PhotoResolver ]; } ``` -------------------------------- ### Controller with PubSub for Sending Messages Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/subscribers.md Controller that saves a message and then publishes it using the injected PubSub instance. This triggers the 'messageSent' subscription for relevant clients. ```typescript import {Controller, Mutation, Subscription} from "vesper"; import {EntityManager} from "typeorm"; import {PubSub} from "graphql-subscriptions"; @Controller() export class MessageController { constructor(private entityManager: EntityManager, private pubSub: PubSub) { } @Subscription() messageSent({ messageSent }, args: MessageSentArgs) { return messageSent.receiver === args.me; } @Mutation() async messageSave(args: MessageSaveArgs) { const message = new Message(); message.id = args.id; message.text = args.text; message.receiver = args.receiver; await this.entityManager.save(message); this.pubSub.publish("messageSent", { messageSent: message }); return message; } } ``` -------------------------------- ### GraphQL Schema for Controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/project-structure.md Defines the GraphQL schema for the `PhotoController`. Controller schemas belong in the `schema/controller` directory. ```graphql type PhotoController { # ... } ``` -------------------------------- ### Optimized Post Resolver with DataLoader Pattern Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/resolvers.md An optimized Vesper resolver that accepts an array of posts and fetches all category names in a single database query, mimicking the DataLoader pattern. ```typescript import {Resolver, Resolve, ResolverInterface} from "vesper"; import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; import {Category} from "../entity/Category"; @Resolver(Post) export class PostResolver implements ResolverInterface { constructor(private entityManager: EntityManager) { } @Resolve() categoryNames(posts: Post[]) { const postIds = posts.map(post => post.id); return this.entityManager .createQueryBuilder(Category, "category") .innerJoinAndSelect("category.posts", "post", "post.id IN (:...postIds)", { postIds }) .getMany() .then(categories => { return posts.map(post => { return categories .filter(category => category.posts.some(categoryPost => categoryPost.id === post.id)) .map(category => category.name); }); }); } } ``` -------------------------------- ### Query Decorator Source: https://context7.com/vesper-framework/vesper/llms.txt Marks a controller method as a GraphQL query handler with optional configuration. ```APIDOC ## @Query Decorator ### Description The `@Query` decorator marks a controller method as a GraphQL query handler. It accepts optional configuration for custom naming and transaction behavior. ### Usage ```typescript import { Controller, Query } from "vesper"; import { EntityManager } from "typeorm"; import { Post } from "../entity/Post"; import { PostsArgs } from "../args/PostsArgs"; @Controller() export class PostController { constructor(private entityManager: EntityManager) {} // Basic query - method name matches GraphQL query name @Query() posts(args: PostsArgs) { return this.entityManager.find(Post, { take: args.limit || 10, skip: args.offset || 0, order: { id: "DESC" } }); } // Query with custom name @Query({ name: "featuredPosts" }) getFeaturedPosts() { return this.entityManager.find(Post, { where: { featured: true } }); } // Query with transaction enabled @Query({ transaction: true }) postsWithStats() { return this.entityManager.find(Post); } } ``` ### Parameters - **name** (string) - Optional - Custom name for the GraphQL query. - **transaction** (boolean) - Optional - Enable database transaction for the query (defaults to false). ``` -------------------------------- ### Define PostController Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/getting-started.md Implements GraphQL query and mutation handlers using TypeORM's EntityManager. ```javascript import {EntityManager} from "typeorm"; import {Post} from "../entity/Post"; export class PostController { constructor(container) { this.entityManager = container.get(EntityManager); } // serves "posts: [Post]" requests posts() { return this.entityManager.find(Post); } // serves "post(id: Int): Post" requests post({ id }) { return this.entityManager.findOne(Post, id); } // serves "postSave(id: Int, title: String, text: String): Post" requests postSave(args) { const post = this.entityManager.create(Post, args); return this.entityManager.save(Post, post); } // serves "postDelete(id: Int): Boolean" requests postDelete({ id }) { return this.entityManager .remove(Post, { id: id }) .then(() => true); } } ``` -------------------------------- ### Another Model Class Definition Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/project-structure.md Defines an `Album` model class. Follow the convention of placing each model in its own file within the `model` or `entity` directory. ```javascript export class Album { // ... } ``` -------------------------------- ### Register Resolver in bootstrap Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/resolvers.md Configuration to register the PostResolver within the Vesper bootstrap process. ```javascript bootstrap({ resolvers: [ { resolver: PostResolver, model: Post, methods: ["categoryNames"] }, ], // ... }); ``` -------------------------------- ### Implement PostResolver for lazy loading Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/resolvers.md A Resolver class that resolves category names only when requested by the client. ```javascript import {EntityManager} from "typeorm"; import {Category} from "../entity/Category"; export class PostResolver { constructor(container) { this.entityManager = container.get(EntityManager); } categoryNames(post) { return this.entityManager .createQueryBuilder(Category, "category") .innerJoin("category.posts", "post", "post.id = :postId", { postId: post.id }) .getMany() .then(categories => categories.map(category => category.name)); } } ``` -------------------------------- ### Register Modules in Bootstrap Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/modules.md Registers the PhotoModule and PostModule (assuming PostModule is defined elsewhere) in the Vesper bootstrap configuration. This is done in the main application bootstrap file. ```javascript bootstrap({ port: 3000, modules: [ PhotoModule, PostModule, ] }); ``` -------------------------------- ### Query Posts with Categories Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/entity-relations.md GraphQL query to fetch posts including their associated categories. ```graphql query { posts { id title text categories { id name } } } ``` -------------------------------- ### Define a UserModule class Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/modules.md Create a module class implementing GraphModule to encapsulate user-related components. ```typescript export class UserModule implements GraphModule { schemas = [ __dirname + "/schema/**/*.graphql" ]; controllers = [ UserController ]; entities = [ User ]; resolvers = [ UserResolver ]; } ``` -------------------------------- ### Query Categories with Posts Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/entity-relations.md GraphQL query to fetch categories including their associated posts. ```graphql query { categories { id name posts { id title text } } } ``` -------------------------------- ### GraphQL Schema for Another Model Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/project-structure.md Defines the GraphQL schema for the `Album` type. Model schemas should be located in the `schema/model` directory. ```graphql type Album { # ... } ``` -------------------------------- ### GraphQL Schema for Model Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/project-structure.md Defines the GraphQL schema for the `Photo` type. Place model schemas in the `schema/model` directory. ```graphql type Photo { # ... } ``` -------------------------------- ### Inject and use a service in a controller Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/services-and-repositories.md Use the container to inject services into controllers or other components via the constructor. ```javascript import {EntityManager} from "typeorm"; import {User} from "../entity/User"; import {PasswordEncryptor} from "../service/PasswordEncryptor"; export class UserController { constructor(container) { this.entityManager = container.get(EntityManager); this.passwordEncryptor = container.get(PasswordEncryptor); } userSave(args) { const user = new User(); user.password = this.passwordEncryptor.encrypt(args.password); return this.entityManager.save(user); } } ``` -------------------------------- ### Define Post GraphQL Model Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/entity-relations.md GraphQL schema definition for the Post entity. ```graphql type Post { id: Int title: String text: String categories: [Category] } ``` -------------------------------- ### Define a GraphQL Model Source: https://github.com/vesper-framework/vesper/blob/master/docs/javascript/models-and-entities.md Standard GraphQL type definition for a model. ```graphql type Post { id: Int title: String text: String } ``` -------------------------------- ### Define CurrentUser Class Source: https://github.com/vesper-framework/vesper/blob/master/docs/typescript/authorization.md Create a class to represent the currently authorized user. This class will hold user-specific information. ```typescript export class CurrentUser { id: number; name: string; constructor(id, name) { this.id = id; this.name = name; } } ```