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 model
to generate your modelOr
-
create a file under /src/models named
character.model.ts
and 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.json
and one calleddb.datasouce.ts
in the folder /src/datasources -
in
db.datasource.json
we want to add:
{
"name": "db",
"connector": "memory",
"localStorage": "",
"file": "./data/db.json"
}
- in
db.datasource.ts
we 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
data
with a file calleddb.json
in 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 controller
and 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 openapi
and 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
GET
endpoints/heroes/{id}
and/heroes
under 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}/species
endpoints. -
Restart the oasgraph server