npm i -g @loopback/cli
lb4 app
? Project name: heroes
? Project description: heroes api
? Project root directory: /heroes
? Application class name: Heroes
? Select features to enable in the project:
❯◉ Enable tslint
◉ Enable prettier
◉ Enable mocha
◉ Enable loopbackBuild
◉ Enable vscode
◉ Enable repositories
◉ Enable services
-
run
lb4 modelto generate your modelOr
-
create a file under /src/models named
character.model.tsand add:
import {Entity, model, property} from '@loopback/repository';
@model()
export class Character extends Entity {
@property({
type: 'number',
id: true,
})
id: number;
@property({
type: 'string',
required: true,
})
name: string;
@property({
type: 'string',
required: true,
})
friend: string;
@property({
type: 'string',
required: true,
})
planetId: string;
@property({
type: 'string',
required: true,
})
species: string;
constructor(data?: Partial<Character>) {
super(data);
}
}
- add
export * from './character.model';to index.ts
-
create two files, one called
db.datasource.jsonand one calleddb.datasouce.tsin the folder /src/datasources -
in
db.datasource.jsonwe want to add:
{
"name": "db",
"connector": "memory",
"localStorage": "",
"file": "./data/db.json"
}
- in
db.datasource.tswe want to add:
import {inject} from '@loopback/core';
import {juggler} from '@loopback/repository';
import * as config from './db.datasource.json';
export class DbDataSource extends juggler.DataSource {
static dataSourceName = 'db';
constructor(
@inject('datasources.config.db', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
}
}
- We now want to create a folder called
datawith a file calleddb.jsonin the root directory of the app.
- create a file under /src/repositories named
character.repository.ts
import {inject} from '@loopback/core';
export class CharacterRepository extends DefaultCrudRepository<
Character,
typeof Character.prototype.id
> {
constructor(
@inject('datasources.db') dataSource: DbDataSource,
) {
super(Character, dataSource);
}
}
- add
export * from './character.repository;to index.ts
- create a file under /src/repositories named
character.controller.ts
import {
Filter,
repository,
Where,
} from '@loopback/repository';
import {
post,
param,
get,
getFilterSchemaFor,
getWhereSchemaFor,
patch,
del,
requestBody,
} from '@loopback/rest';
import {Character} from '../models';
import {CharacterRepository} from '../repositories';
export class HeroController {
constructor(
@repository(CharacterRepository)
public characterRepository : CharacterRepository,
) {}
@post('/heroes', {
responses: {
'200': {
description: 'Character model instance',
content: {'application/json': {schema: {'x-ts-type': Character}}},
},
},
})
async create(@requestBody() character: Character): Promise<Character> {
return await this.characterRepository.create(character);
}
@get('/heroes', {
responses: {
'200': {
description: 'Array of Character model instances',
content: {
'application/json': {
schema: {type: 'array', items: {'x-ts-type': Character}},
},
},
},
},
})
async find(
@param.query.object('filter', getFilterSchemaFor(Character)) filter?: Filter,
): Promise<Character[]> {
return await this.characterRepository.find(filter);
}
@patch('/heroes', {
responses: {
'200': {
description: 'Character PATCH success count',
content: {'application/json': {schema: CountSchema}},
},
},
})
async updateAll(
@requestBody() character: Character,
@param.query.object('where', getWhereSchemaFor(Character)) where?: Where,
): Promise<Count> {
return await this.characterRepository.updateAll(character, where);
}
@get('/heroes/{id}', {
responses: {
'200': {
description: 'Character model instance',
content: {'application/json': {schema: {'x-ts-type': Character}}},
},
},
})
async findById(@param.path.number('id') id: number): Promise<Character> {
return await this.characterRepository.findById(id);
}
@patch('/heroes/{id}', {
responses: {
'204': {
description: 'Character PATCH success',
},
},
})
async updateById(
@param.path.number('id') id: number,
@requestBody() character: Character,
): Promise<void> {
await this.characterRepository.updateById(id, character);
}
@del('/heroes/{id}', {
responses: {
'204': {
description: 'Character DELETE success',
},
},
})
async deleteById(@param.path.number('id') id: number): Promise<void> {
await this.characterRepository.deleteById(id);
}
- add
export * from './character.controller;to index.ts
npm run start
- first lets add
planet.model.ts
import {Entity, model, property} from '@loopback/repository';
@model()
export class Planet extends Entity {
@property({
type: 'number',
id: true,
})
id: number;
@property({
type: 'string',
required: true
})
name: string;
@property({
type: 'string',
required: true,
})
climate: string;
constructor(data?: Partial<Planet>) {
super(data);
}
}
- next lets add
species.model.ts
import {model, property, Entity, belongsTo} from '@loopback/repository';
import { Planet } from './planet.model';
@model()
export class Species extends Entity {
@property({
type: 'number',
id: true,
})
id: number;
@property({
name: 'name',
description: "The species name",
type: 'string',
})
name: string;
@belongsTo(() => Planet, {keyTo: 'id'})
planetId: number;
@property({
name: 'lifespan',
description: "The lifespan of the species",
type: 'number',
})
lifespan: number;
constructor(data?: Partial<Species>) {
super(data);
}
}
- and now lets update
character.model.ts
import {Entity, model, property, belongsTo} from '@loopback/repository';
import { Planet } from './planet.model';
import { Species } from './species.model';
@model()
export class Character extends Entity {
@property({
type: 'number',
id: true,
})
id: number;
@property({
type: 'string',
required: true,
})
name: string;
@belongsTo(() => Character, {keyTo: 'id'})
friendId: number;
@belongsTo(() => Planet, {keyTo: 'id'})
planetId: number;
@belongsTo(() => Species, {keyTo: 'id'})
speciesId: number;
constructor(data?: Partial<Character>) {
super(data);
}
}
- lets create the planet repository by running:
lb4 repository
? Please select the datasource DbDatasource
? Select the model(s) you want to generate a repository Planet
create src/repositories/planet.repository.ts
update src/repositories/index.ts
Repository Todo was created in src/repositories/
- now lets create the species repository the same way:
lb4 repository
? Please select the datasource DbDatasource
? Select the model(s) you want to generate a repository Species
create src/repositories/species.repository.ts
update src/repositories/index.ts
- and finally lets update the Character repository
import {DefaultCrudRepository, juggler, BelongsToAccessor, repository} from '@loopback/repository';
import {Character, Species, Planet} from '../models';
import {DbDataSource} from '../datasources';
import {inject, Getter} from '@loopback/core';
import { SpeciesRepository } from './species.repository';
import { PlanetRepository } from './planet.repository';
export class CharacterRepository extends DefaultCrudRepository<
Character,
typeof Character.prototype.id
> {
public readonly friend: BelongsToAccessor<
Character,
typeof Character.prototype.id
>;
public readonly species: BelongsToAccessor<
Species,
typeof Character.prototype.id
>;
public readonly planet: BelongsToAccessor<
Planet,
typeof Character.prototype.id
>;
constructor(
@inject('datasources.db') dataSource: DbDataSource,
@repository.getter('SpeciesRepository')
speciesRepositoryGetter: Getter<SpeciesRepository>,
@repository.getter('PlanetRepository')
planetRepositoryGetter: Getter<PlanetRepository>,
) {
super(Character, dataSource);
this.friend = this._createBelongsToAccessorFor(
'friend',
Getter.fromValue(this),
);
this.species= this._createBelongsToAccessorFor(
'species',
speciesRepositoryGetter,
);
this.planet = this._createBelongsToAccessorFor(
'planet',
planetRepositoryGetter,
);
}
}
-
We don't need to add the controllers for species and planet but we could by running
lb4 controllerand selecting the options. -
We do want to add some new functionalities to the
character.controller.ts. Add these new methods below the existing:
@get('/heroes/{id}/friend', {
responses: {
'200': {
description: 'Character model instance',
content: {'application/json': {schema: {'x-ts-type': Character}}},
},
},
})
async getFriend(
@param.path.number('id') heroId: typeof Character.prototype.id,
): Promise<Character> {
return await this.characterRepository.friend(heroId);
}
@get('/heroes/{id}/planet', {
responses: {
'200': {
description: 'Character model instance',
content: {'application/json': {schema: {'x-ts-type': Planet}}},
},
},
})
async getPlanet(
@param.path.number('id') heroId: typeof Character.prototype.id,
): Promise<Planet> {
return await this.characterRepository.planet(heroId);
}
@get('/heroes/{id}/species', {
responses: {
'200': {
description: 'Character model instance',
content: {'application/json': {schema: {'x-ts-type': Species}}},
},
},
})
async getSpecies(
@param.path.number('id') heroId: typeof Character.prototype.id,
): Promise<Species> {
return await this.characterRepository.species(heroId);
}
{
"ids": {
"Planet": 4,
"Species": 4,
"Character": 5
},
"models": {
"Planet": {
"1": "{\"id\":1,\"name\":\"Earth\",\"climate\":\"Earthy\"}",
"2": "{\"id\":2,\"name\":\"Krypton\",\"climate\":\"Kryptony\"}",
"3": "{\"id\":3,\"name\":\"Themyscira\",\"climate\":\"Themysciray\"}"
},
"Species": {
"1": "{\"id\":1,\"name\":\"Human\",\"planetId\":1,\"lifespan\":80}",
"2": "{\"id\":2,\"name\":\"Kryptonian\",\"planetId\":2,\"lifespan\":200}",
"3": "{\"id\":3,\"name\":\"Demi-Goddess\",\"planetId\":3,\"lifespan\":1000}"
},
"Character": {
"1": "{\"id\":1,\"name\":\"Batman\",\"friendId\":2,\"planetId\":1,\"speciesId\":1}",
"2": "{\"id\":2,\"name\":\"Robin\",\"friendId\":1,\"planetId\":1,\"speciesId\":1}",
"3": "{\"id\":3,\"name\":\"Superman\",\"friendId\":4,\"planetId\":2,\"speciesId\":2}",
"4": "{\"id\":4,\"name\":\"Wonder Woman\",\"friendId\":3,\"planetId\":3,\"speciesId\":3}"
}
}
npm run start
-
Get the swagger from the end of part 2 at
http://127.0.0.1:3000/openapi.json -
Save this in a file called swagger.json
-
Generate a new base app using
lb4 app -
now we want to run:
lb4 openapiand direct to the swagger we just created. This will create your models for you automatically and create stub controllers based on the swagger the rest is up to you.
npm i oasgraph
-
Now if we run:
oasgraph {pathToSwagger}we will start the graphql server -
We also want to make sure that the api from part 2 is running
npm run start
- Add links on the
GETendpoints/heroes/{id}and/heroesunder the response:
"links": {
"friend": {
"operationId": "getCurrentFriend",
"parameters": {
"id": "$response.body#/id"
}
},
"planet": {
"operationId": "getCurrentPlanet",
"parameters": {
"id": "$response.body#/id"
}
},
"species": {
"operationId": "getCurrentSpecies",
"parameters": {
"id": "$response.body#/id"
}
}
}
-
Update the operationId for the
heroes/{id}/character,heroes/{id}/planet, andheroes/{id}/speciesendpoints. -
Restart the oasgraph server