2

I will show you an example of what i'm trying to do :

server.ts

export class ExpressServer {
  public readonly app: express.Application;
  public readonly lbApp: ImportedApp;
  private server?: Server;

  constructor(options: ApplicationConfig = {}) {
    this.app = express();
    this.lbApp = new ImportedApp(options);
    this.app.get('/hello', async function (_req: Request, res: Response) {
      //Here i'd like to call a model like User.findById() but can't figure out how to do it..
    });

  }
}

As you see in the comment i'm trying to access my models method to use them in my route (Like showing users informations on my view) But can't figure out how to do it. I'v already tryed to import the DataSource, the model, the controller but nothing's containing my methods (FindById, Create etc..)

If i find nothing i will have to use something like Axios or Request to request the ressource from the api instead of inside my code like await request('api/users/myusername)

Lin Du
  • 88,126
  • 95
  • 281
  • 483
Jeremy M.
  • 1,154
  • 1
  • 9
  • 29

2 Answers2

2

In LoopBack 4, we use Repository design patter for accessing data. In order to find a user instance by its id, you need to obtain an instance of UserRepository via dependency injection. Quoting from https://loopback.io/doc/en/lb4/Repository.html:

Repositories are adding behavior to Models. Models describe the shape of data, Repositories provide behavior like CRUD operations. This is different from LoopBack 3.x where models implement behavior too.

UPDATED SOLUTION

To obtain an instance of a Repository class, you can use the Service Locator design pattern and get the instance from the per-request Context object provided by LoopBack's REST layer.

import {MIDDLEWARE_CONTEXT, RequestContext} from '@loopback/rest';
import {UserRepository} from '../repositories';

function expressHandler(req, res, next) {
  const ctx = (req as any)[MIDDLEWARE_CONTEXT];  
  const userRepo = await ctx.get<UserRepository>('repositories.UserRepository');
  const users = await userRepo.find({limit: 10});
  // render your view
}

We are discussing how to make this use case easier to implement in GitHub pull request loopback-next#6793, feel free to join the discussion there.

ORIGINAL ANSWER

Instead of writing an Express route for your rendered pages, I recommend you to write a LoopBack 4 Controller instead; and inject Express Response object to allow you to render the HTML view, as explained in https://loopback.io/doc/en/lb4/Accessing-http-request-response.html#inject-http-response

import {Response, RestBindings, oas} from '@loopback/rest';
import {inject} from '@loopback/core';
import {UserRepository} from '../repositories';

export class PingController {
  constructor(
    @inject(RestBindings.Http.RESPONSE) 
    private response: Response

    @repository(UserRepository)
    public userRepository: UserRepository,
  ) {}

  // Hide this endpoint from OpenAPI spec generated for the app
  @oas.visibility('undocumented')
  @get('/users')
  list(): Response {
    // Access User data via this.userRepository API
    const users = await this.userRepository.find({limit: 10});

    // Access the response object via `this.response`
    this.response.render('users', {users});

    // Return the HTTP response object so that LoopBack framework skips the
    // generation of HTTP response
    return this.response;
  }
}

Having said that, if you already know how to access DataSource instances from your LB4 app in your Express routes, then you can instantiate Repository classes manually from your routes too:

const db = // your datasource

this.app.get('/hello', async function (_req: Request, res: Response) {
  const repo = new UserRepository(db);
  const users = await this.userRepository.find({limit: 10});
});
Miroslav Bajtoš
  • 10,667
  • 1
  • 41
  • 99
1

To me the solution is not working. Started from the express-composition example, i just need to access lb repositories from a generic express route outside of the lb4 request handler:

constructor(options: ApplicationConfig = {}) {
  this.app = express();
  this.lbApp = new NoteApplication(options);
  this.lbApp.basePath('')

  // Expose the front-end assets via Express, not as LB4 route
  this.app.use('/api', this.lbApp.requestHandler);

  this.app.get('/hello', async (req: Request, res: Response) => {
    const ctx = (req as any)[MIDDLEWARE_CONTEXT];
    const userRepo = await ctx.get('repositories.UserRepository');
    res.send('Hello world!');
  });
}

the ctx in the line

const ctx = (req as any)[MIDDLEWARE_CONTEXT];

is always undefined.

My main goal is to have routes not under /api that can still access lb4 repositories.

Ivanva
  • 11
  • 1
  • 6 months after update: my previous request was probably based on poor lb4 knowledge. I have discarded that idea and i have set the rest api path to '' (empty string), and mounted a generic express router to a lb4 app. I just have to remember to set the '/api' on controller routes. – Ivanva Jan 24 '22 at 22:42